From fa48374b87abb1bc84ec881633f0dad72be38066 Mon Sep 17 00:00:00 2001 From: richardbarry Date: Sat, 27 May 2006 16:53:42 +0000 Subject: [PATCH] git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@10 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- .../tern_code/i2chip_hw.c | 239 +++ Demo/WizNET_DEMO_TERN_186/tern_code/socket.c | 1870 +++++++++++++++++ 2 files changed, 2109 insertions(+) create mode 100644 Demo/WizNET_DEMO_TERN_186/tern_code/i2chip_hw.c create mode 100644 Demo/WizNET_DEMO_TERN_186/tern_code/socket.c diff --git a/Demo/WizNET_DEMO_TERN_186/tern_code/i2chip_hw.c b/Demo/WizNET_DEMO_TERN_186/tern_code/i2chip_hw.c new file mode 100644 index 000000000..eb513ef1b --- /dev/null +++ b/Demo/WizNET_DEMO_TERN_186/tern_code/i2chip_hw.c @@ -0,0 +1,239 @@ +/* +******************************************************************************** +* TERN, Inc. +* (c) Copyright 2005, http://www.tern.com +* +* - Created to support i2chip module on a variety of TERN hardware platforms. +******************************************************************************** +*/ + +#include +#include "i2chip_hw.h" + +#ifdef I2CHIP_MMC +#include "mmc.h" +#endif + +void i2chip_init(void) +{ + +#ifdef TERN_586 +/* + poke(MMCR,_BOOTCSCTL_,peek(MMCR,_BOOTCSCTL_)&0xffc9); // ROM 1 wait + poke(MMCR,_ROMCS2CTL_,peek(MMCR,_ROMCS2CTL_)&0xffc8); // SRAM 0 wait + + pokeb(MMCR, _GPCSRT_, 24); // set the GP CS recovery time, 12 works + pokeb(MMCR, _GPCSPW_, 128); // set the GP CS width, 64 works + pokeb(MMCR, _GPCSOFF_, 16); // set the GP CS offset, 8 works + pokeb(MMCR, _GPRDW_, 80); // set the GP RD pulse width, 50 works + pokeb(MMCR, _GPRDOFF_, 30); // set the GP RD offset, 15 works + pokeb(MMCR, _GPWRW_, 80); // set the GP WR pulse width, 50 + pokeb(MMCR, _GPWROFF_, 30); // set the GP WR offset, 15 +*/ + +#ifdef TERN_5E + pokeb(MMCR, _GPCSDW_, peekb(MMCR, _GPCSDW_)&0xf7); // set /CS3-/CSM Data Width=8 + pokeb(MMCR, _CSPFS_, peekb(MMCR, _CSPFS_)|0x08); // set the GP CS3 PIN Function + poke(MMCR, _PAR15_, 0x2000); // set CS3 I/O region + poke(MMCR, _PAR15_+2, 0x2dff); // set CS3 I/O region, 512 bytes + + pokeb(MMCR, _GPCSDW_, peekb(MMCR, _GPCSDW_)&0x7f); // CS7=J4.3 Data Width=8, /CSI +// pokeb(MMCR, _GPCSDW_, peekb(MMCR, _GPCSDW_)|0x80); // CS7=J4.3 Data Width=16 + pokeb(MMCR, _CSPFS_, peekb(MMCR, _CSPFS_)|0x80); // set the GP CS7 PIN Function + poke(MMCR, _PAR7_, 0x4000); // set CS7 I/O region + poke(MMCR, _PAR7_+2, 0x3dff); // set CS7 I/O region, 512 bytes +#else + // If it's not 5E, then it must be 5P... in which case, we use PCS0 and + // PCS1 as the chip-selects. + pokeb(MMCR, _GPCSDW_, peekb(MMCR, _GPCSDW_)&0xfe); // CS0 Data Width=8 + poke(MMCR, _PIOPFS31_16_, peek(MMCR,_PIOPFS31_16_)|0x0800); // P27=/CS0 + poke(MMCR, _PAR13_, 0x1800); // CS0 I/O region + poke(MMCR, _PAR13_+2, 0x21ff); // CS0 I/O RW, 512 bytes, start 0x1800 +#endif + +a HLPRsetvect(0x47, (void far *) spu_m_isr); + HLPRsetvect(0x4f, (void far *) spu_1_isr); + HLPRsetvect(0x57, (void far *) spu_2_isr); +#endif // 186, or RE + +#ifdef TERN_186 + pio_init(18, 0); // P18=CTS1 for /PCS2 + +#ifdef TERN_16_BIT + outport(0xfff2, 2); // AUXCON, MCS, Bus 16-bit +#endif + +#ifdef I2CHIP_MCS_DIRECT + outport(0xffa0,0xc0bf); // UMCS, 256K ROM, disable AD15-0 + outport(0xfff0,inport(0xfff0)|0x4000 ); // SYSCON, MCS0 0x80000-0xbffff + outport(0xffa8,0xa0bf ); // MPCS, MCS0=P14, 64KB, PCS I/O, + outport(0xffa6,0x81ff); // MMCS, base 0x80000, + outport(0xffa2,0x7fbf); // 512K RAM, + outport(0xffa4,0x007d); // PACS, base 0, + +#else + + outport( 0xffa0,0xc0bf); // UMCS, 256K ROM, 3 wait, disable AD15-0 + outport( 0xfff0,inport(0xfff0)|0x4000 ); // SYSCON, MCS0 0x80000-0xbffff +// outport( 0xffa8,0xa0bc ); // MPCS, MCS0=P14, 64KB, PCS I/O 0 wait +// outport( 0xffa8,0xa0bd ); // MPCS, MCS0=P14, 64KB, PCS I/O 1 wait + outport( 0xffa8,0xa0bf ); // MPCS, MCS0=P14, 64KB, PCS I/O 1 wait +#endif // I2CHIP_MCS_DIRECT + +#ifndef TERN_RE // 80 MHz R- boards can't tolerate zero wait state. + outport( 0xffa6,0x81ff ); // MMCS, base 0x80000 + outport(0xffa2,0x7fbe); // 512K RAM, 0 wait states + outport(0xffa4,0x007d); // PACS, base 0, 0 wait +#endif + pio_init(14,0); // Enable /MCS0 + +#endif // TERN_186 + + +#ifdef I2CHIP_WINDOW +#ifdef I2CHIP_SHIFTED_ADDRESS + pio_init(12, 2); // Configure P12 as A7, an output we'll be using. + pio_wr(12, 0); // Set A7 low, initially. +#endif + WINDOW_RESTORE_BASE; // Equivalent to calling mmc_window(7, 0); +#endif +} + +#ifdef I2CHIP_WINDOW + +void i2chip_set_page(u_int page) +{ + u_int new_page = page; + +#ifdef I2CHIP_SHIFTED_ADDRESS + if (page & 0x01) // ... we're checking the right-most bit in the page. + outport(0xff74, inport(0xff74) | 0x1000 ); // Using P12 as A7... + else + outport(0xff74, inport(0xff74) & 0xefff ); + + new_page = page >> 1; +#endif + +#ifdef I2CHIP_MMC + mmc_window(7, new_page); // See mmc.c +#endif +#ifdef I2CHIP_P51 + p51_window(new_page); +#endif +} + +static u_int s_addr = 0xffff; +u_char far* i2chip_mkptr(u_int addr) +{ + if ((s_addr & 0xff00) == (addr & 0xff00)) // No point... no point... + return MK_FP(WINDOW_BASE_SEGM, addr & 0xff); + + s_addr = addr ; + + // So the argument to this function is... what again? + // I think it should be the highest 16-bits... or, in other words, + // FP_SEG of a huge ptr. + // Ok, and the *return* value should be a UINT value for the new + // segment address to be used, if it's at all needed. TODO + I2CHIP_SET_PAGE(s_addr >> 8); // Portable version +// outportb(0x00, addr>>8); // quicker version + + return MK_FP(WINDOW_BASE_SEGM, addr & 0xff); +} + +void i2chip_set_window(u_int window_addr) +{ + s_addr = window_addr; + I2CHIP_SET_PAGE(s_addr >> 8); +} + +// Still inside #define I2CHIP_WINDOW ... + +u_int i2chip_get_window(void) +{ + return s_addr & 0xff00; +} + +void i2chip_push_window(u_int addr) +{ + I2CHIP_SET_PAGE(addr>>8); +} + +void i2chip_pop_window(void) +{ + I2CHIP_SET_PAGE(s_addr >> 8); +} + +#ifdef I2CHIP_WINDOW_IO +u_char io_read_value(u_char far* addr) +{ + // return value ... we assume the page is already set. So, instead, + // we just go ahead and output valeu. + return inportb(I2CHIP_BASE_SEG + (FP_OFF(addr) & 0xff)); +} + +void io_write_value(u_char far* addr, u_char value) +{ + // Get the last whatever bytes... and write value. + outportb(I2CHIP_BASE_SEG + (FP_OFF(addr) & 0xff), value); +} + +#endif // I2CHIP_WINDOW_IO + + +#ifdef I2CHIP_P51 +void p51_window(unsigned int page) +{ +asm xor ax, ax +asm mov ax, page +#ifdef I2CHIP_WINDOW_IO +asm mov dx, 1040h +asm out dx, al +#else +asm out 040h, al +#endif +// use J1.19=/CS6 +} +#endif // I2CHIP_P51 + +#endif // I2CHIP_WINDOW + +#ifdef TERN_586 +/* +// Function: spu_m_isr +// P22=Master PIC IR7, interrupt vector=0x47, /INTA +*/ +void interrupt far spu_m_isr(void) +{ +disable(); +// Issue the EOI to interrupt controller +outportb(_MPICOCW2_IO,0x67); // Specific EQI for master IR7 +enable(); +} + +/* +// Function: spu_1_isr +// P10=slave1 PIC IR7, Master IR2, interrupt vector=0x4f, /INTC +*/ +void interrupt far spu_1_isr(void) +{ +disable(); +// Issue the EOI to interrupt controller + outportb(_S1PICOCW2_IO,0x67); // Specific EOI for slave 1 IR7 + outportb(_MPICOCW2_IO,0x62); // Specific EQI for master IR2 +enable(); +} + +/* +// Function: spu_2_isr +// P20=Slave2 PIC IR7, Master IR5, interrupt vector=0x57, GPIRQ7=PIO16 GP timer1 +*/ +void interrupt far spu_2_isr(void) +{ +disable(); +// Issue the EOI to interrupt controller + outportb(_S2PICOCW2_IO,0x67); // Specific EOI for slave 1 IR7 + outportb(_MPICOCW2_IO,0x65); // Specific EQI for master IR5 +enable(); +} +#endif diff --git a/Demo/WizNET_DEMO_TERN_186/tern_code/socket.c b/Demo/WizNET_DEMO_TERN_186/tern_code/socket.c new file mode 100644 index 000000000..8cbd8ebb6 --- /dev/null +++ b/Demo/WizNET_DEMO_TERN_186/tern_code/socket.c @@ -0,0 +1,1870 @@ +/* +******************************************************************************** +* TERN, Inc. +* (c) Copyright 2005, http://www.tern.com +* +* MODIFIED BY RICHARD BARRY TO ADD SEMAPHORE FOR COMMUNICATION BETWEEN THE +* WIZnet ISR AND THE HTTP TASK. +* +* - Derived based on development version provided by Wiznet. +* +* Filename : socket.h +* Programmer(s): +* Created : 2002/06/20 +* Modified : +* 2002/09/27 : - Renaming +* INT_STATUS --> INT_REG +* STATUS(i) --> INT_STATUS(i) +* C_STATUS(i) --> SOCK_STATUS(i) +* 2003/11/06 : Ported for use with TERN controller. Note all byte access is at even addresses +* 2005/10/8 : Modified constants for easier initialization. +* +* Description : Header file of W3100A for TERN embedded controller +******************************************************************************** +*/ +/* +############################################################################### +File Include Section +############################################################################### +*/ +#include "i2chip_hw.h" +#include "socket.h" +#include "types.h" +#include +#include + +#include +#include +#include + + +/* +############################################################################### +Local Variable Declaration Section +############################################################################### +*/ +u_char I_STATUS[4]; // Store Interrupt Status according to channels +u_int Local_Port; // Designate Local Port +union un_l2cval SEQ_NUM; // Set initial sequence number + +u_long SMASK[MAX_SOCK_NUM]; // Variable to store MASK of Tx in each channel, + // on setting dynamic memory size. +u_long RMASK[MAX_SOCK_NUM]; // Variable to store MASK of Rx in each channel, + // on setting dynamic memory size. +int SSIZE[MAX_SOCK_NUM]; // Maximun Tx memory size by each channel +int RSIZE[MAX_SOCK_NUM]; // Maximun Rx memory size by each channel + +u_int SBUFBASEADDRESS[MAX_SOCK_NUM]; // Maximun Tx memory base address by each channel +u_int RBUFBASEADDRESS[MAX_SOCK_NUM]; // Maximun Rx memory base address by each channel + +/* +############################################################################### +Function Implementation Section +############################################################################### +*/ + +/* +******************************************************************************** +* Interrupt handling function of the W3100A +* +* Description : +* Stores the status information that each function waits for in the global variable I_STATUS +* for transfer. I_STATUS stores the interrupt status value for each channel. +* Arguments : None +* Returns : None +* Note : Internal Function +******************************************************************************** +*/ + +portBASE_TYPE prvProcessISR( void ) +{ +unsigned char status; +extern xSemaphoreHandle xTCPSemaphore; +portBASE_TYPE xSwitchRequired = pdFALSE; + +#ifdef I2CHIP_WINDOW +u_int current_window = i2chip_get_window(); +#endif + +status = READ_VALUE(INT_REG); + + +if (status) + { + xSwitchRequired = pdTRUE; + // channel 0 interrupt(sysinit, sockinit, established, closed, timeout, send_ok, recv_ok) + if (status & 0x01) + { + I_STATUS[0] = READ_VALUE(INT_STATUS(0)); + +// if (I_STATUS[0] & SESTABLISHED) +// ISR_ESTABLISHED(0); +// if (I_STATUS[0] & SCLOSED) +// ISR_CLOSED(0); + + WRITE_VALUE(INT_REG, 0x01); + } + + // channel 1 interrupt(sysinit, sockinit, established, closed, timeout, send_ok, recv_ok) + if (status & 0x02) + { + I_STATUS[1] = READ_VALUE(INT_STATUS(1)); + +// if (I_STATUS[1] & SESTABLISHED) +// ISR_ESTABLISHED(1); +// if (I_STATUS[1] & SCLOSED) +// ISR_CLOSED(1); + + WRITE_VALUE(INT_REG, 0x02); + } + + // channel 2 interrupt(sysinit, sockinit, established, closed, timeout, send_ok, recv_ok) + if (status & 0x04) + { + I_STATUS[2] = READ_VALUE(INT_STATUS(2)); + +// if (I_STATUS[2] & SESTABLISHED) +// ISR_ESTABLISHED(2); +// if (I_STATUS[2] & SCLOSED) +// ISR_CLOSED(2); + + WRITE_VALUE(INT_REG, 0x04); + } + + // channel 3 interrupt(sysinit, sockinit, established, closed, timeout, send_ok, recv_ok) + if (status & 0x08) + { + I_STATUS[3] = READ_VALUE(INT_STATUS(3)); + +// if (I_STATUS[3] & SESTABLISHED) ISR_ESTABLISHED(3); +// if (I_STATUS[3] & SCLOSED) ISR_CLOSED(3); + + WRITE_VALUE(INT_REG, 0x08); + } + + // channel 0 receive interrupt + if (status & 0x10) + { +// ISR_RX(0); + WRITE_VALUE(INT_REG, 0x10); + } + + // channel 1 receive interrupt + if (status & 0x20) + { +// ISR_RX(1); + WRITE_VALUE(INT_REG, 0x20); + } + + // channel 2 receive interrupt + if (status & 0x40) + { +// ISR_RX(2); + WRITE_VALUE(INT_REG, 0x40); + } + + // channel 3 receive interrupt + if (status & 0x80) + { +// ISR_RX(3); + WRITE_VALUE(INT_REG, 0x80); + } + status = READ_VALUE(INT_REG); + } + +WRITE_VALUE(INT_REG, 0xFF); + +#ifdef I2CHIP_WINDOW +i2chip_set_window(current_window); +#endif + + if( xSwitchRequired == pdTRUE ) + { + xSwitchRequired = xSemaphoreGiveFromISR( xTCPSemaphore, pdFALSE ); + } + + return xSwitchRequired; +} + +void far interrupt in4_isr_i2chip(void) +{ + if( prvProcessISR() == pdTRUE ) + { + portEND_SWITCHING_ISR(); + } + + INT_EOI; +} + +/* +**************************************************************************************************** +* Established connection interrupt handling function. +* +* Description : +* Called upon connection establishment, and may be inserted in user code if needed by +* the programmer. +* Arguments : None +* Returns : None +* Note : Internal Function +**************************************************************************************************** +*/ +/* +void ISR_ESTABLISHED(SOCKET s) +{ +// TO ADD YOUR CODE +} +*/ + +/* +**************************************************************************************************** +* Closed connection interrupt handling function +* +* Description : +* Called upon connection closure, and may be inserted in user code if needed by the programmer. +* Arguments : None +* Returns : None +* Note : Internal Function +**************************************************************************************************** +*/ +/* +void ISR_CLOSED(SOCKET s) +{ +// TO ADD YOUR CODE +} +*/ + +/* +**************************************************************************************************** +* Received data interrupt handling function +* +* Description : +* Called upon receiving data, and may be inserted in user code if needed by the programmer. +* Arguments : None +* Returns : None +* Note : Internal Function +**************************************************************************************************** +*/ +/* +void ISR_RX(SOCKET s) +{ +// TO ADD YOUR CODE +} +*/ + +/* +**************************************************************************************************** +* W3100A Initialization Function +* +* Description: Reset of W3100A S/W and Registeration of i386 interrupt +* Arguments : None. +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void initW3100A(void) +{ + +// Install interrupt handler for i2Chip +INT_INIT(in4_isr_i2chip); + + +Local_Port = 1000; // This default value will be set if you didn't designate it when you + // create a socket. If you don't designate port number and create a + // socket continuously, the port number will be assigned with + // incremented by one to Local_Port +SEQ_NUM.lVal = 4294967293ul; // Sets the initial SEQ# to be used for TCP communication. + // (It should be ramdom value) +WRITE_VALUE(COMMAND(0), CSW_RESET); // Software RESET +} + +/* +**************************************************************************************************** +* W3100A initialization function +* +* Description : +* Sets the Tx, Rx memory size by each channel, source MAC, source IP, gateway, and subnet mask +* to be used by the W3100A to the designated values. +* May be called when reflecting modified network information or Tx, Rx memory size on the W3100A +* Include Ping Request for ARP update (In case that a device embedding W3100A is directly +* connected to Router) +* Arguments : sbufsize - Tx memory size (00 - 1KByte, 01- 2KBtye, 10 - 4KByte, 11 - 8KByte) +* bit 1-0 : Tx memory size of channel #0 +* bit 3-2 : Tx memory size of channel #1 +* bit 5-4 : Tx memory size of channel #2 +* bit 7-6 : Tx memory size of channel #3 +* rbufsize - Rx memory size (00 - 1KByte, 01- 2KBtye, 10 - 4KByte, 11 - 8KByte) +* bit 1-0 : Rx memory size of channel #0 +* bit 3-2 : Rx memory size of channel #1 +* bit 5-4 : Rx memory size of channel #2 +* bit 7-6 : Rx memory size of channel #3 +* Returns : None +* Note : API Function +* Maximum memory size for Tx, Rx in W3100A is 8KBytes, +* In the range of 8KBytes, the memory size could be allocated dynamically by +* each channel +* Be attentive to sum of memory size shouldn't exceed 8Kbytes +* and to data transmission and receiption from non-allocated channel may cause +* some problems. +* If 8KBytes memory already is assigned to centain channel, other 3 channels +* couldn't be used, for there's no available memory. +* If two 4KBytes memory are assigned to two each channels, other 2 channels couldn't +* be used, for there's no available memory. +* (Example of memory assignment) +* sbufsize => 00000011, rbufsize => 00000011 : +* Assign 8KBytes for Tx and Rx to channel #0, Cannot use channel #1,#2,#3 +* sbufsize => 00001010, rbufsize => 00001010 : +* Assign 4KBytes for Tx and Rx to each channel #0,#1 respectively. Cannot use +* channel #2,#3 +* sbufsize => 01010101, rbufsize => 01010101 : +* Assign 2KBytes for Tx and Rx to each all channels respectively. +* sbufsize => 00010110, rbufsize => 01010101 : +* Assign 4KBytes for Tx, 2KBytes for Rx to channel #0 +* s 2KBytes for Tx, 2KBytes for Rx to channel #1 +* 2KBytes for Tx, 2KBytes for Rx to channel #2 +* 2KBytes is available exclusively for Rx in channel #3. There's no memory for Tx. +**************************************************************************************************** +*/ +void sysinit(u_char sbufsize, u_char rbufsize) +{ +char i; +int ssum,rsum; + +ssum = 0; +rsum = 0; + +// Set Tx memory size for each channel +WRITE_VALUE(TX_DMEM_SIZE, sbufsize); + +// Set Rx memory size for each channel +WRITE_VALUE(RX_DMEM_SIZE, rbufsize); + +// Set Base Address of Tx memory for channel #0 +SBUFBASEADDRESS[0] = 0; + +// Set Base Address of Rx memory for channel #0 +RBUFBASEADDRESS[0] = 0; + +// Set maximum memory size for Tx and Rx, mask, base address of memory by each channel +for(i = 0 ; i < MAX_SOCK_NUM; i++) + { + SSIZE[i] = 0; + RSIZE[i] = 0; + if(ssum < 8192) + { + switch((sbufsize >> i*2) & 0x03) // Set maximum Tx memory size + { + case 0: + SSIZE[i] = 1024; + SMASK[i] = 0x000003FF; + break; + + case 1: + SSIZE[i] = 2048; + SMASK[i] = 0x000007FF; + break; + + case 2: + SSIZE[i] = 4096; + SMASK[i] = 0x00000FFF; + break; + + case 3: + SSIZE[i] = 8192; + SMASK[i] = 0x00001FFF; + break; + } + } + if(rsum < 8192) + { + switch((rbufsize >> i*2) & 0x03) // Set maximum Rx memory size + { + case 0: + RSIZE[i] = 1024; + RMASK[i] = 0x000003FF; + break; + + case 1: + RSIZE[i] = 2048; + RMASK[i] = 0x000007FF; + break; + + case 2: + RSIZE[i] = 4096; + RMASK[i] = 0x00000FFF; + break; + + case 3: + RSIZE[i] = 8192; + RMASK[i] = 0x00001FFF; + break; + } + } + ssum += SSIZE[i]; + rsum += RSIZE[i]; + + // Set base address of Tx and Rx memory for channel #1,#2,#3 + if(i != 0) + { + SBUFBASEADDRESS[i] = ssum - SSIZE[i]; + RBUFBASEADDRESS[i] = rsum - RSIZE[i]; + } + } + + WRITE_VALUE(COMMAND(0), CSYS_INIT); + +while(!(I_STATUS[0] & SSYS_INIT_OK)) + I2CHIP_POLL_ISR(in4_isr_i2chip); + +#ifdef __PING__ + { + u_char xdata pingbuf[8]; + setIPprotocol(0, IPPROTO_ICMP); + socket(0, SOCK_IPL_RAW, 3000,0); // Create a socket for ARP update + + pingbuf[0] = 8; // ICMP TYPE + pingbuf[1] = 0; // ICMP CODE + pingbuf[2] = 0xf7; // CHECKSUM (already calculated) + pingbuf[3] = 0xfd; + pingbuf[4] = 0; // ID + pingbuf[5] = 1; + pingbuf[6] = 0; // SEQ # + pingbuf[7] = 1; + pingbuf[8] = 0; // Data 1 Byte + + sendto(0, pingbuf, 9, GATEWAY_PTR,3000); // Ping Request + close(0); + printf("Route MAC Update Success"); + } +#endif +} + +/* +**************************************************************************************************** +* Function to set subnet mask +* +* Description: +* Arguments : addr--> Pointer that has the value to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setsubmask(u_char * addr) +{ +u_char i; +u_char far* sm_ptr = SUBNET_MASK_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. + +for (i = 0; i < 4; i++) + { + WRITE_VALUE(sm_ptr + SA_OFFSET(i), addr[i]); + } +} + +/* +**************************************************************************************************** +* Function to set gateway IP +* +* Description: +* Arguments : addr--> Pointer that has Gateway IP to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setgateway(u_char * addr) +{ +u_char i; +u_char far* gw_ptr = GATEWAY_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. +for (i = 0; i < 4; i++) + { + WRITE_VALUE(gw_ptr + SA_OFFSET(i), addr[i]); + } +} + +/* +**************************************************************************************************** +* Function to set W3100A IP +* +* Description: +* Arguments : addr--> Pointer that has Source IP to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setIP(u_char * addr) +{ +u_char i; +u_char far* src_ptr = SRC_IP_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. + +for (i = 0; i < 4; i++) + { + WRITE_VALUE(src_ptr + SA_OFFSET(i), addr[i]); + } +} + +// DEBUG +void getIP(u_char* addr) +{ +u_char i; +u_char far* src_ptr = SRC_IP_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. + +for (i = 0; i < 4; i++) + addr[i] = READ_VALUE(src_ptr + SA_OFFSET(i)); +} + + +/* +**************************************************************************************************** +* Function to set MAC +* +* Description: +* Arguments : addr--> Pointer that has MAC to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setMACAddr(u_char * addr) +{ +u_char i; +u_char far* ha_ptr = SRC_HA_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. + +for (i = 0; i < 6; i++) + { + WRITE_VALUE(ha_ptr + SA_OFFSET(i), addr[i]); + } +} + +/* +**************************************************************************************************** +* Function to set TCP timeout +* +* Description: The function that used to adjust time to resend TCP +* Arguments : val --> Pointer that has the value to be set +* Upper 2 byte:Initial timeout value +* Last 1 byte:The count to retry till timeout +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void settimeout(u_char * val) +{ +u_char i; +u_char far* tout_ptr = TIMEOUT_PTR; // We can only convert to 'regular' + // pointer if we're confident arithmetic + // won't take us out of current window. + +for (i = 0; i < 3; i++) + { + WRITE_VALUE(tout_ptr + SA_OFFSET(i), val[i]); + } +} + +/* +**************************************************************************************************** +* Function to set interrupt mask. +* +* Description: +* Arguments : mask--> Mask value to be set ('1'-> interrupt ) +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setINTMask(u_char mask) +{ +WRITE_VALUE(INTMASK, mask); +} + +/* +**************************************************************************************************** +* Function to set enable in sending and receiving of broadcast data +* +* Description: Enable to process of broadcating data in UDP or IP RAW mode. +* Arguments : s --> Channel No. to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setbroadcast(SOCKET s) +{ +u_char val = READ_VALUE(OPT_PROTOCOL(s)); +WRITE_VALUE(OPT_PROTOCOL(s), val | SOCKOPT_BROADCAST); +} + +/* +**************************************************************************************************** +* Function to set process protocol in IP RAW mode. +* +* Description: +* Arguments : s--> Channel No. to be set +* tos-->Protocol Value to be set +* Returns : None. +* Note : +**************************************************************************************************** +*/ +void setTOS(SOCKET s, u_char tos) +{ +WRITE_VALUE(TOS(s), tos); +} + +/* +**************************************************************************************************** +* Upper layer protocol setup function in IP RAW Mode +* +* Description : Upper layer protocol setup function in protocol field of IP header when +* developing upper layer protocol like ICMP, IGMP, EGP etc. by using IP Protocol +* Arguments : s - Channel number +* ipprotocol - Upper layer protocol setting value of IP Protocol +* (Possible to use designated IPPROTO_ in header file) +* Returns : None +* Note : API Function +* This function should be called before calling socket() that is, before +* socket initialization. +**************************************************************************************************** +*/ +void setIPprotocol(SOCKET s, u_char ipprotocol) +{ +WRITE_VALUE(IP_PROTOCOL(s), ipprotocol); +} + +/* +**************************************************************************************************** +* Initialization function to appropriate channel +* +* Description : Initialize designated channel and wait until W3100 has done. +* Arguments : s - channel number +* protocol - designate protocol for channel +* SOCK_STREAM(0x01) -> TCP. +* SOCK_DGRAM(0x02) -> UDP. +* SOCK_IPL_RAW(0x03) -> IP LAYER RAW. +* SOCK_MACL_RAW(0x04) -> MAC LAYER RAW. +* port - designate source port for appropriate channel +* flag - designate option to be used in appropriate. +* SOCKOPT_BROADCAST(0x80) -> Send/receive broadcast message in UDP +* SOCKOPT_NDTIMEOUT(0x40) -> Use register value which designated TIMEOUT +* value +* SOCKOPT_NDACK(0x20) -> When not using no delayed ack +* SOCKOPT_SWS(0x10) -> When not using silly window syndrome +* Returns : When succeeded : Channel number, failed :-1 +* Note : API Function +**************************************************************************************************** +*/ +char socket(SOCKET s, u_char protocol, u_int port, u_char flag) +{ +u_char k; + +//Designate socket protocol and option +WRITE_VALUE(OPT_PROTOCOL(s), protocol | flag); + +// setup designated port number +if (port != 0) + { + k = (u_char)((port & 0xff00) >> 8); + WRITE_VALUE(SRC_PORT_PTR(s), k); + k = (u_char)(port & 0x00ff); + WRITE_VALUE(SRC_PORT_PTR(s) + SA_OFFSET(1), k); + } +else + { + // Designate random port number which is managed by local when you didn't designate source port + Local_Port++; + + WRITE_VALUE(SRC_PORT_PTR(s), (u_char)((Local_Port & 0xff00) >> 8)); + WRITE_VALUE(SRC_PORT_PTR(s) + SA_OFFSET(1), (u_char)(Local_Port & 0x00ff)); + } + +// SOCK_INIT +I_STATUS[s] = 0; +WRITE_VALUE(COMMAND(s), CSOCK_INIT); + +// Waiting Interrupt to CSOCK_INIT +while (I_STATUS[s] == 0) + I2CHIP_POLL_ISR(in4_isr_i2chip); + +if (!(I_STATUS[s] & SSOCK_INIT_OK)) + return(-1); + +initseqnum(s); // Use initial seq# with random number + +return(s); +} + +/* +**************************************************************************************************** +* Connection establishing function to designated peer. +* +* Description : This function establish a connection to the peer by designated channel, +* and wait until the connection is established successfully. (TCP client mode) +* Arguments : s - channel number +* addr - destination IP Address +* port - destination Port Number +* Returns : when succeeded : 1, failed : -1 +* Note : API Function +**************************************************************************************************** +*/ +char connect(SOCKET s, u_char far * addr, u_int port) +{ + +if (port != 0) + { //designate destination port + WRITE_VALUE(DST_PORT_PTR(s), (u_char)((port & 0xff00) >> 8)); + WRITE_VALUE(DST_PORT_PTR(s) + SA_OFFSET(1), (u_char)(port & 0x00ff)); + } +else + return(-1); + + WRITE_VALUE(DST_IP_PTR(s), addr[0]); //designate destination IP address + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(1), addr[1]); + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(2), addr[2]); + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(3), addr[3]); + +I_STATUS[s] = 0; + + WRITE_VALUE(COMMAND(s), CCONNECT); // CONNECT + I2CHIP_POLL_ISR(in4_isr_i2chip); + +// Wait until connection is established successfully +while (I_STATUS[s] == 0) + { + // When failed, appropriate channel will be closed and return an error + if (select(s, SEL_CONTROL) == SOCK_CLOSED) + return -1; + } + +if (!(I_STATUS[s] & SESTABLISHED)) + return(-1); + +return(1); +} + +/* +**************************************************************************************************** +* Connection establishing function to designated peer. (Non-blocking Mode) +* +* Description : This function establish a connection to the peer by designated channel. +* +* Arguments : s - channel number +* addr - destination IP Address +* port - destination Port Number +* Returns : when succeeded : 1, failed : -1 +* Note : API Function +**************************************************************************************************** +*/ +char NBconnect(SOCKET s, u_char far * addr, u_int port) +{ + +if (port != 0) + { //designate destination port + WRITE_VALUE(DST_PORT_PTR(s), (u_char) ((port & 0xff00) >> 8) ); + WRITE_VALUE(DST_PORT_PTR(s) + SA_OFFSET(1), (u_char)(port & 0x00ff)); + } +else + return(-1); + + WRITE_VALUE(DST_IP_PTR(s), addr[0]); //designate destination IP address + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(1), addr[1]); + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(2), addr[2]); + WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(3), addr[3]); + +I_STATUS[s] = 0; + +WRITE_VALUE(COMMAND(s), CCONNECT); // CONNECT +return(1); +} + +/* +**************************************************************************************************** +* Waits for connection request from a peer (Blocking Mode) +* +* Description : Wait for connection request from a peer through designated channel (TCP Server mode) +* Arguments : s - channel number +* addr - IP Address of the peer when a connection is established +* port - Port number of the peer when a connection is established +* Returns : When succeeded : 1, failed : -1 +* Note : API Function +**************************************************************************************************** +*/ +/* +char listen(SOCKET s, u_char far * addr, u_int far * port) +{ +u_int i; + +I_STATUS[s] = 0; + +// LISTEN +COMMAND(s) = CLISTEN; + +// Wait until connection is established +while (I_STATUS[s] == 0) + { + // When failed to connect, the designated channel will be closed and return an error. + if (select(s, SEL_CONTROL) == SOCK_CLOSED) + return -1; + } + +// Receive IP address and port number of the peer connected +if (I_STATUS[s] & SESTABLISHED) + { + i = *DST_PORT_PTR(s); + *port = (u_int)((i & 0xff00) >> 8); + i = *(DST_PORT_PTR(s) + 2); + i = (u_int)(i & 0x00ff); + *port += (i << 8); + + addr[0] = *DST_IP_PTR(s); + addr[1] = *(DST_IP_PTR(s) + 2); + addr[2] = *(DST_IP_PTR(s) + 4); + addr[3] = *(DST_IP_PTR(s) + 6); + } +else + return(-1); + +return(1); +} +*/ + +/* +**************************************************************************************************** +* Waits for connection request from a peer (Non-blocking Mode) +* +* Description : Wait for connection request from a peer through designated channel (TCP Server mode) +* Arguments : s - channel number +* Returns : None +* Note : API Function +**************************************************************************************************** +*/ +char NBlisten(SOCKET s) +{ +I_STATUS[s] = 0; + +// LISTEN +WRITE_VALUE(COMMAND(s), CLISTEN); + +return(1); +} + +/* +**************************************************************************************************** +* Create random value for initial Seq# when establishing TCP connection +* +* Description : In this function, you can add some source codes to create random number for +* initial Seq#. In real, TCP initial SEQ# should be random value. +* (Currently, we're using static value in EVB/DK.) +* Arguments : s - channel number +* Returns : None +* Note : API Function +**************************************************************************************************** +*/ +void initseqnum(SOCKET s) +{ +// Designate initial seq# +// If you have random number generation function, assign random number instead of SEQ_NUM.lVal++. +SEQ_NUM.lVal++; + +//randomize(); +//SEQ_NUM.lVal = rand(); + +WRITE_VALUE(TX_WR_PTR(s), SEQ_NUM.cVal[0]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(1), SEQ_NUM.cVal[1]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(2), SEQ_NUM.cVal[2]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(3), SEQ_NUM.cVal[3]); +delay0(2); + +WRITE_VALUE(TX_RD_PTR(s), SEQ_NUM.cVal[0]); +WRITE_VALUE(TX_RD_PTR(s) + SA_OFFSET(1), SEQ_NUM.cVal[1]); +WRITE_VALUE(TX_RD_PTR(s) + SA_OFFSET(2), SEQ_NUM.cVal[2]); +WRITE_VALUE(TX_RD_PTR(s) + SA_OFFSET(3), SEQ_NUM.cVal[3]); +delay0(2); + +WRITE_VALUE(TX_ACK_PTR(s), SEQ_NUM.cVal[0]); +WRITE_VALUE(TX_ACK_PTR(s) + SA_OFFSET(1), SEQ_NUM.cVal[1]); +WRITE_VALUE(TX_ACK_PTR(s) + SA_OFFSET(2), SEQ_NUM.cVal[2]); +WRITE_VALUE(TX_ACK_PTR(s) + SA_OFFSET(3), SEQ_NUM.cVal[3]); +delay0(2); +} + +/* +**************************************************************************************************** +* Function for sending TCP data. +* +* Description : Function for sending TCP data and Composed of the send() and send_in() functions. +* The send() function is an application I/F function. +* It continues to call the send_in() function to complete the sending of the data up to the +* size of the data to be sent when the application is called. +* The send_in() function receives the return value (the size of the data sent), calculates +* the size of the data to be sent, and calls the send_in() function again if there is any +* data left to be sent. +* Arguments : s - channel number +* buf - Pointer pointing data to send +* len - data size to send +* Returns : Succeed: sent data size, Failed: -1; +* Note : API Function +**************************************************************************************************** +*/ +int send(SOCKET s, u_char far * buf, u_int len) +{ +int ptr, size; +u_char huge* huge_buf = (u_char huge*)buf; +u_char far* local_buf = (u_char far*)huge_buf; + +if (len <= 0) + return (0); +else + { + ptr = 0; + do + { + size = send_in(s, local_buf + ptr, len); + if (size == -1) + return -1; + len = len - size; + ptr += size; + } while ( len > 0); + } +return ptr; +} + +/* +**************************************************************************************************** +* Internal function for sending TCP data. +* +* Description : Called by the send() function for TCP transmission. +* It first calculates the free transmit buffer size +* and compares it with the size of the data to be transmitted to determine the transmission size. +* After calculating the data size, it copies data from TX_WR_PTR. +* It waits if there is a previous send command in process. +* When the send command is cleared, it updates the TX_WR_PTR up to the size to be transmitted + and performs the send command. +* Arguments : s - channel number +* buf - Pointer pointing data to send +* len - data size to send +* Returns : Succeeded: sent data size, Failed: -1 +* Note : Internal Function +**************************************************************************************************** +*/ +int send_in(SOCKET s, u_char far * buf, u_int len) +{ +u_char k; +u_int size; +union un_l2cval wr_ptr, ack_ptr; +unsigned int offset; + +S_START: +disable(); // CT: Shadow register access should not conflict with ISR. +k = READ_VALUE(SHADOW_TXWR_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +wr_ptr.cVal[3] = READ_VALUE(TX_WR_PTR(s)); +wr_ptr.cVal[2] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(1)); +wr_ptr.cVal[1] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(2)); +wr_ptr.cVal[0] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(3)); + +k = READ_VALUE(SHADOW_TXACK_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +ack_ptr.cVal[3] = READ_VALUE(TX_ACK_PTR(s)); +ack_ptr.cVal[2] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(1)); +ack_ptr.cVal[1] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(2)); +ack_ptr.cVal[0] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(3)); +enable(); + +// Suppress compiler errors that k is not used +k = k; + +// Calculate send free buffer size +if (wr_ptr.lVal >= ack_ptr.lVal) + size = (u_int)(SSIZE[s] - (wr_ptr.lVal - ack_ptr.lVal)); +else + size = (u_int)(SSIZE[s] - (0 - ack_ptr.lVal + wr_ptr.lVal)); + +// Recalulate after some delay because of error in pointer calculation +if (size > SSIZE[s]) + { + if (select(s, SEL_CONTROL) != SOCK_ESTABLISHED) + return -1; + delay_ms(1); + goto S_START; + } + +// Wait when previous sending has not finished yet and there's no free buffer +if (size == 0) + { + if (select(s, SEL_CONTROL) != SOCK_ESTABLISHED) + return -1; + + delay_ms(1); + goto S_START; + } +else if (size < len) + { + len = size; + } + +// Calculate pointer to data copy +offset = (UINT)(wr_ptr.lVal & SMASK[s]); + +// copy data +write_data(s, buf, offset, len); + +while (READ_VALUE(COMMAND(s)) & CSEND) + { + // Confirm previous send command + if (select(s, SEL_CONTROL) != SOCK_ESTABLISHED) + return -1; + } + +// update tx_wr_ptr +wr_ptr.lVal = wr_ptr.lVal + len; +WRITE_VALUE(TX_WR_PTR(s), wr_ptr.cVal[3]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(1), wr_ptr.cVal[2]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(2), wr_ptr.cVal[1]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(3), wr_ptr.cVal[0]); + +delay0(1); + +// SEND +WRITE_VALUE(COMMAND(s), CSEND); + +return(len); +} + +/* +**************************************************************************************************** +* TCP data receiving function. +* +* Description : This function is to clear out any received TCP data. +* Arguments : s - channel number +* Returns : None +* Note : API Fcuntion +**************************************************************************************************** +*/ +void recv_clear(SOCKET s) +{ +u_char k; +u_int size; +union un_l2cval wr_ptr, rd_ptr; + +disable(); +k = READ_VALUE(SHADOW_RXWR_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +wr_ptr.cVal[3] = READ_VALUE(RX_WR_PTR(s)); +wr_ptr.cVal[2] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(1)); +wr_ptr.cVal[1] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(2)); +wr_ptr.cVal[0] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(3)); + +k = READ_VALUE(SHADOW_RXRD_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +rd_ptr.cVal[3] = READ_VALUE(RX_RD_PTR(s)); +rd_ptr.cVal[2] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(1)); +rd_ptr.cVal[1] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(2)); +rd_ptr.cVal[0] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(3)); +enable(); + +// Suppress compiler errors that k is not used +k = k; + +// calculate received data size +if (wr_ptr.lVal >= rd_ptr.lVal) + size = (u_int)(wr_ptr.lVal - rd_ptr.lVal); +else + size = (u_int)(0 - rd_ptr.lVal + wr_ptr.lVal); + +// Update rx_rd_ptr +rd_ptr.lVal += size; +WRITE_VALUE(RX_RD_PTR(s), rd_ptr.cVal[3]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(1), rd_ptr.cVal[2]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(2), rd_ptr.cVal[1]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(3), rd_ptr.cVal[0]); + +// RECV + WRITE_VALUE(COMMAND(s), CRECV); +} + +/* +**************************************************************************************************** +* TCP data receiving function. +* +* Description : This function is for receiving TCP data. +* The recv() function is an application I/F function. It will read up to len chars if there are + enough characters in the buffer, otherwise will onl read the number of characters availiable +* Arguments : s - channel number +* buf - Pointer where the data to be received is copied +* len - Size of the data to be received +* Returns : Succeeded: received data size, Failed: -1 +* Note : API Fcuntion +**************************************************************************************************** +*/ +int recv(SOCKET s, u_char far * buf, u_int len) +{ +u_char k; +u_int size; +union un_l2cval wr_ptr, rd_ptr; +unsigned int offset; + +// If out length is 0, then we do not need to do anything +if (len <= 0) + return (0); + +disable(); +k = READ_VALUE(SHADOW_RXWR_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +wr_ptr.cVal[3] = READ_VALUE(RX_WR_PTR(s)); +wr_ptr.cVal[2] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(1)); +wr_ptr.cVal[1] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(2)); +wr_ptr.cVal[0] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(3)); + +k = READ_VALUE(SHADOW_RXRD_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +rd_ptr.cVal[3] = READ_VALUE(RX_RD_PTR(s)); +rd_ptr.cVal[2] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(1)); +rd_ptr.cVal[1] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(2)); +rd_ptr.cVal[0] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(3)); +enable(); + +// Suppress compiler errors that k is not used +k = k; + +// calculate IIM7010A received data size +if (wr_ptr.lVal == rd_ptr.lVal) + return(0); +else if (wr_ptr.lVal >= rd_ptr.lVal) + size = (u_int)(wr_ptr.lVal - rd_ptr.lVal); +else + size = (u_int)(0 - rd_ptr.lVal + wr_ptr.lVal); + +// Make sure we do not try to read more characters than what is availiable in the IIM7010 buffer +if (size < len) + len = size; + +// Calculate pointer to be copied received data +offset = ((UINT)(rd_ptr.lVal & RMASK[s])); + +// Copy received data +size = read_data(s, offset, buf, len); + +// Update rx_rd_ptr +rd_ptr.lVal += size; +WRITE_VALUE(RX_RD_PTR(s), rd_ptr.cVal[3]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(1), rd_ptr.cVal[2]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(2), rd_ptr.cVal[1]); +WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(3), rd_ptr.cVal[0]); + +// RECV + WRITE_VALUE(COMMAND(s), CRECV); +return(size); +} + + +/* +**************************************************************************************************** +* UDP data sending function. +* +* Description : Composed of the sendto()and sendto_in() functions. +* The send() function is an application I/F function. +* It continues to call the send_in() function to complete the sending of the data up to the +* size of the data to be sent +* when the application is called.Unlike TCP transmission, it designates the destination address +* and the port. +* Arguments : s - channel port +* buf - Pointer pointing data to send +* len - data size to send +* addr - destination IP address to send data +* port - destination port number to send data +* Returns : Sent data size +* Note : API Function +**************************************************************************************************** +*/ +u_int sendto(SOCKET s, u_char far * buf, u_int len, u_char * addr, u_int port) +{ +//char val; +u_int ptr, size; + +// Wait until previous send commnad has completed. +while(READ_VALUE(COMMAND(s)) & CSEND) + { + if(select(s, SEL_CONTROL) == SOCK_CLOSED) + return -1; // Error. + } + +// Designate destination port number. +if (port != 0) + { + WRITE_VALUE(DST_PORT_PTR(s), (u_char)((port & 0xff00) >> 8)); + WRITE_VALUE(DST_PORT_PTR(s) + SA_OFFSET(1), (u_char)(port & 0x00ff)); + } + +// Designate destination IP address +WRITE_VALUE(DST_IP_PTR(s), addr[0]); +WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(1), addr[1]); +WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(2), addr[2]); +WRITE_VALUE(DST_IP_PTR(s) + SA_OFFSET(3), addr[3]); + +if (len <= 0) + return (0); +else + { + ptr = 0; + do + { + size = sendto_in(s, buf + ptr, len); + len = len - size; + ptr += size; + } while ( len > 0); + } +return ptr; +} + +/* +**************************************************************************************************** +* UDP data sending function. +* +* Description : An internal function that is the same as the send_in() function of the TCP. +* Arguments : s - Channel number +* buf - Pointer indicating the data to send +* len - data size to send +* Returns : Sent data size +* Note : Internal Function +**************************************************************************************************** +*/ +u_int sendto_in(SOCKET s, u_char far * buf, u_int len) +{ +u_char k; +u_int size; +union un_l2cval wr_ptr, rd_ptr; +unsigned int offset; + +S2_START: +disable(); +k = READ_VALUE(SHADOW_TXWR_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +wr_ptr.cVal[3] = READ_VALUE(TX_WR_PTR(s)); +wr_ptr.cVal[2] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(1)); +wr_ptr.cVal[1] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(2)); +wr_ptr.cVal[0] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(3)); + +k = READ_VALUE(SHADOW_TXRD_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +rd_ptr.cVal[3] = READ_VALUE(TX_RD_PTR(s)); +rd_ptr.cVal[2] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(1)); +rd_ptr.cVal[1] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(2)); +rd_ptr.cVal[0] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(3)); +enable(); + +// Suppress compiler errors that k is not used +k = k; + +// Calculate free buffer size to send +if (wr_ptr.lVal >= rd_ptr.lVal) + size = (u_int)(SSIZE[s] - (wr_ptr.lVal - rd_ptr.lVal)); +else + size = (u_int)(SSIZE[s] - (0 - rd_ptr.lVal + wr_ptr.lVal)); + +// Recalulate after some delay because of error in pointer caluation +if (size > SSIZE[s]) + { + delay_ms(1); + goto S2_START; + } + +// Wait when previous sending has not finished yet and there's no free buffer +if (size == 0) + { + delay_ms(1); + goto S2_START; + + } +else if (size < len) + { + len = size; + } + +// Calculate pointer to copy data pointer +offset =(UINT)(wr_ptr.lVal & SMASK[s]); + +// copy data +write_data(s, buf, offset, len); + +// Confirm previous send command +while (READ_VALUE(COMMAND(s)) & CSEND) + { + if(select(s, SEL_CONTROL)==SOCK_CLOSED) + return -1; // Error + } + +// update tx_wr_ptr +wr_ptr.lVal = wr_ptr.lVal + len; +WRITE_VALUE(TX_WR_PTR(s), wr_ptr.cVal[3]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(1), wr_ptr.cVal[2]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(2), wr_ptr.cVal[1]); +WRITE_VALUE(TX_WR_PTR(s) + SA_OFFSET(3), wr_ptr.cVal[0]); + +delay0(1); + +// SEND +WRITE_VALUE(COMMAND(s), CSEND); + +return(len); +} + +/* +**************************************************************************************************** +* UDP data receiving function. +* +* Description : Function for receiving UDP and IP layer RAW mode data, and handling the data header. +* Arguments : s - channel number +* buf - Pointer where the data to be received is copied +* len - Size of the data to be received +* addr - Peer IP address for receiving +* port - Peer port number for receiving +* Returns : Received data size +* Note : API Function +**************************************************************************************************** +*/ +u_int recvfrom(SOCKET s, u_char far *buf, u_int len, u_char *addr, u_int *port) +{ +struct _UDPHeader // When receiving UDP data, header added by W3100A + { + union + { + struct + { + u_int size; + u_char addr[4]; + u_int port; + } header; + u_char stream[8]; + } u; + } UDPHeader; + +u_int ret; +union un_l2cval wr_ptr, rd_ptr; +u_long size; +u_char k; +unsigned int offset; + +if(select(s,SEL_CONTROL)==SOCK_CLOSED) + return -1; + +disable(); +k = READ_VALUE(SHADOW_RXWR_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +wr_ptr.cVal[3] = READ_VALUE(RX_WR_PTR(s)); +wr_ptr.cVal[2] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(1)); +wr_ptr.cVal[1] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(2)); +wr_ptr.cVal[0] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(3)); + +k = READ_VALUE(SHADOW_RXRD_PTR(s)); +WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. +delay0(2); +rd_ptr.cVal[3] = READ_VALUE(RX_RD_PTR(s)); +rd_ptr.cVal[2] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(1)); +rd_ptr.cVal[1] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(2)); +rd_ptr.cVal[0] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(3)); +enable(); + +// Suppress compiler errors that k is not used +k = k; + +// Calculate received data size +if (len <= 0) + return (0); +else if (wr_ptr.lVal >= rd_ptr.lVal) + size = wr_ptr.lVal - rd_ptr.lVal; +else + size = 0 - rd_ptr.lVal + wr_ptr.lVal; + +if (size == 0) + return 0; + + // Calulate received data pointer +offset = ((UINT)(rd_ptr.lVal & RMASK[s])); + +// When UDP data +if (( READ_VALUE(OPT_PROTOCOL(s)) & 0x07) == SOCK_DGRAM) + { + // Copy W3100A UDP header + read_data(s, offset, UDPHeader.u.stream, 8); + + // Read UDP Packet size + size = UDPHeader.u.stream[0]; + size = (size << 8) + UDPHeader.u.stream[1]; + + // Read IP address of the peer + addr[0] = UDPHeader.u.header.addr[0]; + addr[1] = UDPHeader.u.header.addr[1]; + addr[2] = UDPHeader.u.header.addr[2]; + addr[3] = UDPHeader.u.header.addr[3]; + + // Read Port number of the peer + *port = UDPHeader.u.stream[6]; + *port = (*port << 8) + UDPHeader.u.stream[7]; + + // Increase read pointer by 8, because already read as UDP header size + rd_ptr.lVal += 8; + + // Calculate UDP data copy pointer + offset = ((UINT)(rd_ptr.lVal & RMASK[s])); + + // Calculate data size of current UDP Packet from UDP header + size = size - 8; + + // Copy one UDP data packet to user-specific buffer + ret = read_data(s, offset, buf, (u_int)size); + + // Increase read pointer by UDP packet data size + rd_ptr.lVal += ret; + } +else if ((READ_VALUE(OPT_PROTOCOL(s)) & 0x07) == SOCK_IPL_RAW) // When IP layer RAW mode data + { + // Copy W3100A IP Raw header + read_data(s, offset, UDPHeader.u.stream, 6); + + // Read IP layer RAW Packet size + size = UDPHeader.u.stream[0]; + size = (size << 8) + UDPHeader.u.stream[1]; + + // Read IP address of the peer + addr[0] = UDPHeader.u.header.addr[0]; + addr[1] = UDPHeader.u.header.addr[1]; + addr[2] = UDPHeader.u.header.addr[2]; + addr[3] = UDPHeader.u.header.addr[3]; + + // Increase read pointer by 6, because already read as IP RAW header size + rd_ptr.lVal += 6; + + // Calculate IP layer raw mode data pointer + offset = ((UINT)(rd_ptr.lVal & RMASK[s])); + + // Copy one IP Raw data packet to user-specific buffer + ret = read_data(s, offset, buf, (u_int)size); + rd_ptr.lVal = rd_ptr.lVal + (ret - 4); + } + + // Update rx_rd_ptr + WRITE_VALUE(RX_RD_PTR(s), rd_ptr.cVal[3]); + WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(1), rd_ptr.cVal[2]); + WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(2), rd_ptr.cVal[1]); + WRITE_VALUE(RX_RD_PTR(s) + SA_OFFSET(3), rd_ptr.cVal[0]); + + // RECV + WRITE_VALUE(COMMAND(s), CRECV); + +// Real received size return +return(ret); +} + +/* +**************************************************************************************************** +* Channel closing function. +* +* Description : Function for closing the connection of the designated channel. +* Arguments : s - channel number +* Returns : None +* Note : API Function +**************************************************************************************************** +*/ +void close(SOCKET s) +{ +u_int len; +short sAttempts = 0; + +I_STATUS[s] = 0; + +if (select(s, SEL_CONTROL) == SOCK_CLOSED) + return; // Already closed + +// When closing, if there's data which have not processed, Insert some source codes to handle this +// Or before application call close(), handle those data first and call close() later. + +len = select(s, SEL_SEND); +if (len == SSIZE[s]) + { + // CLOSE + WRITE_VALUE(COMMAND(s), CCLOSE); + // TODO: The 'SCLOSED' status value is only set briefly as part of the close, + // and will otherwise quickly return to normal. That means your code might + // become 'stuck' at this point even if the packet has closed normally. + // Rather than a while() call, it might be preferred to time out on this + // close check and return to the application after some time. + while(!(I_STATUS[s] & SCLOSED)) + { + sAttempts++; + if( sAttempts > 10 ) + { + break; + } + I2CHIP_POLL_ISR(in4_isr_i2chip); + vTaskDelay( 1 ); + } + } +} + +u_char tx_empty(SOCKET s) +{ + return (select(s, SEL_SEND) == SSIZE[s]); +} + +/* +**************************************************************************************************** +* Channel closing function. +* +* Description : Function for closing the connection of the designated channel. +* Arguments : s - channel number +* Returns : None +* Note : API Function +**************************************************************************************************** +*/ +char reset_sock(SOCKET s) +{ +u_char c; + +c = 1 << s; + +// RESET +WRITE_VALUE(RESETSOCK, c); +return (1); +} + +/* +**************************************************************************************************** +* Function handling the channel socket information. +* +* Description : Return socket information of designated channel +* Arguments : s - channel number +* func - SEL_CONTROL(0x00) -> return socket status +* SEL_SEND(0x01) -> return free transmit buffer size +* SEL_RECV(0x02) -> return received data size +* Returns : socket status or free transmit buffer size or received data size +* Note : API Function +**************************************************************************************************** +*/ +u_int select(SOCKET s, u_char func) +{ +u_int val; +union un_l2cval rd_ptr, wr_ptr, ack_ptr; +u_char k; + +switch (func) + { + // socket status information + case SEL_CONTROL : + val = READ_VALUE(SOCK_STATUS(s)); + break; + + // Calculate send free buffer size + case SEL_SEND : + disable(); + k = READ_VALUE(SHADOW_TXWR_PTR(s)); + WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. + delay0(2); + wr_ptr.cVal[3] = READ_VALUE(TX_WR_PTR(s)); + wr_ptr.cVal[2] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(1)); + wr_ptr.cVal[1] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(2)); + wr_ptr.cVal[0] = READ_VALUE(TX_WR_PTR(s) + SA_OFFSET(3)); + + if (( READ_VALUE(OPT_PROTOCOL(s)) & 0x07) == SOCK_STREAM) // TCP + { + k = READ_VALUE(SHADOW_TXACK_PTR(s)); + WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. + delay0(2); + ack_ptr.cVal[3] = READ_VALUE(TX_ACK_PTR(s)); + ack_ptr.cVal[2] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(1)); + ack_ptr.cVal[1] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(2)); + ack_ptr.cVal[0] = READ_VALUE(TX_ACK_PTR(s) + SA_OFFSET(3)); + enable(); + + if (wr_ptr.lVal >= ack_ptr.lVal) + val = (u_int)(SSIZE[s] - (wr_ptr.lVal - ack_ptr.lVal)); + else + val = (u_int)(SSIZE[s] - (0 - ack_ptr.lVal + wr_ptr.lVal)); + } + else // UDP, IP RAW ... (except TCP) + { + k = READ_VALUE(SHADOW_TXRD_PTR(s)); + WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. + delay0(2); + rd_ptr.cVal[3] = READ_VALUE(TX_RD_PTR(s)); + rd_ptr.cVal[2] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(1)); + rd_ptr.cVal[1] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(2)); + rd_ptr.cVal[0] = READ_VALUE(TX_RD_PTR(s) + SA_OFFSET(3)); + enable(); + + if (wr_ptr.lVal >= rd_ptr.lVal) + val = (u_int)(SSIZE[s] - (wr_ptr.lVal - rd_ptr.lVal)); + else + val = (u_int)(SSIZE[s] - (0 - rd_ptr.lVal + wr_ptr.lVal)); + } + break; + + // Calculate received data size + case SEL_RECV : + disable(); + k = READ_VALUE(SHADOW_RXWR_PTR(s)); + WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. + delay0(2); + wr_ptr.cVal[3] = READ_VALUE(RX_WR_PTR(s)); + wr_ptr.cVal[2] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(1)); + wr_ptr.cVal[1] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(2)); + wr_ptr.cVal[0] = READ_VALUE(RX_WR_PTR(s) + SA_OFFSET(3)); + + k = READ_VALUE(SHADOW_RXRD_PTR(s)); + WINDOW_RESTORE_BASE; // Needed whenever we touch a shadow ptr; different window. + delay0(2); + rd_ptr.cVal[3] = READ_VALUE(RX_RD_PTR(s)); + rd_ptr.cVal[2] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(1)); + rd_ptr.cVal[1] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(2)); + rd_ptr.cVal[0] = READ_VALUE(RX_RD_PTR(s) + SA_OFFSET(3)); + enable(); + + if (wr_ptr.lVal == rd_ptr.lVal) + val = 0; + else if (wr_ptr.lVal > rd_ptr.lVal) + val = (u_int)(wr_ptr.lVal - rd_ptr.lVal); + else + val = (u_int)(0 - rd_ptr.lVal + wr_ptr.lVal); + break; + + default : + val = -1; + break; + } +// Suppress compiler errors that k is not used +k = k; +return(val); +} + +// +// unsigned char dma_read_i2chip (unsigned int i2_segm, unsigned int i2_offs, +// unsigned int cnt, unsigned int des_segm, unsigned int des_offs); +// Using DMA0 to read data from i2chip buffer into destination SRAM. +// where: +// unsigned int cnt = number of sectors, 512-byte per sector +// unsigned int des_segm = segment of destination SRAM data memory +// unsigned int des_offs = offset of destination SRAM data memory +// unsigned int i2_segm = segment of i2chip buffer mapped in memory +// unsigned int i2_offs = offset of i2chip buffer mapped in memory +// return DMA counter value +// +unsigned int dma_read_i2chip(u_char far* i2_src, u_char far* des, u_int cnt) +{ + u_int des_segm, des_offs; + u_int i2_segm, i2_offs; + u_long temp; + + temp = ((long)FP_SEG(des) << 4) + ((long)FP_OFF(des)); + des_segm = (u_int)(temp >> 16); + des_offs = (u_int)(temp & 0xffff); + + temp = ((long)FP_SEG(i2_src) << 4) + ((long)FP_OFF(i2_src)); + i2_segm = (u_int)(temp >> 16); + i2_offs = (u_int)(temp & 0xffff); + + outport(0xffc6, des_segm); /* D0DSTH destination SRAM segment */ + outport(0xffc4, des_offs); /* D0DSTL destination SRAM offset */ + outport(0xffc2, i2_segm); /* D0SRCH=SP0RD */ + outport(0xffc0, i2_offs); /* D0SRCL=SP0RD */ + outport(0xffc8, cnt); // D0TC counter + outport(0xfff8,0x0504); // PLLCON, 0203=10M,050f=40M, 051f=80MHz +// DMA0 mem-mem, 16-bit, unsync, Start moving data line below + outport(0xffca, 0xb60e); /* D0CON 1011 0110 0000 1111 */ +// outport(0xffca, 0xb42e); // 1011 0100 0010 1110 + while( inport(0xffc8) ); /* D0TC counter=0, DMA complete */ + outport(0xfff8,0x051f); // PLLCON, 0203=10M,050f=40M, 051f=80MHz +return( inport(0xffc8) ); // counter +} + +// +// unsigned int dma_write_i2chip (unsigned int src_segm, unsigned int src_offs, +// unsigned int cnt, unsigned int i2_segm, unsigned int i2_offs); +// Using DMA0 to write data from memory into i2chip. +// where: +// unsigned int cnt = number of 16-bit DMA transfers +// unsigned int src_segm = segment of the source SRAM data memory +// unsigned int src_offs = offset of the source SRAM data memory +// unsigned int i2_segm = segment of i2chip buffer mapped in memory +// unsigned int i2_offs = offset of i2chip buffer mapped in memory +// return DMA counter value +// +unsigned int dma_write_i2chip(u_char far* src, u_char far* i2_dest, u_int cnt) +{ + u_int src_segm, src_offs; + u_int i2_segm, i2_offs; + u_long temp; + + temp = (FP_SEG(src) << 4) + (FP_OFF(src)); + src_segm = (u_int)(temp >> 4); + src_offs = (u_int)(temp & 0xffff); + + temp = (FP_SEG(i2_dest) << 4) + (FP_OFF(i2_dest)); + i2_segm = (u_int)(temp >> 4); + i2_offs = (u_int)(temp & 0xffff); + + outport(0xffc8, cnt); // D0TC counter + outport(0xffc6, i2_segm); // D0DSTH=i2chip buffer segment + outport(0xffc4, i2_offs); // D0DSTL=i2chip buffer offset + outport(0xffc2, src_segm); /* D0SRCH=SP0RD */ + outport(0xffc0, src_offs); /* D0SRCL=SP0RD */ +// outport(0xfff8,0x050f); // PLLCON, 0203=10M,050f=40M, 051f=80MHz +// DMA0 mem-mem, 16-bit, unsync, Start moving data line below + outport(0xffca, 0xb60f); /* D0CON 1011 0110 0000 1111 */ + while( inport(0xffc8) ); /* D0TC counter=0, DMA complete */ +// outport(0xfff8,0x051f); // PLLCON, 0203=10M,050f=40M, 051f=80MHz + +return( inport(0xffc8) ); // counter +} + +/* +**************************************************************************************************** +* Copies the receive buffer data of the W3100A to the system buffer. +* +* Description : Copies the receive buffer data of the W3100A to the system buffer. +* It is called from the recv()or recvfrom() function. +* Arguments : s - channel number +* src - receive buffer pointer of W3100A +* dst - system buffer pointer +* len - data size to copy +* Returns : copied data size +* Note : Internal Function +**************************************************************************************************** +*/ +u_int read_data(SOCKET s, u_int offset, u_char far * dst, u_int len) +{ + u_int i, size, size1; + u_char far* src = (u_char far*)(MK_FP_WINDOW(RECV_DATA_BUF, + RBUFBASEADDRESS[s] + offset)); +// src = (u_char far*)(MK_FP_WINDOW(RECV_DATA_BUF, +// 0)); + + if (len == 0) + { + WINDOW_RESTORE_BASE; // Needed whenever we do a call to MK_FP_WINDOW. + return 0; + } + + if ((offset + len) > RSIZE[s]) + { + size = (u_int)(RSIZE[s] - offset); + + if (size > TERN_RDMA_THRES) + { + dma_read_i2chip(src, dst, size); + } + else + { + for (i = 0; i < size; i++) + { + *dst++ = READ_VALUE(src); + WINDOW_PTR_INC(src); + + } + } + + size1 = len - size; + src = (u_char far *)(MK_FP_WINDOW(RECV_DATA_BUF, (RBUFBASEADDRESS[s]))); + + if (size1 > TERN_RDMA_THRES) + { + dma_read_i2chip(src, dst, size); + } + else + { + for (i = 0; i < size1; i++) + { + *dst++ = READ_VALUE(src); + WINDOW_PTR_INC(src); + } + } + } + else + { + if (len > TERN_RDMA_THRES) + { + dma_read_i2chip(src, dst, size); + } + else + { + for (i = 0; i < len; i++) + { + *dst++ = READ_VALUE(src); + WINDOW_PTR_INC(src); + } + } + } + WINDOW_RESTORE_BASE; // Needed whenever we do a call to MK_FP_WINDOW. + return len; +} + + +/* +**************************************************************************************************** +* Copies the system buffer data to the transmit buffer of the W3100A. +* +* Description : Copies the system buffer data to the transmit buffer of the W3100A. +* It is called from the send_in()or sendto_in() function. +* Arguments : s - channel number +* src - system buffer pointer +* dst - send buffer pointer of W3100A +* len - data size to copy +* Returns : copied data size +* Note : Internal Function +**************************************************************************************************** +*/ +u_int write_data(SOCKET s, u_char far * src, u_int offset, u_int len) +{ + u_int i, size, size1; + u_char far* dst = (u_char far*)MK_FP_WINDOW(SEND_DATA_BUF, + SBUFBASEADDRESS[s] + offset); + + if (len == 0) + { + WINDOW_RESTORE_BASE; // Needed whenever we do a call to MK_FP_WINDOW. + return 0; + } + + if ((offset + len) > SSIZE[s]) + { + size = (u_int)(SSIZE[s] - offset); + + for (i = 0; i < size; i++) + { + WRITE_VALUE(dst, *src++); + WINDOW_PTR_INC(dst); + } + + size1 = len - size; + dst = (u_char far *)(MK_FP_WINDOW(SEND_DATA_BUF, (SBUFBASEADDRESS[s]))); + + for (i = 0; i < size1; i++) + { + WRITE_VALUE(dst, *src++); + WINDOW_PTR_INC(dst); + } + } + else + { + for (i = 0; i < len; i++) + { + WRITE_VALUE(dst, *src++); + WINDOW_PTR_INC(dst); + } + } + WINDOW_RESTORE_BASE; // Needed whenever we do a call to MK_FP_WINDOW. + return len; +} + + + -- 2.39.2