Mercurial > ecos
changeset 3164:acfb9769162d
* cdl/enc424j600_eth_drivers.cdl:
* host/enc424j600_eth_drivers.cdl:
* include/enc424j600_eth.h:
* src/enc424j600_spi.h:
* src/enc424j600_spi.c:
* tests/netconn_test_server.c:
New package [Bugzilla ID #1000910].
| author | jlarmour |
|---|---|
| date | Fri, 04 May 2012 16:40:04 +0000 |
| parents | b67f16dc337c |
| children | 1057533611c4 |
| files | packages/devs/eth/microchip/enc424j600/current/ChangeLog packages/devs/eth/microchip/enc424j600/current/cdl/enc424j600_eth_drivers.cdl packages/devs/eth/microchip/enc424j600/current/host/test_tcp_client.c packages/devs/eth/microchip/enc424j600/current/include/enc424j600_eth.h packages/devs/eth/microchip/enc424j600/current/src/enc424j600_spi.c packages/devs/eth/microchip/enc424j600/current/src/enc424j600_spi.h packages/devs/eth/microchip/enc424j600/current/tests/netconn_test_server.c |
| diffstat | 7 files changed, 2640 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/ChangeLog @@ -0,0 +1,33 @@ +2012-04-20 Ilija Stanislevik <ilijas@siva.mk> + + * cdl/enc424j600_eth_drivers.cdl: + * host/enc424j600_eth_drivers.cdl: + * include/enc424j600_eth.h: + * src/enc424j600_spi.h: + * src/enc424j600_spi.c: + * tests/netconn_test_server.c: + New package [Bugzilla ID #1000910]. + +//=========================================================================== +// ####GPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2012 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, +// Fifth Floor, Boston, MA 02110-1301, USA. +// ------------------------------------------- +// ####GPLCOPYRIGHTEND#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/cdl/enc424j600_eth_drivers.cdl @@ -0,0 +1,271 @@ +# ==================================================================== +# +# enc424j600_eth_drivers.cdl +# +# Ethernet driver for Microchip ENC424J600 controller +# +# ==================================================================== +## ####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2009, 2010 Free Software Foundation, Inc. +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later +## version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT +## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License +## along with eCos; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +## +## As a special exception, if other files instantiate templates or use +## macros or inline functions from this file, or you compile this file +## and link it with other works to produce a work based on this file, +## this file does not by itself cause the resulting work to be covered by +## the GNU General Public License. However the source code for this file +## must still be made available in accordance with section (3) of the GNU +## General Public License v2. +## +## This exception does not invalidate any other reasons why a work based +## on this file might be covered by the GNU General Public License. +## ------------------------------------------- +## ####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Ilija Stanislevik +# Contributors: +# Date: 2010-11-23 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_DEVS_ETH_ENC424J600 { + display "Microchip ENC424J600 / ENC624J600 ethernet controller" + + parent CYGPKG_IO_ETH_DRIVERS + active_if CYGPKG_IO_ETH_DRIVERS + + implements CYGHWR_NET_DRIVERS + implements CYGHWR_NET_DRIVER_ETH0 + implements CYGINT_IO_ETH_MULTICAST + + active_if CYGPKG_IO_SPI + + requires { CYGINT_IO_ETH_INT_SUPPORT_REQUIRED implies + (CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR != "") + } + + requires { CYGINT_IO_ETH_INT_SUPPORT_REQUIRED implies + (CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_PRIORITY != "") + } + + include_dir cyg/io/eth + description " + Driver for Microchip ENC424J600 and ENC624J600 Ethernet controllers, + connected to the platform with an SPI interface." + + compile -library=libextras.a enc424j600_spi.c + + define_proc { + puts $::cdl_header "#include <pkgconf/system.h>" + } + + cdl_component CYGPKG_DEVS_ETH_ENC424J600_OPTIONS { + display "Microchip ENC424J600 driver build options" + flavor none + no_define + + cdl_option CYGPKG_DEVS_ETH_ENC424J600_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "-D_KERNEL -D__ECOS" } + description " + This option modifies the set of compiler flags for + building the ENC424J600 ethernet driver package. + These flags are used in addition + to the set of global flags." + } + + } + + cdl_option CYGDAT_IO_ETH_ENC424J600_NAME { + display "Ethernet device name" + flavor data + default_value { "\"eth0\"" } + } + + cdl_component CYGSEM_DEVS_ETH_ENC424J600_NO_AUTO_NEGOTIATION { + display "No auto negotiation of speed and duplex" + default_value 0 + description " + Enable this component in order to manually configure the link speed + and duplex configuration. Otherwise the chip will automatically + negotiate them with the peer." + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_SPEED { + display "Ethernet speed in megabit per second" + flavor data + legal_values { 100 10 } + default_value 100 + } + + cdl_option CYGSEM_DEVS_ETH_ENC424J600_FULL_DUPLEX { + display "Full duplex" + default_value 1 + } + } + + cdl_component CYGSEM_DEVS_ETH_ENC424J600_SET_ESA { + display "Set the Ethernet station address" + flavor bool + default_value 0 + description " + Enabling this option will override the ENC424J600 + factory set ethernet station address (MAC address)." + + cdl_option CYGDAT_DEVS_ETH_ENC424J600_ESA { + display "Ethernet station address" + flavor data + default_value { " { 0x00, 0x04, 0xa3, 0x10, 0x08, 0x3e } "} + description "The ethernet station address" + } + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE { + display "Largest acceptable packet size" + flavor data + legal_values 60 to 9600 + default_value 1518 + description " + This option configures the size in bytes of the largest + packet expected to be sent or received. The chip will + truncate any received or transmitted packet to this + size. This size excludes any space required for the + Preamble, Start of Frame Delimiter and the Frame Check + Sequence (CRC)." + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_TXBUF_SIZE { + display "Size of transmit buffer" + flavor data + legal_values CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE to (0x6000 - CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE -2) + default_value CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE + description " + The 0x6000 bytes of on-chip RAM are divided between transmit + buffer and receive buffer. This parameter impacts the sizes of + both. The transmit buffer must be large enough to hold one + transmit packet." + } + + cdl_option CYGSEM_DEVS_ETH_ENC424J600_FLOWC { + display "Flow control" + flavor data + default_value { "None" } + legal_values { "None" "OnChip" } + description " + Enabling flow control prevents overflow of the input buffer if + received packets are not processed fast enough." + } + + cdl_component CYGNUM_DEVS_ETH_ENC424J600_FLOW_CONTROL_PARAMETERS { + display "Flow control parameters" + active_if { CYGSEM_DEVS_ETH_ENC424J600_FLOWC != "None" } + flavor none + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_FLOWC_UPPER_WATERMARK { + display "Upper threshold for flow control" + flavor data + default_value 16 + legal_values 2 to 255 + description " + This threshold is measured in multiples of blocks of + 96 bytes. When this number of blocks have been used + in the on-chip receive buffer, the device considers its + receive buffer full and initiates flow control." + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_FLOWC_LOWER_WATERMARK { + display "Lower threshold for flow control" + flavor data + default_value 15 + legal_values 1 to (CYGNUM_DEVS_ETH_ENC424J600_FLOWC_UPPER_WATERMARK - 1) + description " + This threshold is measured in multiples of blocks of + 96 bytes. If flow control has been activated, then + when the number of occupied blocks in the on-chip + receive buffer falls below this level, the device + considers the buffer to be empty enough to receive + more data and therefore disengages flow control." + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_FLOWC_PAUSE { + display "Duration of flow control pause" + flavor data + default_value { 0x1000 } + legal_values 0 to 0xffff + description " + Duration of flow control pause in units of 512 bit times. Valid for full-duplex + operation only." + } + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_CLOCKOUT_FREQUENCY { + display "Frequency of clock output at the CLKOUT pin" + flavor data + default_value { "0" } + legal_values { + "0" "33M33" "25M00" + "20M00" "16M67" "12M50" "10M00" + "8M333" "8M000" "6M250" "5M000" + "4M000" "3M125" + "0M0" "100K" "50K" + } + description " + The ENC424J600 has a clock output which can be used by + other parts of the system. M stands for megahertz, K for + kilohertz, and where these letters are used stands for the + position of a decimal point. Values 0 and 0M0 both mean + DC output, or, in other words, no clock output. This + driver does not make use of the clock output facility + itself. Control of the clock output frequency is provided + here as a convenience." + } + + cdl_component CYGPKG_DEVS_ETH_ENC424J600_INTERRUPT { + display "Interrupt configuration" + flavor none + no_define + active_if CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + description " + Configuration options related to interrupt handling." + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_PRIORITY { + display "Interrupt priority" + flavor data + default_value { is_active(CYGNUM_HAL_SPIETH_INTERRUPT_PRIORITY) ? CYGNUM_HAL_SPIETH_INTERRUPT_PRIORITY : "\"\"" } + description " + Interrupt priority for the ENC424J600. The default value + is typically set by the platform HAL." + } + + cdl_option CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR { + display "Interrupt vector" + flavor data + default_value { is_active(CYGNUM_ETH_SPIETH_HAL_INTERRUPT_VECTOR) ? CYGNUM_ETH_SPIETH_HAL_INTERRUPT_VECTOR : "\"\"" } + description " + Interrupt vector for the ENC424J600. The default value is + typically set by the platform HAL." + } + } +} + +# End of enc424j600_eth_drivers.cdl
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/host/test_tcp_client.c @@ -0,0 +1,198 @@ +//================================================================= +// +// test_tcp_client.c +// +// TCP/Ethernet testing - host-side +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Ilija Stanislevik +// Date: 2010-02-10 +//####DESCRIPTIONEND#### +//========================================================================== + +/* + The target hardware runs a listener/server application. The server + calculates checksum on recived packets for the whole duration of + connection. Once conection ends, it displays the checksum. + + The host client generates pseudorandom data and sends it to the target. + Before sending, it calculates checksum. Once data sent, it dispalys + the checksum for comparison with the checksum at target side. + + For now, no attempts has been made to employ a host other than Linux. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#define NUM_ARG 4 + +struct addrinfo * hostaddr_p; +int socket_d; +struct sockaddr serv_addr; + +void print_syntax(char *name) +{ + printf("Syntax: %s ip_address port bytes_to_send\n", name); + printf(" Format ip_address as d1.d2.d3.d4, where dx is integer 0-255\n\n"); +} + +int main(int argc, char *argv[]) +{ + int retval; + struct addrinfo hint_s; + struct addrinfo *p; + char ipstr[INET6_ADDRSTRLEN]; + unsigned long int msg_len; + unsigned long int left_to_send; + unsigned long int i; + unsigned long int csum; + char *msg_p = NULL; + char *fill_p; + unsigned char value; + + if(NUM_ARG != argc) + { + printf("\nError: Wrong number of arguments %d instead of %d.\n", argc-1, NUM_ARG-1); + print_syntax(argv[0]); + return EXIT_FAILURE; + } + + memset(&hint_s, 0, sizeof(struct addrinfo)); + hint_s.ai_family = AF_INET; + hint_s.ai_socktype = SOCK_STREAM; + hint_s.ai_flags = AI_NUMERICSERV; + hint_s.ai_protocol = 0; /* Any protocol */ + hint_s.ai_canonname = NULL; + hint_s.ai_addr = NULL; + hint_s.ai_next = NULL; + retval = getaddrinfo(argv[1], argv[2], &hint_s, &hostaddr_p); + if(0 != retval) + { + printf("\nError: %s\n", gai_strerror(retval)); + print_syntax(argv[0]); + return EXIT_FAILURE; + } + + printf("IP addresses for %s:\n", argv[1]); + + for(p = hostaddr_p; p != NULL; p = p->ai_next) + { + void *addr; + char *ipver; + // get the pointer to the address itself, + // different fields in IPv4 and IPv6: + if (p->ai_family == AF_INET) + { // IPv4 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + addr = &(ipv4->sin_addr); + ipver = "IPv4"; + } + else + { // IPv6 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + addr = &(ipv6->sin6_addr); + ipver = "IPv6"; + } + // convert the IP to a string and print it: + inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); + printf(" %s: %s\n", ipver, ipstr); + } + + socket_d = socket(hostaddr_p->ai_family, hostaddr_p->ai_socktype, hostaddr_p->ai_protocol); + if(-1 == socket_d) + { + printf("Error: Can't get socket. errno %d\n", errno); + return EXIT_FAILURE; + } + + retval = connect(socket_d, hostaddr_p->ai_addr, hostaddr_p->ai_addrlen); + if(0 != retval) + { + perror("\nError: Failed connect()\n"); + return EXIT_FAILURE; + } + + // Prepare message to send + msg_len = strtoul(argv[3], NULL, 0); + msg_len &= ~0x3; + if (0 == msg_len) + { + printf("Nothing to do (bytes_to_send == 0). Exiting.\n"); + return EXIT_SUCCESS; + } + else + { + printf("Preparing to send %lu bytes.\n", msg_len); + } + + msg_p = malloc(msg_len); + if(NULL == msg_p) + { + printf("Error: Can't allocate %lu bytes.\n", msg_len); + return EXIT_FAILURE; + } + + fill_p = msg_p; + csum = 0; + + for(i=0; i<msg_len; i++) + { + value = random() & 0xff; + *fill_p++ = value; + csum += value; + } + + left_to_send = msg_len; + do + { + retval = send(socket_d, msg_p, msg_len, 0); + left_to_send -= retval; + printf("%d bytes sent.\n", retval); + } while(left_to_send); + printf("Checksum %08lx\n", csum); + + freeaddrinfo(hostaddr_p); // free the linked list + + return EXIT_SUCCESS; +}
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/include/enc424j600_eth.h @@ -0,0 +1,100 @@ +#ifndef CYGONCE_DEVS_ETH_ENC424J600_ETH_H_ +#define CYGONCE_DEVS_ETH_ENC424J600_ETH_H_ + +//========================================================================== +// +// enc424j600_eth.h +// +// Microchip enc424j600 Ethernet chip +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010, 2012 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Ilija Stanislevik +// Contributors: +// Date: 2010-11-23 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/devs_eth_enc424j600.h> + +// Ethernet flow control options +enum enc424j600_flow_control_e +{ + ENC424J600_NO_FC, // No flow control + ENC424J600_ONCHIP_AUTO_FC // On-chip automatic flow control. Not tested yet. +}; + +// enc424j600 clock out frequencies +enum enc424j600_clkout_e +{ + ENC424J600_CLKO_0, // DC + ENC424J600_CLKO_33M33, ENC424J600_CLKO_25M00, ENC424J600_CLKO_20M00, + ENC424J600_CLKO_16M67, ENC424J600_CLKO_12M50, ENC424J600_CLKO_10M00, + ENC424J600_CLKO_8M333, ENC424J600_CLKO_8M000, ENC424J600_CLKO_6M250, + ENC424J600_CLKO_5M000, ENC424J600_CLKO_4M000, ENC424J600_CLKO_3M125, + ENC424J600_CLKO_0M0, // DC + ENC424J600_CLKO_100K, ENC424J600_CLKO_50K +}; + +struct enc424j600_priv_data_s; +typedef cyg_bool (*provide_esa_t)(struct enc424j600_priv_data_s*); + +typedef struct enc424j600_priv_data_s +{ + // Configurable data + cyg_spi_device *spi_service_device; // SPI device used to communicate + // with the Ethernet chip + provide_esa_t provide_esa; + bool hardwired_esa; + unsigned char esa[ETHER_ADDR_LEN]; + cyg_handle_t interrupt_handle; + cyg_interrupt interrupt_object; + struct cyg_netdevtab_entry *tab; + + // Really private data + cyg_uint16 NextPacketPointer; + enum { ENC424J600_LINK_OFF, ENC424J600_LINK_ON } link_status; + bool txbusy; + unsigned long txkey; +} enc424j600_priv_data_t; + +#endif /* CYGONCE_DEVS_ETH_ENC424J600_ETH_H_ */ + +// End of enc424j600_eth.h
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/src/enc424j600_spi.c @@ -0,0 +1,1340 @@ +//========================================================================== +// +// enc424j600_spi.c +// +// Microchip enc424j600 Ethernet chip +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010, 2012 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Ilija Stanislevik +// Contributors: +// Date: 2010-11-23 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_if.h> + +#include <cyg/infra/cyg_type.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> +#include <cyg/infra/diag.h> +#include <cyg/hal/drv_api.h> +#include <cyg/io/eth/netdev.h> +#include <cyg/io/eth/eth_drv.h> + +#include <cyg/io/spi.h> // Common SPI API + +#include "enc424j600_spi.h" +#include <cyg/io/eth/enc424j600_eth.h> + +// Set ENC424J600_DEBUG to: +// 0 to suppress all printout +// 1 to print error reports +// 2 to print interrupt tracing +// 4 printout _init() progress +// 8 to print progress tracing data +// 0x10 for test with dummy stack +// 0x20 to printout status registers +#define ENC424J600_DEBUG (0) + +#define ENC424J600_DONT_DROP_CS (0) +#define ENC424J600_DO_DROP_CS (1) +#define ENC424J600_TXBUF_START (0) + +#define ENC424J600_RXBUF_START (ENC424J600_TXBUF_START + CYGNUM_DEVS_ETH_ENC424J600_TXBUF_SIZE) + +#ifndef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED +# define SPI_POLLING true +#else +# define SPI_POLLING false +#endif + +static enc424j600_priv_data_t eth0_enc424j600_priv_data; + +ETH_DRV_SC(eth0_spi_sc, + ð0_enc424j600_priv_data, // Driver specific data. + CYGDAT_IO_ETH_ENC424J600_NAME, + enc424j600_spi_start, + enc424j600_spi_stop, + enc424j600_spi_control, + enc424j600_spi_can_send, + enc424j600_spi_send, + enc424j600_spi_recv, + enc424j600_spi_deliver, + enc424j600_spi_poll, + enc424j600_spi_int_vector +); + +NETDEVTAB_ENTRY(eth0_spi_netdev, + "eth_spi", + enc424j600_spi_init, + ð0_spi_sc); + +// Local service functions + +// Interface for enc424j600 "single byte instruction". +static void +simple_operation(enc424j600_priv_data_t *dpd, enum enc424j600_spi_opcode_1_e opcode) +{ + cyg_spi_transaction_begin(dpd->spi_service_device); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 1, + & opcode, + NULL, // not interested in answer + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(dpd->spi_service_device); + + return; +} + +// Interface for enc424j600 "banked N-byte instructions". +// Banked read, write, bit set or bit clear operation with control register +static int +banked_register_operation(enc424j600_priv_data_t *cpd, + enum enc424j600_spi_opcode_e opcode, + cyg_uint8 address, + cyg_uint16 length, + cyg_uint8 *buffer) +{ + if ((NULL == buffer) || (0 == length)) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Wrong parm buffer=0x%x or length=%d.\n", (int)buffer, length); +#endif + return -1; + } + + if ((ENC424J600_ECON1H < address) || (opcode & ENC424J600_ECON1H)) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Wrong address=0x%02x or opcode=0x%02x for banked operation.\n", + address, opcode); +#endif + return -1; + } + + address |= opcode; // combine them in single byte + cyg_spi_transaction_begin(cpd->spi_service_device); + // Send the opcode and address, + cyg_spi_transaction_transfer(cpd->spi_service_device, + SPI_POLLING, + 1, + & address, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + // then the data. + cyg_spi_transaction_transfer(cpd->spi_service_device, + SPI_POLLING, + length, + (ENC424J600_READCR == opcode) ? NULL : buffer, + (ENC424J600_READCR == opcode) ? buffer : NULL, + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(cpd->spi_service_device); + + return 0; +} + +// Interface for enc424j600 "unbanked N-byte instructions". +// Unbanked read, write, bit set or bit clear operation with control register +static int +unbanked_register_operation(enc424j600_priv_data_t *cpd, + enum enc424j600_spi_opcode_u_e opcode, + cyg_uint8 address, + cyg_uint16 length, + cyg_uint8 *buffer) +{ + if ((NULL == buffer) || (0 == length)) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Wrong parm buffer=0x%x or length=%d.\n", (int)buffer, length); +#endif + return -1; + } + + cyg_spi_transaction_begin(cpd->spi_service_device); + // Send the opcode, + cyg_spi_transaction_transfer(cpd->spi_service_device, + SPI_POLLING, + 1, + & opcode, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + // then the address + cyg_spi_transaction_transfer(cpd->spi_service_device, + SPI_POLLING, + 1, + & address, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + // and finally the data. + cyg_spi_transaction_transfer(cpd->spi_service_device, + SPI_POLLING, + length, + (ENC424J600_READCRU == opcode) ? NULL : buffer, + (ENC424J600_READCRU == opcode) ? buffer : NULL, + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(cpd->spi_service_device); + + return 0; +} + +// Get ESA from the Ethernet chip, unbanked +static int +read_esa_u(enc424j600_priv_data_t *cpd, cyg_uint8 *esa_buf) +{ + int retval = -1; + cyg_uint8 response_msg[ETHER_ADDR_LEN]; + +#if ENC424J600_DEBUG & 8 + diag_printf("read_esa_u()...\n"); +#endif + if (NULL == esa_buf) + { + return -1; + } + + retval = unbanked_register_operation(cpd, ENC424J600_READCRU, ENC424J600_MAADR3_U, ETHER_ADDR_LEN, + &response_msg[0]); + if (0 != retval) + { + return retval; + } + *esa_buf++ = response_msg[4]; + *esa_buf++ = response_msg[5]; + *esa_buf++ = response_msg[2]; + *esa_buf++ = response_msg[3]; + *esa_buf++ = response_msg[0]; + *esa_buf = response_msg[1]; + + return 0; +} + +// Set ESA into Ethernet chip, unbanked +static int +write_esa_u(enc424j600_priv_data_t *dpd, cyg_uint8 *esa_buf) +{ + int retval = -1; + cyg_uint8 msg[8]; + +#if ENC424J600_DEBUG & 8 + diag_printf("write_esa_u()...1...\n"); +#endif + if (NULL == esa_buf) + { + return -1; + } + + msg[4] = *esa_buf++; + msg[5] = *esa_buf++; + msg[2] = *esa_buf++; + msg[3] = *esa_buf++; + msg[0] = *esa_buf++; + msg[1] = *esa_buf; + + retval = unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MAADR3_U, 6, &msg[0]); + if (0 != retval) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Writing ESA into chip failed.\n"); +#endif + return retval; + } + + return 0; +} + +#if ENC424J600_DEBUG & 0x20 +// Read data from PHY register +static void +read_phy(enc424j600_priv_data_t *dpd, + const enum enc424j600_pr_address_e pr_address, + cyg_uint16 *retval_p) +{ + cyg_uint8 aux[2] = {pr_address, 0x01}; + cyg_uint8 status; + + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MIREGADR_U, 2, &aux[0]); + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MICMD_U, 2, &aux[0]); + aux[0] |= ENC424J600_MIIRD; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MICMD_U, 2, &aux[0]); + CYGACC_CALL_IF_DELAY_US(26); + do + { // Check and busy wait for read operation to complete + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MISTAT_U, 1, &status); + } while(0 != (status & ENC424J600_MISTAT_BUSY)); + + aux[0] &= !ENC424J600_MIIRD; // Clear ENC424J600_MIIRD bit + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MICMD_U, 2, &aux[0]); + + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MIRD_U, 2, &aux[0]); + *retval_p = aux[0] + 256 * aux[1]; + + return; +} +#endif + +// Write data to PHY register +static void +write_phy(enc424j600_priv_data_t *dpd, + const enum enc424j600_pr_address_e pr_address, + const cyg_uint16 value) +{ + cyg_uint8 aux[2] = {pr_address, 0x01}; + + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MIREGADR_U, 2, &aux[0]); + aux[0] = value & 0xff; + aux[1] = (value >> 8) & 0xff; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MIWR_U, 2, &aux[0]); + + return; + // There must be no other MIIM operation in the following 25.6 us! +} + +// Write value for on-chip pointer. +static void +set_pointer(enc424j600_priv_data_t *dpd, + enum enc424j600_spi_bufferp_e oc_pointer, + cyg_uint16 value + ) +{ + cyg_spi_transaction_begin(dpd->spi_service_device); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 1, + & oc_pointer, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 2, + (cyg_uint8 *) & value, + NULL, // not interested in answer + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(dpd->spi_service_device); + return; +} + +static inline void +write_to_tx_buffer(enc424j600_priv_data_t *dpd, + cyg_uint16 length, + cyg_uint8 *buffer) +{ + cyg_uint8 oc_pointer = ENC424J600_WGPDATA; // We use General Purpose Buffer + + cyg_spi_transaction_begin(dpd->spi_service_device); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 1, + & oc_pointer, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + length, + buffer, + NULL, + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(dpd->spi_service_device); + + return; +} + +static int +read_from_rx_buffer(enc424j600_priv_data_t *dpd, + cyg_uint16 length, + cyg_uint8 *buffer) +{ + cyg_uint8 oc_pointer = ENC424J600_RRXDATA; + +// if ((0 == length) || (NULL == buffer)) + if (0 == length) + { + return -1; + } + cyg_spi_transaction_begin(dpd->spi_service_device); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 1, + & oc_pointer, + NULL, // not interested in answer + ENC424J600_DONT_DROP_CS); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + length, + NULL, + buffer, + ENC424J600_DO_DROP_CS); + cyg_spi_transaction_end(dpd->spi_service_device); + + return 0; +} + +static void +set_MAC(enc424j600_priv_data_t *dpd, + cyg_uint8 ethernet_status // As read from upper byte of ESTAT register + ) +{ + cyg_uint8 aux[2]; + + if (0 != (ethernet_status & ENC424J600_PHYDPX)) + { // Full duplex +#if ENC424J600_DEBUG & 0x20 + diag_printf("Full duplex\n"); +#endif + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MACON2_U, 2, &aux[0]); + aux[0] |= ENC424J600_FULDPX; // Set MAC to full duplex + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MACON2_U, 2, &aux[0]); + + aux[1] = 0; + aux[0] = 0x15; // Inter-packet gap + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MABBIPG_U, 2, &aux[0]); + } + else + { // Half duplex +#if ENC424J600_DEBUG & 0x20 + diag_printf("Half duplex\n"); +#endif + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MACON2_U, 2, &aux[0]); + aux[0] &= !ENC424J600_FULDPX; // Set MAC to half duplex + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MACON2_U, 2, &aux[0]); + + aux[1] = 0; + aux[0] = 0x12; // Inter-packet gap + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MABBIPG_U, 2, &aux[0]); + } + + return; +} + +static void +enc424j600_spi_LINKevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 iflag = ENC424J600_LINKIF; + cyg_uint8 aux[2]; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_LINKevent()....\n"); +#endif + + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ESTATL, 2, &aux[0]); +#if ENC424J600_DEBUG & 0x20 + diag_printf("ESTAT %02x%02x Link ", aux[1], aux[0]); +#endif + if (0 != (aux[1] & ENC424J600_PHYLNK)) + { + dpd->link_status = ENC424J600_LINK_ON; + +#if ENC424J600_DEBUG & 0x20 + diag_printf("ON\n"); +#endif + // Link is just established. Set up the MAC + set_MAC(dpd, aux[1]); + } + else + { + dpd->link_status = ENC424J600_LINK_OFF; + +#if ENC424J600_DEBUG & 0x20 + diag_printf("OFF\n"); +#endif + } +#if ENC424J600_DEBUG & 0x20 + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MACON1_U, 2, &aux[0]); + diag_printf("MACON1 %02x%02x\n", aux[1], aux[0]); + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MACON2_U, 2, &aux[0]); + diag_printf("MACON2 %02x%02x\n", aux[1], aux[0]); + read_phy(dpd, ENC424J600_PHSTAT1, (cyg_uint16 *) &aux[0]); + diag_printf("PHSTAT1 %02x%02x\n", aux[1], aux[0]); + read_phy(dpd, ENC424J600_PHSTAT3, (cyg_uint16 *) &aux[0]); + diag_printf("PHSTAT3 %02x%02x\n", aux[1], aux[0]); + read_phy(dpd, ENC424J600_PHANLPA, (cyg_uint16 *) &aux[0]); + diag_printf("PHANLPA %02x%02x\n", aux[1], aux[0]); + read_phy(dpd, ENC424J600_PHANA, (cyg_uint16 *) &aux[0]); + diag_printf("PHANA %02x%02x\n", aux[1], aux[0]); + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ECON1L, 2, &aux[0]); + diag_printf("ECON1 %02x%02x\n", aux[1], aux[0]); + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_ECON2_U, 2, &aux[0]); + diag_printf("ECON2 %02x%02x\n", aux[1], aux[0]); + +#endif + + // Clear Link interrupt flag + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRH, 1, &iflag); +} + +static void +enc424j600_spi_PCFULevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 flags = ENC424J600_PCFULIF; + +#if ENC424J600_DEBUG & 8 + diag_printf("\nenc424j600_spi_PCFULevent()....\n\n"); +#endif + simple_operation(dpd, ENC424J600_SETPKTDEC); // Decrement received packet counter + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRL, 1, &flags); +} + +static void +enc424j600_spi_RXABTevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 flags = ENC424J600_RXABTIF; + +#if ENC424J600_DEBUG & 8 + diag_printf("\nenc424j600_spi_RXABTevent()....\n\n"); +#endif + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRL, 1, &flags); +} + +static void +enc424j600_spi_TXevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 iflag = ENC424J600_TXIF; + +#if ENC424J600_DEBUG & 8 + diag_printf("TX event.\n"); +#endif + + dpd->txbusy = false; + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRL, 1, &iflag); +} + +static void +enc424j600_spi_TXABTevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 iflag = ENC424J600_TXABTIF; + +#if ENC424J600_DEBUG & 8 + diag_printf("TXABT event.\n"); +#endif + + dpd->txbusy = false; + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRL, 1, &iflag); +} + +static void +enc424j600_spi_RXevent(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 pkcnt; // Local copy of the packet counter + cyg_uint8 rsv[2]; + cyg_uint16 packet_length; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_RXevent()....\n"); +#endif + + // Take info on single received packet + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ESTATL, 1, &pkcnt); + if (0 != pkcnt) + { +#if ENC424J600_DEBUG & 8 + diag_printf("There are %u packets pending in receive buffer.\n", pkcnt); +#endif + // Prepare on-chip read pointer + set_pointer(dpd, ENC424J600_RXRDPT, dpd->NextPacketPointer); + // The two bytes to read next point to the next packet + read_from_rx_buffer(dpd, 2, (cyg_uint8 *) & dpd->NextPacketPointer); + + // Next 6 bytes to read are the Read Status Vector. We need only the first two. + read_from_rx_buffer(dpd, 2, & rsv[0]); + read_from_rx_buffer(dpd, 4, NULL); + packet_length = rsv[0] + rsv[1] * 256; + sc->funs->eth_drv->recv(sc, packet_length); + } + + return; +} + +// - End of local service functions ------------------------- + +// - Higher level interface functions ----------------------- + +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED +// This ISR is called when the Ethernet interrupt occurs +static cyg_uint32 +enc424j600_spi_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs) +{ + cyg_drv_interrupt_mask(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR); + cyg_drv_interrupt_acknowledge(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR); + + return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR +} + +static void +enc424j600_spi_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)data; + struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(dpd->tab); + struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance); +#if (ENC424J600_DEBUG & 2) + diag_printf("DSR: Interrupt! count %u\n", count); +#endif + +#if (ENC424J600_DEBUG & 0x10) + extern void fake_eth_drv_dsr(cyg_vector_t vector, + cyg_ucount32 count, + cyg_addrword_t data); + + fake_eth_drv_dsr( vector, count, (cyg_addrword_t)sc ); +#else + eth_drv_dsr( vector, count, (cyg_addrword_t)sc ); +#endif + + return; +} +#endif // #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + +// Initialization of driver and chip + +#define ENC424J600_INIT_SPI_READY_RETRY 100 +#define ENC424J600_INIT_CLK_READY_RETRY 100 +#define ENC424J600_INIT_RETRY_PERIOD_US 100 + +static bool +enc424j600_spi_init(struct cyg_netdevtab_entry *tab) +{ + int retval; + cyg_uint8 aux_8[2]; + cyg_bool esa_configured = false; +#ifdef CYGSEM_DEVS_ETH_ENC424J600_SET_ESA + cyg_uint8 esa_from_cdl[ETHER_ADDR_LEN] = CYGDAT_DEVS_ETH_ENC424J600_ESA; +#endif + unsigned int i; // loop counter + + struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + + dpd->tab = tab; + + CYG_DEVS_ETH_ENC424J600_PLF_INIT(tab); // Platform HAL should define this macro + +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + // Install interrupt handler. + cyg_drv_interrupt_create(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR, + CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_PRIORITY, + (cyg_addrword_t)dpd, // Data item passed to interrupt handler + (cyg_ISR_t *)enc424j600_spi_isr, + (cyg_DSR_t *)enc424j600_spi_dsr, + &dpd->interrupt_handle, + &dpd->interrupt_object); + cyg_drv_interrupt_attach(dpd->interrupt_handle); + cyg_drv_interrupt_configure(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR, + ENC424J600_ETH_INTERRUPT_LEVEL_LOW, ENC424J600_ETH_INTERRUPT_EDGE_FALLING); + cyg_drv_interrupt_acknowledge(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR); + cyg_drv_interrupt_unmask(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR); +#if ENC424J600_DEBUG & 4 + diag_printf("%s(): Interrupt handler is installed.\n", __FUNCTION__); +#endif +#endif // #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + + // Check if the Ethernet chip is connected and listening + for (i=0; i <= ENC424J600_INIT_SPI_READY_RETRY; i++) + { + aux_8[1] = (cyg_uint8)0x12; // Test pattern + aux_8[0] = (cyg_uint8)0x34; + banked_register_operation(dpd, ENC424J600_WRITECR, ENC424J600_EUDASTL, 2, &aux_8[0]); + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_EUDASTL, 2, &aux_8[0]); + // Verify the test pattern + if ((0x12 == aux_8[1]) && (0x34 == aux_8[0])) + { // verified + break; + } + CYGACC_CALL_IF_DELAY_US(ENC424J600_INIT_RETRY_PERIOD_US); + } + + if (i > ENC424J600_INIT_SPI_READY_RETRY) + { + CYG_FAIL("enc424j600 SPI is not ready!\n"); + return false; + } + +#if ENC424J600_DEBUG & 4 + diag_printf("enc424j600 SPI is there.\n"); +#endif + + // Check if ENC424J600_CLKRDY flag is set + for (i=0; i <= ENC424J600_INIT_CLK_READY_RETRY; i++) + { + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ESTATL, 2, &aux_8[0]); + if (0 == (aux_8[1] & ENC424J600_CLKRDY)) + { + i++; + } + else + { // clock is ready + break; + } + CYGACC_CALL_IF_DELAY_US(ENC424J600_INIT_RETRY_PERIOD_US); + } + if (i > ENC424J600_INIT_CLK_READY_RETRY) + { + CYG_FAIL("enc424j600 clock is not ready!\n"); + return false; + } + +#if ENC424J600_DEBUG & 4 + diag_printf("enc424j600 clock is ready.\n"); +#endif + + // Issue a System Reset for Ethernet chip + aux_8[0] = ENC424J600_ETHRST; + unbanked_register_operation(dpd, ENC424J600_BITFIELDSETU, ENC424J600_ECON2_U, 1, &aux_8[0]); + CYGACC_CALL_IF_DELAY_US(25); + // Check if the reset really happened + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_EUDASTL, 2, &aux_8[0]); + if ((0 != aux_8[0]) || (0 != aux_8[1])) + { + CYG_FAIL("enc424j600 is not reset!\n"); + return false; + } +#if ENC424J600_DEBUG & 4 + diag_printf("Successful reset of the Ethernet chip.\n"); +#endif + + CYGACC_CALL_IF_DELAY_US(256); + +#define CLOCKOUT_CODE(_freq_) CLOCKOUT_LABEL(_freq_) +#define CLOCKOUT_LABEL(_freq_) (ENC424J600_CLKO_##_freq_) + + // Set clock output frequency. External hardware may need it. + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_ECON2_U + 1, 1, &aux_8[1]); + aux_8[1] &= 0xf0; + aux_8[1] |= (cyg_uint8)CLOCKOUT_CODE(CYGNUM_DEVS_ETH_ENC424J600_CLOCKOUT_FREQUENCY); +#if ENC424J600_DEBUG & 4 + diag_printf("Setting clock out, ECON2H 0x%02x\n", aux_8[1]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ECON2_U + 1, 1, &aux_8[1]); + + + // Find ESA - check possible sources in sequence and stop when + // one provides the ESA: + // RedBoot option (via provide_esa) + // Set by application + // Set by configuration option + // Ethernet chip (manufacture-time set) + + if (NULL != dpd->provide_esa) + { + esa_configured = dpd->provide_esa(dpd); +# if ENC424J600_DEBUG & 4 + if (esa_configured) + diag_printf("Got ESA from RedBoot option\n"); +# endif + } + if (!esa_configured && dpd->hardwired_esa) + { + // ESA is already set in dpd->esa[] +#if ENC424J600_DEBUG & 4 + diag_printf("Got hardcoded ESA\n"); +#endif + esa_configured = true; + } +#ifdef CYGSEM_DEVS_ETH_ENC424J600_SET_ESA + else + { + memcpy(dpd->esa, esa_from_cdl, ETHER_ADDR_LEN); + esa_configured = true; +# if ENC424J600_DEBUG & 4 + diag_printf("Got ESA from CDL.\n"); +# endif + } +#endif // #ifdef CYGSEM_DEVS_ETH_ENC424J600_SET_ESA + + if (!esa_configured) + { +# if ENC424J600_DEBUG & 4 + diag_printf("ESA is not provided. Getting it from the chip.\n"); +# endif + retval = read_esa_u(dpd, &(dpd->esa[0])); + if (0 != retval) + { + CYG_FAIL("Reading ESA from chip failed.\n"); + return false; + } + } + else + { +# if ENC424J600_DEBUG & 4 + diag_printf("Setting ESA into chip. Will last till next reset.\n"); +# endif + retval = write_esa_u(dpd, &(dpd->esa[0])); + if (0 != retval) + { + CYG_FAIL("Writing ESA into chip failed.\n"); + return false; + } + } + +# if ENC424J600_DEBUG & 4 + cyg_uint8 cesa[ETHER_ADDR_LEN] = {0,0,0,0,0,0}; + diag_printf("ESA %02x:%02x:%02x:%02x:%02x:%02x\n", + dpd->esa[0], dpd->esa[1], dpd->esa[2], + dpd->esa[3], dpd->esa[4], dpd->esa[5]); + retval = read_esa_u(dpd, &cesa[0]); + if (0 != retval) + { + diag_printf("Reading ESA from chip failed.\n"); + } + diag_printf("Control reading from chip: "); + diag_printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + cesa[0], cesa[1], cesa[2], + cesa[3], cesa[4], cesa[5]); +# endif + + // Set receive filters + aux_8[0] = ENC424J600_BCEN | ENC424J600_UCEN | ENC424J600_RUNTEN | ENC424J600_CRCEN; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXFCON_U, 1, &aux_8[0]); + +#ifdef CYGSEM_DEVS_ETH_ENC424J600_FLOWC_OnChip +#if ENC424J600_DEBUG & 4 + diag_printf("eth_spi_enc424j600_init() Setting auto flow control\n"); +#endif + // Duration of pause + aux_8[0] = CYGNUM_DEVS_ETH_ENC424J600_FLOWC_PAUSE & 0xff; + aux_8[1] = (CYGNUM_DEVS_ETH_ENC424J600_FLOWC_PAUSE >> 8 ) & 0xff; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_EPAUS_U, 2, &aux_8[0]); + + // Thresholds for flow control + aux_8[0] = CYGNUM_DEVS_ETH_ENC424J600_FLOWC_LOWER_WATERMARK; + aux_8[1] = CYGNUM_DEVS_ETH_ENC424J600_FLOWC_UPPER_WATERMARK; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXWM_U, 2, &aux_8[0]); + + aux_8[0] = ENC424J600_AUTOFC; // Enable automatic flow control + unbanked_register_operation(dpd, ENC424J600_BITFIELDSETU, ENC424J600_ECON2_U, 1, &aux_8[0]); + +#else + // No flow control + simple_operation(dpd, ENC424J600_FCDISABLE); +#endif + + // Set PHY speed and duplex + aux_8[0] = aux_8[1] = 0; +#ifndef CYGNUM_DEVS_ETH_ENC424J600_NO_AUTO_NEGOTIATION + aux_8[1] = ENC424J600_ANEN; // Enable auto negotiation +#else +# if(CYGNUM_DEVS_ETH_ENC424J600_SPEED == 100) + aux_8[1] |= ENC424J600_SPD100; +# endif +# ifdef CYGSEM_DEVS_ETH_ENC424J600_FULL_DUPLEX + aux_8[1] |= ENC424J600_PFULDPX; +# endif +#endif +#if ENC424J600_DEBUG & 4 + diag_printf("Writing PHCON1 0x%02x%02x\n", aux_8[1], aux_8[0]); +#endif + write_phy(dpd, ENC424J600_PHCON1, (aux_8[1] << 8) + aux_8[0]); + CYGACC_CALL_IF_DELAY_US(26); + + // Compose and set PHY capabilities advertisement register + aux_8[1] = 0; + aux_8[0] = ENC424J600_IEEE802_3STD; +#ifdef CYGNUM_DEVS_ETH_ENC424J600_FLOWC_ENC424J600_ONCHIP_AUTO_FC + aux_8[1] |= ENC424J600_ADPAUS_SYMM; +#endif + +#ifdef CYGNUM_DEVS_ETH_ENC424J600_NO_AUTO_NEGOTIATION +# ifdef CYGSEM_DEVS_ETH_ENC424J600_FULL_DUPLEX +# if(CYGNUM_DEVS_ETH_ENC424J600_SPEED == 100) + aux_8[1] |= ENC424J600_AD100FD; +# else + aux_8[0] |= ENC424J600_AD10FD; +# endif +# else +# if(CYGNUM_DEVS_ETH_ENC424J600_SPEED == 100) + aux_8[0] |= ENC424J600_AD100; +# else + aux_8[0] |= ENC424J600_AD10; +# endif +# endif +#else + aux_8[1] |= ENC424J600_AD100FD; + aux_8[0] |= ENC424J600_AD100 | ENC424J600_AD10FD | ENC424J600_AD10; +#endif + + write_phy(dpd, ENC424J600_PHANA, (aux_8[1] << 8) + aux_8[0]); + +#if ENC424J600_DEBUG & 4 + diag_printf("Setting CRC generation\n"); +#endif + + // Set ENC424J600_TXCRCEN, PADCFG + unbanked_register_operation(dpd, ENC424J600_READCRU, ENC424J600_MACON2_U, 2, &aux_8[0]); + aux_8[0] = 0xa0; // Pad VLAN frames to 64bytes, others to 60 + aux_8[0] |= ENC424J600_TXCRCEN; // Calculate and append CRC in transmit frames + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MACON2_U, 2, &aux_8[0]); + +#if ENC424J600_DEBUG & 4 + diag_printf("Setting acceptable packet size\n"); +#endif + // Set acceptable packet size + aux_8[0] = CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE & 0xff; + aux_8[1] = (CYGNUM_DEVS_ETH_ENC424J600_ACCEPTABLE_PACKET_SIZE >> 8) & 0xff; + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_MAMXFL_U, 2, &aux_8[0]); + +#if ENC424J600_DEBUG & 2 + diag_printf("Initializing upper level driver\n"); +#endif + (sc->funs->eth_drv->init)(sc, dpd->esa); + +#if ENC424J600_DEBUG & 4 + diag_printf("End of _init().\n"); +#endif + + return true; +} + +// +// This function is called to "start up" the interface. It may be called +// multiple times, even when the hardware is already running. It will be +// called whenever something "hardware oriented" changes and should leave +// the hardware ready to send/receive packets. +// +static void +enc424j600_spi_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 aux[2]; + cyg_uint8 pkcnt; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_start() begin.\n"); +#endif + // Disable packet reception + simple_operation(dpd, ENC424J600_DISABLERX); + + // Enable interrupts in Ethernet chip + simple_operation(dpd, ENC424J600_CLREIE); // First disable interrupts globally + + aux[0] = ENC424J600_PKTIE | ENC424J600_TXIE | ENC424J600_TXABTIE | ENC424J600_RXABTIE | ENC424J600_PCFULIE; // then set the desired ones + aux[1] = ENC424J600_LINKIE; + unbanked_register_operation(dpd, ENC424J600_BITFIELDSETU, ENC424J600_EIE_U, 2, &aux[0]); + + // Are there any packets left in receive buffer? + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ESTATL, 1, &pkcnt); + + while (0 < pkcnt--) + { + simple_operation(dpd, ENC424J600_SETPKTDEC); // Decrement on-chip packet counter + } // This clears ENC424J600_PKTIF, if set. + + // Set the chip's receive buffer start address and buffer head. + aux[0] = ENC424J600_RXBUF_START & 0xfe; // make it even, just in case it's not + aux[1] = (ENC424J600_RXBUF_START >> 8) & 0x7f; // clear the MSB +#if ENC424J600_DEBUG & 8 + diag_printf("Setting receive buffer start address, ERXST 0x%02x%02x.\n", + aux[1], aux[0]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXST_U, 2, &aux[0]); + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXHEAD_U, 2, &aux[0]); + + // Next packet will be received at the buffer start address + *(cyg_uint8 *) & dpd->NextPacketPointer = ENC424J600_RXBUF_START & 0xff; + *(((cyg_uint8 *)&dpd->NextPacketPointer) + 1) = (ENC424J600_RXBUF_START >> 8 ) & 0xff; + + // Set receive buffer tail + aux[1] = 0x5f; // just bellow the end of on-chip SRAM + aux[0] = 0xfe; +#if ENC424J600_DEBUG & 8 + diag_printf("Setting receive buffer tail, ERXTAIL 0x%02x%02x.\n", + aux[1], aux[0]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXTAIL_U, 2, &aux[0]); + + // Abort any pending transmission + aux[0] = ENC424J600_TXRTS; + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_ECON1L, 1, &aux[0]); + dpd->txbusy = false; + + // Set start of transmit buffer + aux[0] = ENC424J600_TXBUF_START & 0xff; + aux[1] = (ENC424J600_TXBUF_START >> 8 ) & 0xff; +#if ENC424J600_DEBUG & 8 + diag_printf("Writing ETXST 0x%02x%02x.\n", aux[1], aux[0]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ETXST_U, 2, &aux[0]); + + // Clear any remaining interrupt flags + aux[0] = ENC424J600_TXIF | ENC424J600_TXABTIF | ENC424J600_RXABTIF | ENC424J600_PCFULIF; + aux[1] = ENC424J600_LINKIF; + banked_register_operation(dpd, ENC424J600_BITFIELDCLEAR, ENC424J600_EIRL, 2, &aux[0]); + +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + simple_operation(dpd, ENC424J600_SETEIE); // Enable interrupts +#endif + + // Enable packet reception + simple_operation(dpd, ENC424J600_ENABLERX); + + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_ESTATL, 2, &aux[0]); + if (0 != (aux[1] & ENC424J600_PHYLNK)) + { + if (ENC424J600_LINK_ON != dpd->link_status) + { + dpd->link_status = ENC424J600_LINK_ON; + set_MAC(dpd, aux[1]); + } + } + else + { + dpd->link_status = ENC424J600_LINK_OFF; + } + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_start() end.\n"); +#endif + + return; +} + +// Stop receiving and sending packets +static void +enc424j600_spi_stop(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 aux[2]; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_stop() begin.\n"); +#endif + // Disable packet reception + simple_operation(dpd, ENC424J600_DISABLERX); + + // Disable interrupts in Ethernet chip + aux[1] = ENC424J600_INTIE; + aux[0] = ENC424J600_PKTIE | ENC424J600_TXIE | ENC424J600_TXABTIE | ENC424J600_RXABTIE | ENC424J600_PCFULIE; + unbanked_register_operation(dpd, ENC424J600_BITFIELDCLEARU, ENC424J600_EIE_U, 2, &aux[0]); + + return; +} + +static void +enc424j600_spi_deliver(struct eth_drv_sc *sc) +{ + + enc424j600_spi_poll(sc); + + return; +} + +// Special control operations +static int +enc424j600_spi_control(struct eth_drv_sc *sc, unsigned long key, void *data, + int data_length) +{ + enc424j600_priv_data_t *dpd; + unsigned char *esa; + struct eth_drv_mc_list *mc_list; + cyg_uint8 aux; + + if ((NULL == sc) || (NULL == data)) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Wrong parameter(s) for enc424j600_spi_control().\n"); +#endif + return -1; + } + dpd = (enc424j600_priv_data_t *)sc->driver_private; + switch (key) + { + case ETH_DRV_SET_MAC_ADDRESS: + if (ETHER_ADDR_LEN != data_length) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Wrong data length for ETH_DRV_SET_MAC_ADDRESS.\n"); +#endif + return -1; + } + esa = (unsigned char *)data; + // Write ESA in the chip. Will last till the next reset. + return write_esa_u(dpd, esa); + +#ifdef ETH_DRV_GET_MAC_ADDRESS + case ETH_DRV_GET_MAC_ADDRESS: + if (ETHER_ADDR_LEN > data_length) + { +#if ENC424J600_DEBUG & 1 + diag_printf("Too small data length for ETH_DRV_GET_MAC_ADDRESS.\n"); +#endif + return -1; + } + esa = (unsigned char *)data; + // Get ESA from the chip + return read_esa_u(dpd, esa); +#endif + + case ETH_DRV_SET_MC_LIST: + case ETH_DRV_SET_MC_ALL: + // Note: this code always affects all multicast addresses if any + // are desired. + mc_list = data; + simple_operation(dpd, ENC424J600_DISABLERX); + aux = ENC424J600_MCEN; + if (0 == mc_list->len) + { // Disable reception of multicast packets + unbanked_register_operation(dpd, + ENC424J600_BITFIELDCLEARU, ENC424J600_ERXFCON_U, 1, &aux); + } + else + { // Enable reception of multicast packets + unbanked_register_operation(dpd, + ENC424J600_BITFIELDSETU, ENC424J600_ERXFCON_U, 1, &aux); + } + simple_operation(dpd, ENC424J600_ENABLERX); + + return 0; + + default: +#if ENC424J600_DEBUG & 1 + diag_printf("Unsupported key %lx for enc424j600_spi_control().\n", key); +#endif + return -1; + } +} + +// Continue transfer from chip's buffer where the enc424j600_spi_RXevent() stopped. +// Take one packet. +static void +enc424j600_spi_recv(struct eth_drv_sc *sc, + struct eth_drv_sg *sg_list, + int chunks) +{ + int chunk; + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint16 rbuf_tail; + cyg_uint8 * aux_p; + cyg_uint8 aux[2]; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_recv()...\n"); +#endif + + for(chunk = 0; chunk < chunks; chunk++) + { + read_from_rx_buffer(dpd, (*sg_list).len, (cyg_uint8 *)(*sg_list).buf); + sg_list++; + } + aux_p = (cyg_uint8 *) & dpd->NextPacketPointer; + rbuf_tail = *aux_p + *(aux_p+1) * (cyg_uint16)256 ; + rbuf_tail -= 2; + if (rbuf_tail == ENC424J600_RXBUF_START) + { // Wrap over + rbuf_tail = 0x5ffe; + } + aux[0] = rbuf_tail & 0xff; + aux[1] = (rbuf_tail >> 8) & 0xff; +#if ENC424J600_DEBUG & 8 + diag_printf("Setting receive buffer tail, ERXTAIL 0x%02x%02x.\n", + aux[1], aux[0]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ERXTAIL_U, 2, &aux[0]); + + simple_operation(dpd, ENC424J600_SETPKTDEC); // Decrement packet count. If zeroed, + // ENC424J600_PKTIF interrupt flag will be reset. + + return; +} + +// This routine is called to see if it is possible to send another packet. +// It will return non-zero if a transmit is possible, zero otherwise. +static int +enc424j600_spi_can_send(struct eth_drv_sc *sc) +{ + if (((enc424j600_priv_data_t *)sc->driver_private)->txbusy) + { + return 0; + } + else + { + return 1; + } +} + +// This routine is called to send data to the hardware. +static void +enc424j600_spi_send(struct eth_drv_sc *sc, + struct eth_drv_sg *sg_list, + int sg_len, + int total, + unsigned long key) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + int block; + cyg_uint8 aux[2]; + cyg_uint8 opcode = ENC424J600_WGPDATA; + + if (0 >= sg_len) return; + + // Check if there is enough room in transmit buffer + if (total > ENC424J600_RXBUF_START - ENC424J600_TXBUF_START) + { + sc->funs->eth_drv->tx_done(sc, (CYG_ADDRESS)key, 0); +#if ENC424J600_DEBUG & 1 + diag_printf("enc424j600_spi_send(): Packet to send is too large (%d bytes).\n", + total); +#endif + return; + } + + dpd->txbusy = true; + dpd->txkey = key; + + aux[0] = ENC424J600_TXBUF_START & 0xff; + aux[1] = (((cyg_uint8)ENC424J600_TXBUF_START) >> 8) & 0xff; + set_pointer(dpd, ENC424J600_GPBWRPT, (aux[1] << 8) + aux[0]); + +#if ENC424J600_DEBUG & 8 + diag_printf("Starting SPI transaction to send a packet.\n"); +#endif + cyg_spi_transaction_begin(dpd->spi_service_device); + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + 1, + & opcode, + NULL, + ENC424J600_DONT_DROP_CS); + + for (block = 0; block < sg_len; block++) + { + cyg_spi_transaction_transfer(dpd->spi_service_device, + SPI_POLLING, + sg_list->len, + (cyg_uint8 *)sg_list->buf, + NULL, + (block == sg_len) ? ENC424J600_DO_DROP_CS : ENC424J600_DONT_DROP_CS ); + sg_list++; + } + cyg_spi_transaction_end(dpd->spi_service_device); + + aux[0] = total & 0xff; + aux[1] = (total >> 8 ) & 0xff; +#if ENC424J600_DEBUG & 8 + diag_printf("Writing ETXLEN 0x%02x%02x.\n", aux[1], aux[0]); +#endif + unbanked_register_operation(dpd, ENC424J600_WRITECRU, ENC424J600_ETXLEN_U, 2, &aux[0]); + +#if ENC424J600_DEBUG & 8 + diag_printf("Setting request to send.\n"); +#endif + simple_operation(dpd, ENC424J600_SETTXRTS); + + sc->funs->eth_drv->tx_done(sc, (CYG_ADDRESS)key, 0); + + return; +} + +static void +enc424j600_spi_poll(struct eth_drv_sc *sc) +{ + enc424j600_priv_data_t *dpd = (enc424j600_priv_data_t *)sc->driver_private; + cyg_uint8 aux[2]; + cyg_uint16 iflags; + +#if ENC424J600_DEBUG & 8 + diag_printf("enc424j600_spi_poll(%x)...\n", (cyg_uint32)sc); +#endif + +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + // Globally disable interrupt source in Ethernet chip + simple_operation(dpd, ENC424J600_CLREIE); +#endif + do + { + // Figure out what caused the interrupt + banked_register_operation(dpd, ENC424J600_READCR, ENC424J600_EIRL, 2, &aux[0]); + aux[1] &= ENC424J600_LINKIF; + aux[0] &= (ENC424J600_PKTIF | ENC424J600_TXIF | ENC424J600_TXABTIF | ENC424J600_RXABTIF | ENC424J600_PCFULIF); + iflags = aux[0] + (cyg_uint16)256 * aux[1]; + if (0 != (aux[0] & ENC424J600_PKTIF)) + { // Packets are pending in chip's receive buffer + enc424j600_spi_RXevent(sc); + } + if ( 0 != (aux[1] & ENC424J600_LINKIF)) + { // Link status changed + enc424j600_spi_LINKevent(sc); + } + if (0 != (aux[0] & ENC424J600_TXIF)) + { // Packet has been sent + enc424j600_spi_TXevent(sc); + } + if (0 != (aux[0] & ENC424J600_TXABTIF)) + { // Packet send has been aborted + enc424j600_spi_TXABTevent(sc); + } + if ( 0 != (aux[0] & ENC424J600_PCFULIF)) + { // Packet counter overflow + enc424j600_spi_PCFULevent(sc); + } + if ( 0 != (aux[0] & ENC424J600_RXABTIF)) + { // Incoming packet rejected due to buffer or counter overflow + enc424j600_spi_RXABTevent(sc); + } + } while(0 != iflags); + +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + cyg_drv_interrupt_unmask(CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR); + + // Globally enable interrupt source in Ethernet chip + simple_operation(dpd, ENC424J600_SETEIE); +#endif + + return; +} + +static int +enc424j600_spi_int_vector(struct eth_drv_sc *sc) +{ +#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED + return CYGNUM_DEVS_ETH_ENC424J600_INTERRUPT_VECTOR; +#else + return 0; +#endif +} + +// - End of higher level interface ----------------------------- + +// End of enc424j600_spi.c
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/src/enc424j600_spi.h @@ -0,0 +1,344 @@ +#ifndef CYGONCE_DEVS_ETH_ENC424J600_SPI_H_ +#define CYGONCE_DEVS_ETH_ENC424J600_SPI_H_ +//========================================================================== +// +// enc424j600_spi.h +// +// Microchip enc424j600 Ethernet chip +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Ilija Stanislevik +// Contributors: +// Date: 2010-11-23 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// Addresses of enc424j600 registers, banked access +// Banks 0, 1, 2 & 3 +#define ENC424J600_EUDASTL (0X16) +#define ENC424J600_EUDASTH (0X17) +#define ENC424J600_EUDANDL (0X18) +#define ENC424J600_EUDANDH (0X19) +#define ENC424J600_ESTATL (0x1a) +#define ENC424J600_ESTATH (0x1b) +#define ENC424J600_EIRL (0x1c) +#define ENC424J600_EIRH (0x1d) +#define ENC424J600_ECON1L (0x1e) +#define ENC424J600_ECON1H (0x1f) + +// Only bank 0 +#define ENC424J600_ETXSTL (0x00) +#define ENC424J600_ETXSTH (0x01) +#define ENC424J600_ETXLENL (0x02) +#define ENC424J600_ETXLENH (0x03) +#define ENC424J600_ERXSTL (0x04) +#define ENC424J600_ERXSTH (0x05) +#define ENC424J600_ERXTAILL (0x06) +#define ENC424J600_ERXTAILH (0x07) +#define ENC424J600_ERXHEADL (0x08) +#define ENC424J600_ERXHEADH (0x09) + +// Only bank 1 +#define ENC424J600_ERXFCONL (0x14) +#define ENC424J600_ERXFCONH (0x15) + +// Only bank 2 +#define ENC424J600_MACON1L (0x00) +#define ENC424J600_MACON1H (0x01) +#define ENC424J600_MACON2L (0x02) +#define ENC424J600_MACON2H (0x03) +#define ENC424J600_MABBIPGL (0x04) +#define ENC424J600_MABBIPGH (0x05) +#define ENC424J600_MAMXFLL (0x0a) +#define ENC424J600_MAMXFLH (0x0b) +#define ENC424J600_MICMDL (0x12) +#define ENC424J600_MICMDH (0x13) +#define ENC424J600_MIREGADRL (0x14) +#define ENC424J600_MIREGADRH (0x15) +#define ENC424J600_EIRL (0x1c) +#define ENC424J600_EIRH (0x1d) +#define ENC424J600_ECON1L (0x1e) +#define ENC424J600_ECON1H (0x1f) + +// Only bank 3 +#define ENC424J600_MAADR3L (0x00) +#define ENC424J600_MIWRL (0x06) +#define ENC424J600_MIWRH (0x07) +#define ENC424J600_MIRDL (0x08) +#define ENC424J600_MIRDH (0x09) +#define ENC424J600_MISTATL (0x0a) +#define ENC424J600_MISTATH (0x0b) +#define ENC424J600_EPAUSL (0x0c) +#define ENC424J600_EPAUSH (0x0d) +#define ENC424J600_ECON2L (0x0e) +#define ENC424J600_ECON2H (0x0f) +#define ENC424J600_ERXWML (0x10) +#define ENC424J600_ERXWMH (0x11) +#define ENC424J600_EIEL (0x12) +#define ENC424J600_EIEH (0x13) + +// Addresses of enc424j600 registers, unbanked access +// Bank 0 +#define ENC424J600_BANK0_Of (0) +#define ENC424J600_ETXST_U (ENC424J600_ETXSTL + ENC424J600_BANK0_Of) +#define ENC424J600_ETXLEN_U (ENC424J600_ETXLENL + ENC424J600_BANK0_Of) +#define ENC424J600_EUDAST_U (ENC424J600_EUDASTL + ENC424J600_BANK0_Of) +#define ENC424J600_EUDAND_U (ENC424J600_EUDANDL + ENC424J600_BANK0_Of) +#define ENC424J600_ESTAT_U (ENC424J600_ESTATL + ENC424J600_BANK0_Of) +#define ENC424J600_ERXST_U (ENC424J600_ERXSTL + ENC424J600_BANK0_Of) +#define ENC424J600_ERXTAIL_U (ENC424J600_ERXTAILL + ENC424J600_BANK0_Of) +#define ENC424J600_ERXHEAD_U (ENC424J600_ERXHEADL + ENC424J600_BANK0_Of) + +// Bank 1 +#define ENC424J600_BANK1_Of (0x20) +#define ENC424J600_ERXFCON_U ( ENC424J600_ERXFCONL + ENC424J600_BANK1_Of) + +// Bank 2 +#define ENC424J600_BANK2_Of (0x40) +#define ENC424J600_MACON1_U (ENC424J600_MACON1L + ENC424J600_BANK2_Of) +#define ENC424J600_MACON2_U (ENC424J600_MACON2L + ENC424J600_BANK2_Of) +#define ENC424J600_MABBIPG_U (ENC424J600_MABBIPGL + ENC424J600_BANK2_Of) +#define ENC424J600_MAMXFL_U (ENC424J600_MAMXFLL + ENC424J600_BANK2_Of) +#define ENC424J600_MICMD_U (ENC424J600_MICMDL + ENC424J600_BANK2_Of) +#define ENC424J600_MIREGADR_U (ENC424J600_MIREGADRL + ENC424J600_BANK2_Of) +#define ENC424J600_EIR_U (ENC424J600_EIRL + ENC424J600_BANK2_Of) +#define ENC424J600_ECON1_U (ENC424J600_ECON1L + ENC424J600_BANK2_Of) + +// Bank 3 +#define ENC424J600_BANK3_Of (0x60) +#define ENC424J600_MAADR3_U (ENC424J600_MAADR3L + ENC424J600_BANK3_Of) +#define ENC424J600_MIWR_U (ENC424J600_MIWRL + ENC424J600_BANK3_Of) +#define ENC424J600_MIRD_U (ENC424J600_MIRDL + ENC424J600_BANK3_Of) +#define ENC424J600_MISTAT_U (ENC424J600_MISTATL + ENC424J600_BANK3_Of) +#define ENC424J600_EPAUS_U (ENC424J600_EPAUSL + ENC424J600_BANK3_Of) +#define ENC424J600_ECON2_U (ENC424J600_ECON2L + ENC424J600_BANK3_Of) +#define ENC424J600_ERXWM_U (ENC424J600_ERXWML + ENC424J600_BANK3_Of) +#define ENC424J600_EIE_U (ENC424J600_EIEL + ENC424J600_BANK3_Of) + +// Addresses of enc424j600 PHY registers, special access + +enum enc424j600_pr_address_e +{ + ENC424J600_PHCON1 = 0, + ENC424J600_PHSTAT1 = 1, + ENC424J600_PHANA = 4, + ENC424J600_PHANLPA = 5, + ENC424J600_PHANE = 6, + ENC424J600_PHCON2 = 0x11, + ENC424J600_PHSTST2 = 0x1b, + ENC424J600_PHSTAT3 = 0x1f +}; + +//---------- end of register addresses -------------------------------------------------- + +//---------- definitions of bits in control registers ----------------------------------- + +// Interrupt flags (EIR register) higher byte +#define ENC424J600_MODEXIF (0x40) +#define ENC424J600_HASHIF (0x20) +#define ENC424J600_AESIF (0x10) +#define ENC424J600_LINKIF (0x08) // PHY status has changed + +// Interrupt flags (EIR register) lower byte +#define ENC424J600_PKTIF (0x40) // RX packet(s) pending +#define ENC424J600_DMAIF (0x20) +#define ENC424J600_TXIF (0x08) // Transmit done +#define ENC424J600_TXABTIF (0x04) // Packet transmission aborted due to an error +#define ENC424J600_RXABTIF (0x02) // RX packet rejected due to lack of space / to many + // pending packets +#define ENC424J600_PCFULIF (0x01) // To many pending packets + +// Interrupt enable bits (EIE register) higher byte +#define ENC424J600_INTIE (0x80) // Global interrupt enable +#define ENC424J600_MODEXIE (0x40) +#define ENC424J600_HASHIE (0x20) +#define ENC424J600_AESIE (0x10) +#define ENC424J600_LINKIE (0x08) // PHY status has changed + +// Interrupt enable bits (EIE register) lower byte +#define ENC424J600_PKTIE (0x40) // RX packet(s) pending +#define ENC424J600_DMAIE (0x20) +#define ENC424J600_TXIE (0x08) // Transmit done +#define ENC424J600_TXABTIE (0x04) // Packet transmission aborted due to an error +#define ENC424J600_RXABTIE (0x02) // RX packet rejected due to lack of space / too many + // pending packets +#define ENC424J600_PCFULIE (0x01) // Too many pending packets + +// MICMD +#define ENC424J600_MIIRD (0x01) +#define ENC424J600_MIISCAN (0x02) + +// MISTAT +#define ENC424J600_MISTAT_BUSY (0x01) +#define ENC424J600_MISTAT_SCAN (0x02) +#define ENC424J600_MISTAT_NVALID (0x04) + +//ESTAT upper byte +#define ENC424J600_PHYLNK (0x01) +#define ENC424J600_PHYDPX (0x04) +#define ENC424J600_CLKRDY (0x10) +#define ENC424J600_RXBUSY (0x20) +#define ENC424J600_FCIDLE (0x40) +#define ENC424J600_INT (0x80) + +// ECON1 lower byte +#define ENC424J600_TXRTS (0x02) + +//ECON2 upper byte +#define ENC424J600_STRCH (0x40) +#define ENC424J600_ETHEN (0x80) + +// ECON2 lower byte +#define ENC424J600_ETHRST (0x10) +#define ENC424J600_AUTOFC (0x80) + +// MACON2 upper byte +#define ENC424J600_NOBKOFF (0x10) +#define ENC424J600_BPEN (0x20) +#define ENC424J600_DEFER (0x40) + +// MACON2 lower byte +#define ENC424J600_FULDPX (0x01) +#define ENC424J600_HFRMEN (0x04) +#define ENC424J600_PHDREN (0x08) +#define ENC424J600_TXCRCEN (0x10) + +// ERXFCON lower byte +#define ENC424J600_BCEN (0x01) // accept broadcast packets +#define ENC424J600_MCEN (0x02) // accept multicast packets +#define ENC424J600_UCEN (0x08) // accept packets destined for local MAC address +#define ENC424J600_RUNTEN (0x10) // discard runt packets +#define ENC424J600_CRCEN (0x40) // discard packets with invalid CRC + +// PHCON1 upper byte +#define ENC424J600_PFULDPX (0x01) // full duplex +#define ENC424J600_RENEG (0x02) // restart negotiation +#define ENC424J600_ANEN (0x10) // auto negotiation enable +#define ENC424J600_SPD100 (0x20) // 100Mb/s + +// PHANA upper byte +#define ENC424J600_ADPAUS_BOTH (0x0c) // both symmetric and asymmetric flow control +#define ENC424J600_ADPAUS_ASYM (0x08) // asymmetric flow control towards link partner +#define ENC424J600_ADPAUS_SYMM (0x04) // symmetric flow control +#define ENC424J600_ADPAUS_NONE (0x00) // no flow control +#define ENC424J600_AD100FD (0x01) + +// PHANA lower byte +#define ENC424J600_AD100 (0x80) +#define ENC424J600_AD10FD (0x40) +#define ENC424J600_AD10 (0x20) +#define ENC424J600_IEEE802_3STD (0x01) // IEEE Standard Selector Field bits + +//---------- end of definitions of bits in control registers ------------------------- + +// enc424j600 SPI instructions opcodes + +// Single byte instructions +enum enc424j600_spi_opcode_1_e +{ + ENC424J600_SETETHRST = 0xca, // Issue System Reset + ENC424J600_SETPKTDEC = 0xcc, // Decrement packet counter + ENC424J600_SETTXRTS = 0xd4, // Send a packet + ENC424J600_FCDISABLE = 0xe0, // Disable flow control + ENC424J600_FCSINGLE = 0xe2, // Send a single pause frame + ENC424J600_FCMULTIPLE = 0xe4, // Keep sending pause frames + ENC424J600_FCCLEAR = 0xe6, // Send the final pause frame + ENC424J600_ENABLERX = 0xe8, // Enable receiver + ENC424J600_DISABLERX = 0xea, // Disable receiver + ENC424J600_SETEIE = 0xec, // Enable interrupts + ENC424J600_CLREIE = 0xee, // Disable interrupts +}; + +// Pointer definitions for 3-byte instructions +enum enc424j600_spi_bufferp_e +{ + ENC424J600_GPBRDPT = 0x60, // General purpose buffer read pointer + ENC424J600_RXRDPT = 0x64, // Receive buffer read pointer + ENC424J600_UDARDPT = 0x68, // User-defined area read pointer + ENC424J600_GPBWRPT = 0x6c, // General purpose buffer write pointer + ENC424J600_RXWRPT = 0x70, // Receive buffer write pointer + ENC424J600_UDAWRPT = 0x74, // User-defined area write pointer + +}; + +enum enc424j600_spi_opcode_3_e +{ + ENC424J600_WRITE3 = 0x00, // Pointer writing + ENC424J600_READ3 = 0x02 // Pointer reading + +}; + +// N-byte unbanked SFR instructions +enum enc424j600_spi_opcode_u_e +{ + ENC424J600_READCRU = 0x20, // Read Control Register, Unbanked + ENC424J600_WRITECRU = 0x22, // Write Control Register, Unbanked + ENC424J600_BITFIELDSETU = 0x24, // Bit Field Set, Unbanked + ENC424J600_BITFIELDCLEARU = 0x26 // Bit Field Clear, Unbanked +}; + +// N-byte banked SFR instructions +enum enc424j600_spi_opcode_e +{ + ENC424J600_READCR = 0x00, // Read Control Register, Banked + ENC424J600_WRITECR = 0x40, // Write Control Register, Banked + ENC424J600_BITFIELDSET = 0x80, // Bit Field Set, Banked + ENC424J600_BITFIELDCLEAR = 0xa0 // Bit Field Clear, Banked +}; + +// N-byte SRAM instructions +enum enc424j600_spi_opcode_sram_e +{ + ENC424J600_RGPDATA = 0x28, // Read from General Purpose buffer + ENC424J600_WGPDATA = 0x2a, // Write to General Purpose buffer + ENC424J600_RRXDATA = 0x2c, // Read from Rx buffer + ENC424J600_WRXDATA = 0x2e // Write to Rx buffer +}; + +#define ENC424J600_ETH_INTERRUPT_LEVEL_HIGH (true) +#define ENC424J600_ETH_INTERRUPT_LEVEL_LOW (false) +#define ENC424J600_ETH_INTERRUPT_EDGE_RISING (true) +#define ENC424J600_ETH_INTERRUPT_EDGE_FALLING (false) + +//#define ETH_DRV_GET_MAC_ADDRESS (0x1100) + +#endif /* ifndef CYGONCE_DEVS_ETH_ENC424J600_SPI_H_ */ +// End of enc424j600_spi.h
new file mode 100644 --- /dev/null +++ b/packages/devs/eth/microchip/enc424j600/current/tests/netconn_test_server.c @@ -0,0 +1,354 @@ +//========================================================================== +// +// netconn_test_server.c +// +// Utility to test enc424j600 Ethernet driver +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2010 Free Software Foundation, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Ilija Stanislevik +// Contributors: +// Date: 2010-08-04 +// Purpose: +// Description: Utility to test enc424j600 (or any other) Ethernet driver +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/testcase.h> // Test macros +#include <cyg/infra/cyg_ass.h> // Assertion macros +#include <cyg/infra/diag.h> // Diagnostic output +#include <cyg/hal/hal_io.h> +#include <cyg/hal/hal_if.h> + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <cyg/error/codes.h> +#include <cyg/io/io.h> + +#include <cyg/io/spi.h> + +#include <cyg/io/eth/netdev.h> +#include <network.h> + +#include <lwip/netif.h> +#include <cyg/io/eth/eth_drv.h> + +#define PORTNUM 2000 + +// Threads' data structures +typedef struct thread_s +{ + cyg_uint8 stack [CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_thread data; + cyg_handle_t handle; +} thread_s_t; + +typedef struct +{ + struct netconn * connection_p; + cyg_uint32 id; +} server_argument_t; + +//#define INIT_PRIORITY 5 +#define INIT_PRIORITY 12 +#define LISTENER_PRIORITY INIT_PRIORITY+1 +#define SERVER_PRIORITY LISTENER_PRIORITY-1 + +// Init thread + +thread_s_t init_thread; + +void run_init(void) +{ + diag_printf("i: Initializing network stack...\n"); + cyg_lwip_sequential_init(); +} + +// Server thread + +void server(cyg_addrword_t arg_p) +{ + struct netconn *newconn = NULL; + struct netbuf *income_buffer = NULL; + struct ip_addr from_addr; + struct ip_addr my_addr; + cyg_uint16 from_port = 0; + cyg_uint16 my_port = 0; + cyg_uint8 *addr_byte_p = NULL; + cyg_uint16 total_len; + cyg_uint8 * data_buf = NULL; + cyg_int32 retval; + cyg_uint32 id; + cyg_uint32 csum; + + newconn = ((server_argument_t *)arg_p) -> connection_p; + id = ((server_argument_t *)arg_p) -> id; + + retval = netconn_addr(newconn, &my_addr, &my_port); + if(0 != retval) + { + diag_printf("%u: netconn_addr() return %d. Exiting.\n", id, retval); + netconn_delete(newconn); + cyg_thread_exit(); + } + + retval = netconn_peer(newconn, &from_addr, &from_port); + if(0 != retval) + { + diag_printf("%u: netconn_peer() return %d. Exiting.\n", id, retval); + netconn_delete(newconn); + cyg_thread_exit(); + } + + addr_byte_p = (cyg_uint8 *) & from_addr.addr; + diag_printf("%u: Connected to remote peer %u.%u.%u.%u port %u, at my port %u\n", id, + *addr_byte_p, *(addr_byte_p+1), *(addr_byte_p+2), *(addr_byte_p+3), + from_port, my_port); + diag_printf("newconn %x\n", (unsigned)newconn); + + csum =0; + + while((income_buffer = netconn_recv(newconn)) != NULL) + { + total_len = netbuf_len(income_buffer); + if(0 != total_len) + { + data_buf = malloc(total_len); + if(NULL == data_buf) + { + diag_printf("%u: Can not allocate %u bytes. Exiting.\n", id, total_len); + netconn_delete(newconn); + cyg_thread_exit(); + } + + cyg_uint8 *local_data_p = data_buf; + do + { + cyg_uint8 *in_data_p; + cyg_uint16 len; + int i; + + netbuf_data(income_buffer, (void **)&in_data_p, &len); + for(i=0; i<len; i++) + { + csum += *local_data_p++ = *in_data_p++; + } + } while(netbuf_next(income_buffer) >= 0); + diag_printf("\nReceived %u bytes\n", total_len); + + free(data_buf); + } + netbuf_delete(income_buffer); + } + + diag_printf("Checksum %08x\n", csum); + + retval = netconn_close(newconn); + if( 0 != retval) + { + diag_printf("%u: netconn_close(%x) return %d.\n", id, (unsigned)newconn, retval); + } + + retval = netconn_delete(newconn); + if( 0 != retval) + { + diag_printf("%u: netconn_delete(%x) return %d. Exiting.\n", id, (unsigned)newconn, retval); + cyg_thread_exit(); + } + + diag_printf("%u: TCP connection closed.\n", id); + +} + +// Listener thread + +thread_s_t listener_thread; + +void listener(void) +{ + struct netconn *conn = NULL; + struct netconn *newconn = NULL; + struct netif *mynetif = NULL; + server_argument_t server_launch_args_s; + cyg_uint32 server_id = 0; + thread_s_t * new_thread_data = NULL; + char server_name[20]; + cyg_int32 retval; + + // This large malloc is just for testing +// new_thread_data = malloc(150 * sizeof(thread_s_t)); + + cyg_thread_delay(500); // Sleep over the initialization of network + + mynetif = netif_find("et0"); + diag_printf("l: netif_find(et0) return %x\n", (unsigned int)mynetif); + retval = netif_is_up(mynetif); + diag_printf("l: netif_is_up() return %u\n", retval); + + diag_printf("l: Creating TCP connection...\n"); + conn = netconn_new(NETCONN_TCP); + if (NULL == conn) + { + diag_printf("l: netconn_new() return NULL. Exiting.\n"); + cyg_thread_exit(); + } + diag_printf("l: Binding to port %d...\n", PORTNUM); + retval = netconn_bind(conn, NULL, PORTNUM); + if (0 != retval) + { + diag_printf("l: netconn_bind() return %d. Exiting.\n", retval); + cyg_thread_exit(); + } + + diag_printf("l: Setting up for listening...\n"); + retval = netconn_listen(conn); + if( 0!= retval) + { + diag_printf("l: netconn_listen() return %d. Exiting.\n", retval); + cyg_thread_exit(); + } + + do + { + diag_printf("l: Waiting for a new connection...\n"); + newconn = netconn_accept(conn); + if (NULL == newconn) + { + diag_printf("l: netconn_accept() return NULL. Exiting.\n"); + cyg_thread_exit(); + } + + // It's time to start a server thread + + new_thread_data = malloc(sizeof(thread_s_t)); + if(NULL == new_thread_data) + { + diag_printf("l: Can't allocate memory for another server.\n"); + netconn_close(newconn); + netconn_delete(newconn); + continue; + } + /* + * Thread data is never freed after the thread exit. + * After a number of connections made, it will be impossible to + * allocate memory for additional servers. + */ + + server_launch_args_s.connection_p = newconn; + server_launch_args_s.id = ++server_id; + sprintf(server_name, "s%u", server_id); + + diag_printf("l: Starting %s...\n", server_name); + + cyg_thread_create( + SERVER_PRIORITY, + (cyg_thread_entry_t*) server, + (cyg_addrword_t)&server_launch_args_s, + server_name, + &(new_thread_data->stack[0]), + CYGNUM_HAL_STACK_SIZE_TYPICAL, + &(new_thread_data->handle), + &(new_thread_data->data) + ); + cyg_thread_resume(new_thread_data->handle); + + } while(true); +} + +//--------------------------------------------------------------------------- +void show_threads(void) +{ + cyg_handle_t thread = 0; + cyg_uint16 id = 0; + + while( cyg_thread_get_next( &thread, &id ) ) + { + cyg_thread_info info; + + if( !cyg_thread_get_info( thread, id, &info ) ) + break; + + diag_printf("ID: %04x name: %10s pri: %d\n", + info.id, info.name?info.name:"----", info.set_pri ); + diag_printf(" stack_base %x stack_size %d\n", info.stack_base, info.stack_size); + } +} + +void cyg_user_start(void) +{ + diag_printf("%u: Starting initializer...\n", __LINE__); + cyg_thread_create( + INIT_PRIORITY, + (cyg_thread_entry_t*) run_init, + 0, + "initializer", + &init_thread.stack[0], + CYGNUM_HAL_STACK_SIZE_TYPICAL, + &init_thread.handle, + &init_thread.data + ); + cyg_thread_resume(init_thread.handle); + + diag_printf("%u: Starting listener...\n", __LINE__); + + cyg_thread_create( + LISTENER_PRIORITY, + (cyg_thread_entry_t*) listener, + 0, + "listener", + &listener_thread.stack[0], + CYGNUM_HAL_STACK_SIZE_TYPICAL, + &listener_thread.handle, + &listener_thread.data + ); + cyg_thread_resume(listener_thread.handle); + + show_threads(); + + diag_printf("%u: Starting scheduler...\n", __LINE__); + cyg_scheduler_start(); +} + +//============================================================================= +
