Pavel S / UIPEthernet

Fork of UIPEthernet by Zoltan Hudak

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UIPEthernet.cpp Source File

UIPEthernet.cpp

00001 /*
00002  UIPEthernet.cpp - Arduino implementation of a UIP wrapper class.
00003  Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
00004  All rights reserved.
00005 
00006  Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com>
00007 
00008  This program is free software: you can redistribute it and/or modify
00009  it under the terms of the GNU General Public License as published by
00010  the Free Software Foundation, either version 3 of the License, or
00011  (at your option) any later version.
00012 
00013  This program is distributed in the hope that it will be useful,
00014  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  GNU General Public License for more details.
00017 
00018  You should have received a copy of the GNU General Public License
00019  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020   */
00021 #include "mbed.h"
00022 #include "UIPEthernet.h"
00023 #include "utility/Enc28J60Network.h"
00024 #include "UIPUdp.h"
00025 
00026 extern "C"
00027 {
00028 #include "utility/uip-conf.h"
00029 #include "utility/uip.h"
00030 #include "utility/uip_arp.h"
00031 #include "utility/uip_timer.h"
00032 #include "utility/millis.h"
00033 }
00034 #define ETH_HDR ((struct uip_eth_hdr*) &uip_buf[0])
00035 
00036 memhandle       UIPEthernet::in_packet(NOBLOCK);
00037 memhandle       UIPEthernet::uip_packet(NOBLOCK);
00038 uint8_t         UIPEthernet::uip_hdrlen(0);
00039 uint8_t         UIPEthernet::packetstate(0);
00040 
00041 IPAddress       UIPEthernet::_dnsServerAddress;
00042 DhcpClass*      UIPEthernet::_dhcp(NULL);
00043 
00044 unsigned long   UIPEthernet::periodic_timer;
00045 
00046 /**
00047  * @brief
00048  * @note
00049  * @param
00050  * @retval
00051  */
00052 void enc28J60_mempool_block_move_callback(memaddress dest, memaddress src, memaddress len) {
00053 
00054     //as ENC28J60 DMA is unable to copy single bytes:
00055 
00056     if (len == 1) {
00057         uIPEthernet.network.writeByte(dest, uIPEthernet.network.readByte(src));
00058     }
00059     else {
00060 
00061         // calculate address of last byte
00062         len += src - 1;
00063 
00064         /* 1. Appropriately program the EDMAST, EDMAND
00065               and EDMADST register pairs. The EDMAST
00066               registers should point to the first byte to copy
00067               from, the EDMAND registers should point to the
00068               last byte to copy and the EDMADST registers
00069               should point to the first byte in the destination
00070               range. The destination range will always be
00071               linear, never wrapping at any values except from
00072               8191 to 0 (the 8-Kbyte memory boundary).
00073               Extreme care should be taken when
00074               programming the start and end pointers to
00075               prevent a never ending DMA operation which
00076               would overwrite the entire 8-Kbyte buffer.
00077        */
00078         uIPEthernet.network.writeRegPair(EDMASTL, src);
00079         uIPEthernet.network.writeRegPair(EDMADSTL, dest);
00080 
00081         if ((src <= RXSTOP_INIT) && (len > RXSTOP_INIT))
00082             len -= (RXSTOP_INIT - RXSTART_INIT);
00083         uIPEthernet.network.writeRegPair(EDMANDL, len);
00084 
00085         /* 2. If an interrupt at the end of the copy process is
00086               desired, set EIE.DMAIE and EIE.INTIE and
00087               clear EIR.DMAIF.
00088 
00089            3. Verify that ECON1.CSUMEN is clear. */
00090         uIPEthernet.network.writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN);
00091 
00092         /* 4. Start the DMA copy by setting ECON1.DMAST. */
00093         uIPEthernet.network.writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST);
00094 
00095         //    wait until runnig DMA is completed
00096         while (uIPEthernet.network.readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
00097     }
00098 }
00099 
00100 /*
00101  * Because UIP isn't encapsulated within a class we have to use global
00102  * variables, so we can only have one TCP/IP stack per program.
00103  */
00104 UIPEthernet::UIPEthernet(PinName mosi, PinName miso, PinName sck, PinName cs) :
00105     network(mosi, miso, sck, cs) {
00106     millis_start();
00107 }
00108 
00109 #if UIP_UDP
00110 
00111 /**
00112  * @brief
00113  * @note
00114  * @param
00115  * @retval
00116  */
00117 int UIPEthernet::begin(const uint8_t* mac) {
00118     static DhcpClass    s_dhcp;
00119 
00120     _dhcp = &s_dhcp;
00121 
00122     // Initialise the basic info
00123     init(mac);
00124 
00125     // Now try to get our config info from a DHCP server
00126     int ret = _dhcp->beginWithDHCP((uint8_t*)mac);
00127 
00128     if (ret == 1) {
00129 
00130         // We've successfully found a DHCP server and got our configuration info, so set things
00131         // accordingly
00132         configure(_dhcp->getLocalIp(), _dhcp->getDnsServerIp(), _dhcp->getGatewayIp(), _dhcp->getSubnetMask());
00133     }
00134 
00135     return ret;
00136 }
00137 #endif
00138 
00139 /**
00140  * @brief
00141  * @note
00142  * @param
00143  * @retval
00144  */
00145 void UIPEthernet::begin(const uint8_t* mac, IPAddress ip) {
00146     IPAddress   dns = ip;
00147 
00148     dns[3] = 1;
00149     begin(mac, ip, dns);
00150 }
00151 
00152 /**
00153  * @brief
00154  * @note
00155  * @param
00156  * @retval
00157  */
00158 void UIPEthernet::begin(const uint8_t* mac, IPAddress ip, IPAddress dns) {
00159     IPAddress   gateway = ip;
00160 
00161     gateway[3] = 1;
00162     begin(mac, ip, dns, gateway);
00163 }
00164 
00165 /**
00166  * @brief
00167  * @note
00168  * @param
00169  * @retval
00170  */
00171 void UIPEthernet::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway) {
00172     IPAddress   subnet(255, 255, 255, 0);
00173 
00174     begin(mac, ip, dns, gateway, subnet);
00175 }
00176 
00177 /**
00178  * @brief
00179  * @note
00180  * @param
00181  * @retval
00182  */
00183 void UIPEthernet::begin(const uint8_t* mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) {
00184     init(mac);
00185     configure(ip, dns, gateway, subnet);
00186 }
00187 
00188 /**
00189  * @brief
00190  * @note
00191  * @param
00192  * @retval
00193  */
00194 int UIPEthernet::maintain(void) {
00195     tick();
00196 
00197     int rc = DHCP_CHECK_NONE;
00198 
00199 #if UIP_UDP
00200     if (_dhcp != NULL) {
00201 
00202         //we have a pointer to dhcp, use it
00203         rc = _dhcp->checkLease();
00204         switch (rc) {
00205             case DHCP_CHECK_NONE:
00206                 //nothing done
00207                 break;
00208 
00209             case DHCP_CHECK_RENEW_OK:
00210             case DHCP_CHECK_REBIND_OK:
00211                 //we might have got a new IP.
00212                 configure(_dhcp->getLocalIp(), _dhcp->getDnsServerIp(), _dhcp->getGatewayIp(), _dhcp->getSubnetMask());
00213                 break;
00214 
00215             default:
00216                 //this is actually an error, it will retry though
00217                 break;
00218         }
00219     }
00220 
00221     return rc;
00222 #endif
00223 }
00224 
00225 /**
00226  * @brief
00227  * @note
00228  * @param
00229  * @retval
00230  */
00231 IPAddress UIPEthernet::localIP(void) {
00232     uip_ipaddr_t    a;
00233 
00234     uip_gethostaddr(a);
00235     return ip_addr_uip(a);
00236 }
00237 
00238 /**
00239  * @brief
00240  * @note
00241  * @param
00242  * @retval
00243  */
00244 IPAddress UIPEthernet::subnetMask(void) {
00245     uip_ipaddr_t    a;
00246 
00247     uip_getnetmask(a);
00248     return ip_addr_uip(a);
00249 }
00250 
00251 /**
00252  * @brief
00253  * @note
00254  * @param
00255  * @retval
00256  */
00257 IPAddress UIPEthernet::gatewayIP(void) {
00258     uip_ipaddr_t    a;
00259 
00260     uip_getdraddr(a);
00261     return ip_addr_uip(a);
00262 }
00263 
00264 /**
00265  * @brief
00266  * @note
00267  * @param
00268  * @retval
00269  */
00270 IPAddress UIPEthernet::dnsServerIP(void) {
00271     return _dnsServerAddress;
00272 }
00273 
00274 /**
00275  * @brief
00276  * @note
00277  * @param
00278  * @retval
00279  */
00280 void UIPEthernet::tick(void) {
00281     if (in_packet == NOBLOCK) {
00282         in_packet = network.receivePacket();
00283 #ifdef UIPETHERNET_DEBUG
00284         if (in_packet != NOBLOCK) {
00285             printf("--------------\r\nreceivePacket: %d\r\n", in_packet);
00286         }
00287 #endif
00288     }
00289 
00290     if (in_packet != NOBLOCK) {
00291         packetstate = UIPETHERNET_FREEPACKET;
00292         uip_len = network.blockSize(in_packet);
00293         if (uip_len > 0) {
00294             network.readPacket(in_packet, 0, (uint8_t*)uip_buf, UIP_BUFSIZE);
00295             if (ETH_HDR->type == HTONS(UIP_ETHTYPE_IP)) {
00296                 uip_packet = in_packet; //required for upper_layer_checksum of in_packet!
00297 #ifdef UIPETHERNET_DEBUG
00298                 printf("readPacket type IP, uip_len: %d\r\n", uip_len);
00299 #endif
00300                 uip_arp_ipin();
00301                 uip_input();
00302                 if (uip_len > 0) {
00303                     uip_arp_out();
00304                     network_send();
00305                 }
00306             }
00307             else
00308             if (ETH_HDR->type == HTONS(UIP_ETHTYPE_ARP))
00309             {
00310 #ifdef UIPETHERNET_DEBUG
00311                 printf("readPacket type ARP, uip_len: %d\r\n", uip_len);
00312 #endif
00313                 uip_arp_arpin();
00314                 if (uip_len > 0) {
00315                     network_send();
00316                 }
00317             }
00318         }
00319 
00320         if (in_packet != NOBLOCK && (packetstate & UIPETHERNET_FREEPACKET))
00321         {
00322 #ifdef UIPETHERNET_DEBUG
00323             printf("freeing packet: %d\r\n", in_packet);
00324 #endif
00325             network.freePacket();
00326             in_packet = NOBLOCK;
00327         }
00328     }
00329 
00330     unsigned long   now = millis();
00331     bool            periodic = (long)(now - periodic_timer) >= 0;
00332 
00333     for (int i = 0; i < UIP_CONNS; i++) {
00334         uip_conn = &uip_conns[i];
00335         if (periodic) {
00336             uip_process(UIP_TIMER);
00337         }
00338         else {
00339             if ((long)(now - ((uip_userdata_t*)uip_conn->appstate)->timer) >= 0)
00340                 uip_process(UIP_POLL_REQUEST);
00341             else
00342                 continue;
00343         }
00344 
00345         // If the above function invocation resulted in data that
00346         // should be sent out on the Enc28J60Network, the global variable
00347         // uip_len is set to a value > 0.
00348         if (uip_len > 0) {
00349             uip_arp_out();
00350             network_send();
00351         }
00352     }
00353 
00354     if (periodic) {
00355         periodic_timer = now + UIP_PERIODIC_TIMER;
00356 #if UIP_UDP
00357         for (int i = 0; i < UIP_UDP_CONNS; i++) {
00358             uip_udp_periodic(i);
00359 
00360             // If the above function invocation resulted in data that
00361             // should be sent out on the Enc28J60Network, the global variable
00362             // uip_len is set to a value > 0.
00363             if (uip_len > 0) {
00364                 UIPUDP::_send((uip_udp_userdata_t *) (uip_udp_conns[i].appstate));
00365             }
00366         }
00367 #endif // UIP_UDP
00368     }
00369 }
00370 
00371 /**
00372  * @brief
00373  * @note
00374  * @param
00375  * @retval
00376  */
00377 bool UIPEthernet::network_send(void) {
00378     if (packetstate & UIPETHERNET_SENDPACKET)
00379     {
00380 #ifdef UIPETHERNET_DEBUG
00381         printf("Enc28J60Network_send uip_packet: %d, hdrlen: %d\r\n", uip_packet, uip_hdrlen);
00382 #endif
00383         uIPEthernet.network.writePacket(uip_packet, 0, uip_buf, uip_hdrlen);
00384         packetstate &= ~UIPETHERNET_SENDPACKET;
00385         goto sendandfree;
00386     }
00387 
00388     uip_packet = Enc28J60Network::allocBlock(uip_len);
00389     if (uip_packet != NOBLOCK)
00390     {
00391 #ifdef UIPETHERNET_DEBUG
00392         printf("Enc28J60Network_send uip_buf (uip_len): %d, packet: %d\r\n", uip_len, uip_packet);
00393 #endif
00394         uIPEthernet.network.writePacket(uip_packet, 0, uip_buf, uip_len);
00395         goto sendandfree;
00396     }
00397 
00398     return false;
00399 sendandfree:
00400     network.sendPacket(uip_packet);
00401     Enc28J60Network::freeBlock(uip_packet);
00402     uip_packet = NOBLOCK;
00403     return true;
00404 }
00405 
00406 /**
00407  * @brief
00408  * @note
00409  * @param
00410  * @retval
00411  */
00412 void UIPEthernet::init(const uint8_t* mac) {
00413     periodic_timer = millis() + UIP_PERIODIC_TIMER;
00414 
00415     network.init((uint8_t*)mac);
00416     uip_seteth_addr(mac);
00417 
00418     uip_init();
00419     uip_arp_init();
00420 }
00421 
00422 /**
00423  * @brief
00424  * @note
00425  * @param
00426  * @retval
00427  */
00428 void UIPEthernet::configure(IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet) {
00429     uip_ipaddr_t    ipaddr;
00430 
00431     uip_ip_addr(ipaddr, ip);
00432     uip_sethostaddr(ipaddr);
00433 
00434     uip_ip_addr(ipaddr, gateway);
00435     uip_setdraddr(ipaddr);
00436 
00437     uip_ip_addr(ipaddr, subnet);
00438     uip_setnetmask(ipaddr);
00439 
00440     _dnsServerAddress = dns;
00441 }
00442 
00443 /**
00444  * @brief
00445  * @note
00446  * @param
00447  * @retval
00448  */
00449 uint16_t UIPEthernet::chksum(uint16_t sum, const uint8_t* data, uint16_t len) {
00450     uint16_t        t;
00451     const uint8_t*  dataptr;
00452     const uint8_t*  last_byte;
00453 
00454     dataptr = data;
00455     last_byte = data + len - 1;
00456 
00457     while (dataptr < last_byte) {
00458 
00459         /* At least two more bytes */
00460         t = (dataptr[0] << 8) + dataptr[1];
00461         sum += t;
00462         if (sum < t) {
00463             sum++;  /* carry */
00464         }
00465 
00466         dataptr += 2;
00467     }
00468 
00469     if (dataptr == last_byte) {
00470         t = (dataptr[0] << 8) + 0;
00471         sum += t;
00472         if (sum < t) {
00473             sum++;  /* carry */
00474         }
00475     }
00476 
00477     /* Return sum in host byte order. */
00478     return sum;
00479 }
00480 
00481 /**
00482  * @brief
00483  * @note
00484  * @param
00485  * @retval
00486  */
00487 uint16_t UIPEthernet::ipchksum(void) {
00488     uint16_t    sum;
00489 
00490     sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN);
00491     return(sum == 0) ? 0xffff : htons(sum);
00492 }
00493 
00494 uint16_t
00495 #if UIP_UDP
00496 UIPEthernet::upper_layer_chksum(uint8_t proto)
00497 #else
00498 uip_tcpchksum (void)
00499 #endif
00500 {
00501     uint16_t    upper_layer_len;
00502     uint16_t    sum;
00503 
00504 #if UIP_CONF_IPV6
00505     upper_layer_len = (((u16_t) (BUF->len[0]) << 8) + BUF->len[1]);
00506 #else /* UIP_CONF_IPV6 */
00507     upper_layer_len = (((u16_t) (BUF->len[0]) << 8) + BUF->len[1]) - UIP_IPH_LEN;
00508 #endif /* UIP_CONF_IPV6 */
00509 
00510     /* First sum pseudoheader. */
00511 
00512     /* IP protocol and length fields. This addition cannot carry. */
00513 #if UIP_UDP
00514     sum = upper_layer_len + proto;
00515 #else
00516     sum = upper_layer_len + UIP_PROTO_TCP;
00517 #endif
00518     /* Sum IP source and destination addresses. */
00519 
00520     sum = UIPEthernet::chksum(sum, (u8_t*) &BUF->srcipaddr[0], 2 * sizeof(uip_ipaddr_t));
00521 
00522     uint8_t upper_layer_memlen;
00523 #if UIP_UDP
00524     switch (proto) {
00525         //    case UIP_PROTO_ICMP:
00526         //    case UIP_PROTO_ICMP6:
00527         //      upper_layer_memlen = upper_layer_len;
00528         //      break;
00529         case UIP_PROTO_UDP:
00530             upper_layer_memlen = UIP_UDPH_LEN;
00531             break;
00532 
00533         default:
00534             //  case UIP_PROTO_TCP:
00535     #endif
00536             upper_layer_memlen = (BUF->tcpoffset >> 4) << 2;
00537     #if UIP_UDP
00538             break;
00539     }
00540 #endif
00541     sum = UIPEthernet::chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN], upper_layer_memlen);
00542 #ifdef UIPETHERNET_DEBUG_CHKSUM
00543     printf("chksum uip_buf[%d-%d]: %d\r\n", UIP_IPH_LEN + UIP_LLH_LEN, UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen, htons(sum));
00544 #endif
00545     if (upper_layer_memlen < upper_layer_len) {
00546         sum = network.chksum
00547             (
00548                 sum, UIPEthernet::uip_packet, UIP_IPH_LEN +
00549                 UIP_LLH_LEN +
00550                 upper_layer_memlen, upper_layer_len -
00551                 upper_layer_memlen
00552             );
00553 #ifdef UIPETHERNET_DEBUG_CHKSUM
00554         printf("chksum uip_packet(%d)[%d-%d]: %d\r\n", uip_packet, UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_memlen, UIP_IPH_LEN + UIP_LLH_LEN + upper_layer_len, htons(sum));
00555 #endif
00556     }
00557     return(sum == 0) ? 0xffff : htons(sum);
00558 }
00559 
00560 /**
00561  * @brief
00562  * @note
00563  * @param
00564  * @retval
00565  */
00566 uint16_t uip_ipchksum(void) {
00567     return uIPEthernet.ipchksum();
00568 }
00569 
00570 #if UIP_UDP
00571 
00572 /**
00573  * @brief
00574  * @note
00575  * @param
00576  * @retval
00577  */
00578 uint16_t uip_tcpchksum(void) {
00579     uint16_t    sum = uIPEthernet.upper_layer_chksum(UIP_PROTO_TCP);
00580 
00581     return sum;
00582 }
00583 
00584 /**
00585  * @brief
00586  * @note
00587  * @param
00588  * @retval
00589  */
00590 uint16_t uip_udpchksum(void) {
00591     uint16_t    sum = uIPEthernet.upper_layer_chksum(UIP_PROTO_UDP);
00592 
00593     return sum;
00594 }
00595 #endif
00596