Onenet

Dependents:   K64F_eCompass_OneNET_JW

Committer:
robert_jw
Date:
Mon Jun 20 01:40:20 2016 +0000
Revision:
0:b2805b6888dc
ADS

Who changed what in which revision?

UserRevisionLine numberNew contents of line
robert_jw 0:b2805b6888dc 1 /**
robert_jw 0:b2805b6888dc 2 * @file
robert_jw 0:b2805b6888dc 3 * Ethernet Interface Skeleton
robert_jw 0:b2805b6888dc 4 *
robert_jw 0:b2805b6888dc 5 */
robert_jw 0:b2805b6888dc 6
robert_jw 0:b2805b6888dc 7 /*
robert_jw 0:b2805b6888dc 8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
robert_jw 0:b2805b6888dc 9 * All rights reserved.
robert_jw 0:b2805b6888dc 10 *
robert_jw 0:b2805b6888dc 11 * Redistribution and use in source and binary forms, with or without modification,
robert_jw 0:b2805b6888dc 12 * are permitted provided that the following conditions are met:
robert_jw 0:b2805b6888dc 13 *
robert_jw 0:b2805b6888dc 14 * 1. Redistributions of source code must retain the above copyright notice,
robert_jw 0:b2805b6888dc 15 * this list of conditions and the following disclaimer.
robert_jw 0:b2805b6888dc 16 * 2. Redistributions in binary form must reproduce the above copyright notice,
robert_jw 0:b2805b6888dc 17 * this list of conditions and the following disclaimer in the documentation
robert_jw 0:b2805b6888dc 18 * and/or other materials provided with the distribution.
robert_jw 0:b2805b6888dc 19 * 3. The name of the author may not be used to endorse or promote products
robert_jw 0:b2805b6888dc 20 * derived from this software without specific prior written permission.
robert_jw 0:b2805b6888dc 21 *
robert_jw 0:b2805b6888dc 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
robert_jw 0:b2805b6888dc 23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
robert_jw 0:b2805b6888dc 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
robert_jw 0:b2805b6888dc 25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
robert_jw 0:b2805b6888dc 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
robert_jw 0:b2805b6888dc 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
robert_jw 0:b2805b6888dc 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
robert_jw 0:b2805b6888dc 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
robert_jw 0:b2805b6888dc 30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
robert_jw 0:b2805b6888dc 31 * OF SUCH DAMAGE.
robert_jw 0:b2805b6888dc 32 *
robert_jw 0:b2805b6888dc 33 * This file is part of the lwIP TCP/IP stack.
robert_jw 0:b2805b6888dc 34 *
robert_jw 0:b2805b6888dc 35 * Author: Adam Dunkels <adam@sics.se>
robert_jw 0:b2805b6888dc 36 *
robert_jw 0:b2805b6888dc 37 */
robert_jw 0:b2805b6888dc 38
robert_jw 0:b2805b6888dc 39 /*
robert_jw 0:b2805b6888dc 40 * This file is a skeleton for developing Ethernet network interface
robert_jw 0:b2805b6888dc 41 * drivers for lwIP. Add code to the low_level functions and do a
robert_jw 0:b2805b6888dc 42 * search-and-replace for the word "ethernetif" to replace it with
robert_jw 0:b2805b6888dc 43 * something that better describes your network interface.
robert_jw 0:b2805b6888dc 44 */
robert_jw 0:b2805b6888dc 45
robert_jw 0:b2805b6888dc 46 #include "lwip/opt.h"
robert_jw 0:b2805b6888dc 47
robert_jw 0:b2805b6888dc 48 #if 0 /* don't build, this is only a skeleton, see previous comment */
robert_jw 0:b2805b6888dc 49
robert_jw 0:b2805b6888dc 50 #include "lwip/def.h"
robert_jw 0:b2805b6888dc 51 #include "lwip/mem.h"
robert_jw 0:b2805b6888dc 52 #include "lwip/pbuf.h"
robert_jw 0:b2805b6888dc 53 #include "lwip/sys.h"
robert_jw 0:b2805b6888dc 54 #include <lwip/stats.h>
robert_jw 0:b2805b6888dc 55 #include <lwip/snmp.h>
robert_jw 0:b2805b6888dc 56 #include "netif/etharp.h"
robert_jw 0:b2805b6888dc 57 #include "netif/ppp_oe.h"
robert_jw 0:b2805b6888dc 58
robert_jw 0:b2805b6888dc 59 /* Define those to better describe your network interface. */
robert_jw 0:b2805b6888dc 60 #define IFNAME0 'e'
robert_jw 0:b2805b6888dc 61 #define IFNAME1 'n'
robert_jw 0:b2805b6888dc 62
robert_jw 0:b2805b6888dc 63 /**
robert_jw 0:b2805b6888dc 64 * Helper struct to hold private data used to operate your ethernet interface.
robert_jw 0:b2805b6888dc 65 * Keeping the ethernet address of the MAC in this struct is not necessary
robert_jw 0:b2805b6888dc 66 * as it is already kept in the struct netif.
robert_jw 0:b2805b6888dc 67 * But this is only an example, anyway...
robert_jw 0:b2805b6888dc 68 */
robert_jw 0:b2805b6888dc 69 struct ethernetif {
robert_jw 0:b2805b6888dc 70 struct eth_addr *ethaddr;
robert_jw 0:b2805b6888dc 71 /* Add whatever per-interface state that is needed here. */
robert_jw 0:b2805b6888dc 72 };
robert_jw 0:b2805b6888dc 73
robert_jw 0:b2805b6888dc 74 /* Forward declarations. */
robert_jw 0:b2805b6888dc 75 static void ethernetif_input(struct netif *netif);
robert_jw 0:b2805b6888dc 76
robert_jw 0:b2805b6888dc 77 /**
robert_jw 0:b2805b6888dc 78 * In this function, the hardware should be initialized.
robert_jw 0:b2805b6888dc 79 * Called from ethernetif_init().
robert_jw 0:b2805b6888dc 80 *
robert_jw 0:b2805b6888dc 81 * @param netif the already initialized lwip network interface structure
robert_jw 0:b2805b6888dc 82 * for this ethernetif
robert_jw 0:b2805b6888dc 83 */
robert_jw 0:b2805b6888dc 84 static void
robert_jw 0:b2805b6888dc 85 low_level_init(struct netif *netif)
robert_jw 0:b2805b6888dc 86 {
robert_jw 0:b2805b6888dc 87 struct ethernetif *ethernetif = netif->state;
robert_jw 0:b2805b6888dc 88
robert_jw 0:b2805b6888dc 89 /* set MAC hardware address length */
robert_jw 0:b2805b6888dc 90 netif->hwaddr_len = ETHARP_HWADDR_LEN;
robert_jw 0:b2805b6888dc 91
robert_jw 0:b2805b6888dc 92 /* set MAC hardware address */
robert_jw 0:b2805b6888dc 93 netif->hwaddr[0] = ;
robert_jw 0:b2805b6888dc 94 ...
robert_jw 0:b2805b6888dc 95 netif->hwaddr[5] = ;
robert_jw 0:b2805b6888dc 96
robert_jw 0:b2805b6888dc 97 /* maximum transfer unit */
robert_jw 0:b2805b6888dc 98 netif->mtu = 1500;
robert_jw 0:b2805b6888dc 99
robert_jw 0:b2805b6888dc 100 /* device capabilities */
robert_jw 0:b2805b6888dc 101 /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
robert_jw 0:b2805b6888dc 102 netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
robert_jw 0:b2805b6888dc 103
robert_jw 0:b2805b6888dc 104 /* Do whatever else is needed to initialize interface. */
robert_jw 0:b2805b6888dc 105 }
robert_jw 0:b2805b6888dc 106
robert_jw 0:b2805b6888dc 107 /**
robert_jw 0:b2805b6888dc 108 * This function should do the actual transmission of the packet. The packet is
robert_jw 0:b2805b6888dc 109 * contained in the pbuf that is passed to the function. This pbuf
robert_jw 0:b2805b6888dc 110 * might be chained.
robert_jw 0:b2805b6888dc 111 *
robert_jw 0:b2805b6888dc 112 * @param netif the lwip network interface structure for this ethernetif
robert_jw 0:b2805b6888dc 113 * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
robert_jw 0:b2805b6888dc 114 * @return ERR_OK if the packet could be sent
robert_jw 0:b2805b6888dc 115 * an err_t value if the packet couldn't be sent
robert_jw 0:b2805b6888dc 116 *
robert_jw 0:b2805b6888dc 117 * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
robert_jw 0:b2805b6888dc 118 * strange results. You might consider waiting for space in the DMA queue
robert_jw 0:b2805b6888dc 119 * to become availale since the stack doesn't retry to send a packet
robert_jw 0:b2805b6888dc 120 * dropped because of memory failure (except for the TCP timers).
robert_jw 0:b2805b6888dc 121 */
robert_jw 0:b2805b6888dc 122
robert_jw 0:b2805b6888dc 123 static err_t
robert_jw 0:b2805b6888dc 124 low_level_output(struct netif *netif, struct pbuf *p)
robert_jw 0:b2805b6888dc 125 {
robert_jw 0:b2805b6888dc 126 struct ethernetif *ethernetif = netif->state;
robert_jw 0:b2805b6888dc 127 struct pbuf *q;
robert_jw 0:b2805b6888dc 128
robert_jw 0:b2805b6888dc 129 initiate transfer();
robert_jw 0:b2805b6888dc 130
robert_jw 0:b2805b6888dc 131 #if ETH_PAD_SIZE
robert_jw 0:b2805b6888dc 132 pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
robert_jw 0:b2805b6888dc 133 #endif
robert_jw 0:b2805b6888dc 134
robert_jw 0:b2805b6888dc 135 for(q = p; q != NULL; q = q->next) {
robert_jw 0:b2805b6888dc 136 /* Send the data from the pbuf to the interface, one pbuf at a
robert_jw 0:b2805b6888dc 137 time. The size of the data in each pbuf is kept in the ->len
robert_jw 0:b2805b6888dc 138 variable. */
robert_jw 0:b2805b6888dc 139 send data from(q->payload, q->len);
robert_jw 0:b2805b6888dc 140 }
robert_jw 0:b2805b6888dc 141
robert_jw 0:b2805b6888dc 142 signal that packet should be sent();
robert_jw 0:b2805b6888dc 143
robert_jw 0:b2805b6888dc 144 #if ETH_PAD_SIZE
robert_jw 0:b2805b6888dc 145 pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
robert_jw 0:b2805b6888dc 146 #endif
robert_jw 0:b2805b6888dc 147
robert_jw 0:b2805b6888dc 148 LINK_STATS_INC(link.xmit);
robert_jw 0:b2805b6888dc 149
robert_jw 0:b2805b6888dc 150 return ERR_OK;
robert_jw 0:b2805b6888dc 151 }
robert_jw 0:b2805b6888dc 152
robert_jw 0:b2805b6888dc 153 /**
robert_jw 0:b2805b6888dc 154 * Should allocate a pbuf and transfer the bytes of the incoming
robert_jw 0:b2805b6888dc 155 * packet from the interface into the pbuf.
robert_jw 0:b2805b6888dc 156 *
robert_jw 0:b2805b6888dc 157 * @param netif the lwip network interface structure for this ethernetif
robert_jw 0:b2805b6888dc 158 * @return a pbuf filled with the received packet (including MAC header)
robert_jw 0:b2805b6888dc 159 * NULL on memory error
robert_jw 0:b2805b6888dc 160 */
robert_jw 0:b2805b6888dc 161 static struct pbuf *
robert_jw 0:b2805b6888dc 162 low_level_input(struct netif *netif)
robert_jw 0:b2805b6888dc 163 {
robert_jw 0:b2805b6888dc 164 struct ethernetif *ethernetif = netif->state;
robert_jw 0:b2805b6888dc 165 struct pbuf *p, *q;
robert_jw 0:b2805b6888dc 166 u16_t len;
robert_jw 0:b2805b6888dc 167
robert_jw 0:b2805b6888dc 168 /* Obtain the size of the packet and put it into the "len"
robert_jw 0:b2805b6888dc 169 variable. */
robert_jw 0:b2805b6888dc 170 len = ;
robert_jw 0:b2805b6888dc 171
robert_jw 0:b2805b6888dc 172 #if ETH_PAD_SIZE
robert_jw 0:b2805b6888dc 173 len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
robert_jw 0:b2805b6888dc 174 #endif
robert_jw 0:b2805b6888dc 175
robert_jw 0:b2805b6888dc 176 /* We allocate a pbuf chain of pbufs from the pool. */
robert_jw 0:b2805b6888dc 177 p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
robert_jw 0:b2805b6888dc 178
robert_jw 0:b2805b6888dc 179 if (p != NULL) {
robert_jw 0:b2805b6888dc 180
robert_jw 0:b2805b6888dc 181 #if ETH_PAD_SIZE
robert_jw 0:b2805b6888dc 182 pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
robert_jw 0:b2805b6888dc 183 #endif
robert_jw 0:b2805b6888dc 184
robert_jw 0:b2805b6888dc 185 /* We iterate over the pbuf chain until we have read the entire
robert_jw 0:b2805b6888dc 186 * packet into the pbuf. */
robert_jw 0:b2805b6888dc 187 for(q = p; q != NULL; q = q->next) {
robert_jw 0:b2805b6888dc 188 /* Read enough bytes to fill this pbuf in the chain. The
robert_jw 0:b2805b6888dc 189 * available data in the pbuf is given by the q->len
robert_jw 0:b2805b6888dc 190 * variable.
robert_jw 0:b2805b6888dc 191 * This does not necessarily have to be a memcpy, you can also preallocate
robert_jw 0:b2805b6888dc 192 * pbufs for a DMA-enabled MAC and after receiving truncate it to the
robert_jw 0:b2805b6888dc 193 * actually received size. In this case, ensure the tot_len member of the
robert_jw 0:b2805b6888dc 194 * pbuf is the sum of the chained pbuf len members.
robert_jw 0:b2805b6888dc 195 */
robert_jw 0:b2805b6888dc 196 read data into(q->payload, q->len);
robert_jw 0:b2805b6888dc 197 }
robert_jw 0:b2805b6888dc 198 acknowledge that packet has been read();
robert_jw 0:b2805b6888dc 199
robert_jw 0:b2805b6888dc 200 #if ETH_PAD_SIZE
robert_jw 0:b2805b6888dc 201 pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
robert_jw 0:b2805b6888dc 202 #endif
robert_jw 0:b2805b6888dc 203
robert_jw 0:b2805b6888dc 204 LINK_STATS_INC(link.recv);
robert_jw 0:b2805b6888dc 205 } else {
robert_jw 0:b2805b6888dc 206 drop packet();
robert_jw 0:b2805b6888dc 207 LINK_STATS_INC(link.memerr);
robert_jw 0:b2805b6888dc 208 LINK_STATS_INC(link.drop);
robert_jw 0:b2805b6888dc 209 }
robert_jw 0:b2805b6888dc 210
robert_jw 0:b2805b6888dc 211 return p;
robert_jw 0:b2805b6888dc 212 }
robert_jw 0:b2805b6888dc 213
robert_jw 0:b2805b6888dc 214 /**
robert_jw 0:b2805b6888dc 215 * This function should be called when a packet is ready to be read
robert_jw 0:b2805b6888dc 216 * from the interface. It uses the function low_level_input() that
robert_jw 0:b2805b6888dc 217 * should handle the actual reception of bytes from the network
robert_jw 0:b2805b6888dc 218 * interface. Then the type of the received packet is determined and
robert_jw 0:b2805b6888dc 219 * the appropriate input function is called.
robert_jw 0:b2805b6888dc 220 *
robert_jw 0:b2805b6888dc 221 * @param netif the lwip network interface structure for this ethernetif
robert_jw 0:b2805b6888dc 222 */
robert_jw 0:b2805b6888dc 223 static void
robert_jw 0:b2805b6888dc 224 ethernetif_input(struct netif *netif)
robert_jw 0:b2805b6888dc 225 {
robert_jw 0:b2805b6888dc 226 struct ethernetif *ethernetif;
robert_jw 0:b2805b6888dc 227 struct eth_hdr *ethhdr;
robert_jw 0:b2805b6888dc 228 struct pbuf *p;
robert_jw 0:b2805b6888dc 229
robert_jw 0:b2805b6888dc 230 ethernetif = netif->state;
robert_jw 0:b2805b6888dc 231
robert_jw 0:b2805b6888dc 232 /* move received packet into a new pbuf */
robert_jw 0:b2805b6888dc 233 p = low_level_input(netif);
robert_jw 0:b2805b6888dc 234 /* no packet could be read, silently ignore this */
robert_jw 0:b2805b6888dc 235 if (p == NULL) return;
robert_jw 0:b2805b6888dc 236 /* points to packet payload, which starts with an Ethernet header */
robert_jw 0:b2805b6888dc 237 ethhdr = p->payload;
robert_jw 0:b2805b6888dc 238
robert_jw 0:b2805b6888dc 239 switch (htons(ethhdr->type)) {
robert_jw 0:b2805b6888dc 240 /* IP or ARP packet? */
robert_jw 0:b2805b6888dc 241 case ETHTYPE_IP:
robert_jw 0:b2805b6888dc 242 case ETHTYPE_ARP:
robert_jw 0:b2805b6888dc 243 #if PPPOE_SUPPORT
robert_jw 0:b2805b6888dc 244 /* PPPoE packet? */
robert_jw 0:b2805b6888dc 245 case ETHTYPE_PPPOEDISC:
robert_jw 0:b2805b6888dc 246 case ETHTYPE_PPPOE:
robert_jw 0:b2805b6888dc 247 #endif /* PPPOE_SUPPORT */
robert_jw 0:b2805b6888dc 248 /* full packet send to tcpip_thread to process */
robert_jw 0:b2805b6888dc 249 if (netif->input(p, netif)!=ERR_OK)
robert_jw 0:b2805b6888dc 250 { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
robert_jw 0:b2805b6888dc 251 pbuf_free(p);
robert_jw 0:b2805b6888dc 252 p = NULL;
robert_jw 0:b2805b6888dc 253 }
robert_jw 0:b2805b6888dc 254 break;
robert_jw 0:b2805b6888dc 255
robert_jw 0:b2805b6888dc 256 default:
robert_jw 0:b2805b6888dc 257 pbuf_free(p);
robert_jw 0:b2805b6888dc 258 p = NULL;
robert_jw 0:b2805b6888dc 259 break;
robert_jw 0:b2805b6888dc 260 }
robert_jw 0:b2805b6888dc 261 }
robert_jw 0:b2805b6888dc 262
robert_jw 0:b2805b6888dc 263 /**
robert_jw 0:b2805b6888dc 264 * Should be called at the beginning of the program to set up the
robert_jw 0:b2805b6888dc 265 * network interface. It calls the function low_level_init() to do the
robert_jw 0:b2805b6888dc 266 * actual setup of the hardware.
robert_jw 0:b2805b6888dc 267 *
robert_jw 0:b2805b6888dc 268 * This function should be passed as a parameter to netif_add().
robert_jw 0:b2805b6888dc 269 *
robert_jw 0:b2805b6888dc 270 * @param netif the lwip network interface structure for this ethernetif
robert_jw 0:b2805b6888dc 271 * @return ERR_OK if the loopif is initialized
robert_jw 0:b2805b6888dc 272 * ERR_MEM if private data couldn't be allocated
robert_jw 0:b2805b6888dc 273 * any other err_t on error
robert_jw 0:b2805b6888dc 274 */
robert_jw 0:b2805b6888dc 275 err_t
robert_jw 0:b2805b6888dc 276 ethernetif_init(struct netif *netif)
robert_jw 0:b2805b6888dc 277 {
robert_jw 0:b2805b6888dc 278 struct ethernetif *ethernetif;
robert_jw 0:b2805b6888dc 279
robert_jw 0:b2805b6888dc 280 LWIP_ASSERT("netif != NULL", (netif != NULL));
robert_jw 0:b2805b6888dc 281
robert_jw 0:b2805b6888dc 282 ethernetif = mem_malloc(sizeof(struct ethernetif));
robert_jw 0:b2805b6888dc 283 if (ethernetif == NULL) {
robert_jw 0:b2805b6888dc 284 LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
robert_jw 0:b2805b6888dc 285 return ERR_MEM;
robert_jw 0:b2805b6888dc 286 }
robert_jw 0:b2805b6888dc 287
robert_jw 0:b2805b6888dc 288 #if LWIP_NETIF_HOSTNAME
robert_jw 0:b2805b6888dc 289 /* Initialize interface hostname */
robert_jw 0:b2805b6888dc 290 netif->hostname = "lwip";
robert_jw 0:b2805b6888dc 291 #endif /* LWIP_NETIF_HOSTNAME */
robert_jw 0:b2805b6888dc 292
robert_jw 0:b2805b6888dc 293 /*
robert_jw 0:b2805b6888dc 294 * Initialize the snmp variables and counters inside the struct netif.
robert_jw 0:b2805b6888dc 295 * The last argument should be replaced with your link speed, in units
robert_jw 0:b2805b6888dc 296 * of bits per second.
robert_jw 0:b2805b6888dc 297 */
robert_jw 0:b2805b6888dc 298 NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
robert_jw 0:b2805b6888dc 299
robert_jw 0:b2805b6888dc 300 netif->state = ethernetif;
robert_jw 0:b2805b6888dc 301 netif->name[0] = IFNAME0;
robert_jw 0:b2805b6888dc 302 netif->name[1] = IFNAME1;
robert_jw 0:b2805b6888dc 303 /* We directly use etharp_output() here to save a function call.
robert_jw 0:b2805b6888dc 304 * You can instead declare your own function an call etharp_output()
robert_jw 0:b2805b6888dc 305 * from it if you have to do some checks before sending (e.g. if link
robert_jw 0:b2805b6888dc 306 * is available...) */
robert_jw 0:b2805b6888dc 307 netif->output = etharp_output;
robert_jw 0:b2805b6888dc 308 netif->linkoutput = low_level_output;
robert_jw 0:b2805b6888dc 309
robert_jw 0:b2805b6888dc 310 ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
robert_jw 0:b2805b6888dc 311
robert_jw 0:b2805b6888dc 312 /* initialize the hardware */
robert_jw 0:b2805b6888dc 313 low_level_init(netif);
robert_jw 0:b2805b6888dc 314
robert_jw 0:b2805b6888dc 315 return ERR_OK;
robert_jw 0:b2805b6888dc 316 }
robert_jw 0:b2805b6888dc 317
robert_jw 0:b2805b6888dc 318 #endif /* 0 */