adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 1 | /* |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 2 | * Copyright (c) 2003, Adam Dunkels. |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 13 | * 3. The name of the author may not be used to endorse or promote |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 14 | * products derived from this software without specific prior |
| 15 | * written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| 18 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| 23 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | * |
| 29 | * This file is part of the uIP TCP/IP stack. |
| 30 | * |
adamdunkels | 0a08fda | 2004-07-04 18:33:07 +0000 | [diff] [blame] | 31 | * $Id: httpd.c,v 1.5 2004/07/04 18:33:07 adamdunkels Exp $ |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 32 | * |
| 33 | */ |
| 34 | |
| 35 | |
| 36 | #include "uip.h" |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 37 | |
| 38 | #include "http-strings.h" |
| 39 | |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 40 | #include "httpd.h" |
| 41 | #include "httpd-fs.h" |
| 42 | #include "httpd-fsdata.h" |
| 43 | #include "httpd-cgi.h" |
| 44 | |
adamdunkels | 0a08fda | 2004-07-04 18:33:07 +0000 | [diff] [blame] | 45 | #include "tcpip.h" |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 46 | |
| 47 | #include <stdio.h> |
| 48 | |
| 49 | /* The HTTP server states: */ |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 50 | #define STATE_DEALLOCATED 0 |
| 51 | #define STATE_INITIAL 1 |
| 52 | #define STATE_GET 2 |
| 53 | #define STATE_HEADERS 3 |
| 54 | #define STATE_SEND_HEADERS 4 |
| 55 | #define STATE_SEND_CONTENT_TYPE 5 |
| 56 | #define STATE_SEND_DATA 6 |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 57 | |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 58 | #define ISO_nl 0x0a |
| 59 | #define ISO_space 0x20 |
| 60 | #define ISO_slash 0x2f |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 61 | |
| 62 | #ifdef DEBUG |
| 63 | #include <stdio.h> |
| 64 | #define PRINT(x) printf("%s", x) |
| 65 | #define PRINTLN(x) printf("%s\n", x) |
| 66 | #else /* DEBUG */ |
| 67 | #define PRINT(x) |
| 68 | #define PRINTLN(x) |
| 69 | #endif /* DEBUG */ |
| 70 | |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 71 | |
| 72 | #define HTTPD_CONF_NUMCONNS 4 |
| 73 | static struct httpd_state conns[HTTPD_CONF_NUMCONNS]; |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 74 | u8_t i; |
| 75 | |
| 76 | struct http_filetype { |
| 77 | const char *ext; |
| 78 | unsigned char extlen; |
| 79 | const char *type; |
| 80 | unsigned char typelen; |
| 81 | }; |
| 82 | |
| 83 | static struct http_filetype filetypes[] = { |
| 84 | {http_html, sizeof(http_html) - 1, |
| 85 | http_content_type_html, sizeof(http_content_type_html) - 1}, |
| 86 | {http_htm, sizeof(http_htm) - 1, |
| 87 | http_content_type_html, sizeof(http_content_type_html) - 1}, |
| 88 | {http_css, sizeof(http_css) - 1, |
| 89 | http_content_type_css, sizeof(http_content_type_css) - 1}, |
| 90 | {http_png, sizeof(http_png) - 1, |
| 91 | http_content_type_png, sizeof(http_content_type_png) - 1}, |
| 92 | {http_gif, sizeof(http_gif) - 1, |
| 93 | http_content_type_gif, sizeof(http_content_type_gif) - 1}, |
| 94 | {http_jpg, sizeof(http_jpg) - 1, |
| 95 | http_content_type_jpg, sizeof(http_content_type_jpg) - 1}, |
| 96 | {http_txt, sizeof(http_txt) - 1, |
| 97 | http_content_type_text, sizeof(http_content_type_text) - 1}, |
| 98 | {http_text, sizeof(http_text) - 1, |
| 99 | http_content_type_text, sizeof(http_content_type_text) - 1}, |
| 100 | {NULL, 0, NULL, 0} |
| 101 | }; |
| 102 | #define NUMFILETYPES (sizeof(filetypes) / sizeof(struct http_filetype)) |
| 103 | /*-----------------------------------------------------------------------------*/ |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 104 | static struct httpd_state * |
| 105 | alloc_state(void) |
| 106 | { |
| 107 | |
| 108 | for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 109 | if(conns[i].state == STATE_DEALLOCATED) { |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 110 | return &conns[i]; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /* We are overloaded! XXX: we'll just kick all other connections! */ |
| 115 | for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 116 | conns[i].state = STATE_DEALLOCATED; |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | return NULL; |
| 120 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 121 | /*-----------------------------------------------------------------------------*/ |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 122 | static void |
| 123 | dealloc_state(struct httpd_state *s) |
| 124 | { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 125 | s->state = STATE_DEALLOCATED; |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 126 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 127 | /*-----------------------------------------------------------------------------*/ |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 128 | void |
| 129 | httpd_init(void) |
| 130 | { |
| 131 | httpd_fs_init(); |
| 132 | |
| 133 | /* Listen to port 80. */ |
adamdunkels | 0a08fda | 2004-07-04 18:33:07 +0000 | [diff] [blame] | 134 | tcp_listen(HTONS(80)); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 135 | |
| 136 | for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 137 | conns[i].state = STATE_DEALLOCATED; |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 138 | } |
| 139 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 140 | /*-----------------------------------------------------------------------------*/ |
| 141 | /** |
| 142 | * \internal |
| 143 | * Buffer an amount of data from the incoming uIP data. |
| 144 | * |
| 145 | * \param buf A pointer to the buffer. |
| 146 | * \param len The length of data to buffer. |
| 147 | * |
| 148 | * \return Zero if the function wasn't able to buffer to buffer all |
| 149 | * data, non-zero otherwise. |
| 150 | */ |
| 151 | /*-----------------------------------------------------------------------------*/ |
| 152 | static unsigned char |
| 153 | buffer(CC_REGISTER_ARG struct httpd_buffer *buf, int len, u8_t c) |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 154 | { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 155 | while(buf->ptr < len && |
| 156 | uip_len > 0) { |
| 157 | buf->buf[buf->ptr] = *uip_appdata; |
| 158 | ++uip_appdata; |
| 159 | --uip_len; |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 160 | |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 161 | if(buf->buf[buf->ptr] == c) { |
| 162 | return 1; |
| 163 | } |
| 164 | ++buf->ptr; |
| 165 | } |
| 166 | return (buf->ptr == len); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 167 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 168 | /*-----------------------------------------------------------------------------*/ |
| 169 | static void |
| 170 | buffer_reset(struct httpd_buffer *buf) |
| 171 | { |
| 172 | buf->ptr = 0; |
| 173 | } |
| 174 | /*-----------------------------------------------------------------------------*/ |
| 175 | static void |
| 176 | newdata(CC_REGISTER_ARG struct httpd_state *hs) |
| 177 | { |
| 178 | int i; |
| 179 | struct http_filetype *filetype; |
| 180 | |
| 181 | while(uip_len > 0) { |
| 182 | switch(hs->state) { |
| 183 | |
| 184 | case STATE_INITIAL: |
| 185 | /* This is the first data that we receive, so we check if we can |
| 186 | identify the "GET" request. We call the buffer() function to |
| 187 | try to buffer 4 bytes of data from the incoming data stream. If |
| 188 | the function isn't able to buffer enough data, we return and |
| 189 | will try again next time around. */ |
| 190 | hs->buf.buf = hs->getbuffer; |
| 191 | if(buffer(&(hs->buf), 4, ISO_space)) { |
| 192 | if(strncmp(http_get, hs->buf.buf, 4) == 0) { |
| 193 | hs->state = STATE_GET; |
| 194 | buffer_reset(&(hs->buf)); |
| 195 | } else { |
| 196 | uip_abort(); |
| 197 | return; |
| 198 | } |
| 199 | } |
| 200 | break; |
| 201 | |
| 202 | case STATE_GET: |
| 203 | /* We will read one byte at a time from the GET request until we |
| 204 | find a en end of the line (\n\r). The GET request is remembered |
| 205 | in the httpd state since we might need to look at it later. */ |
| 206 | hs->buf.buf = hs->getbuffer; |
| 207 | if(buffer(&(hs->buf), sizeof(hs->getbuffer), ISO_nl)) { |
| 208 | hs->state = STATE_HEADERS; |
| 209 | hs->getlen = hs->buf.ptr - 1; |
| 210 | /* Null-terminate GET request string. */ |
| 211 | hs->getbuffer[hs->getlen] = 0; |
| 212 | |
| 213 | /* If there is a space character in the get request, we find |
| 214 | it and null terminate the string there. */ |
| 215 | for(i = 0; i < hs->getlen; ++i) { |
| 216 | if(hs->getbuffer[i] == ISO_space) { |
| 217 | hs->getbuffer[i] = 0; |
| 218 | hs->getlen = i; |
| 219 | break; |
| 220 | } |
| 221 | } |
| 222 | buffer_reset(&(hs->buf)); |
| 223 | } |
| 224 | break; |
| 225 | |
| 226 | case STATE_HEADERS: |
| 227 | hs->buf.buf = hs->hdrbuffer; |
| 228 | if(buffer(&(hs->buf), sizeof(hs->hdrbuffer), '\n')) { |
| 229 | if(hs->buf.ptr == 1) { |
| 230 | hs->state = STATE_SEND_HEADERS; |
| 231 | if(httpd_fs_open(hs->getbuffer, &hs->file)) { |
| 232 | hs->count = sizeof(http_header_200) - 1; |
| 233 | hs->dataptr = (char *)http_header_200; |
| 234 | hs->contenttype = http_content_type_binary; |
| 235 | hs->contentlen = sizeof(http_content_type_binary) - 1; |
| 236 | for(filetype = filetypes; |
| 237 | filetype->ext != NULL; |
| 238 | ++filetype) { |
| 239 | if(strncmp(filetype->ext, |
| 240 | hs->getbuffer + (hs->getlen - filetype->extlen), |
| 241 | filetype->extlen) == 0) { |
| 242 | hs->contenttype = filetype->type; |
| 243 | hs->contentlen = filetype->typelen; |
| 244 | break; |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | } else if(hs->getbuffer[0] == ISO_slash && |
| 249 | hs->getbuffer[1] == 0) { |
| 250 | hs->count = sizeof(http_header_200) - 1; |
| 251 | hs->dataptr = (char *)http_header_200; |
| 252 | hs->contenttype = http_content_type_html; |
| 253 | hs->contentlen = sizeof(http_content_type_html) - 1; |
| 254 | httpd_fs_open(http_index_html, &hs->file); |
| 255 | } else { |
| 256 | hs->count = sizeof(http_header_404) - 1; |
| 257 | hs->dataptr = (char *)http_header_404; |
| 258 | hs->contenttype = http_content_type_html; |
| 259 | hs->contentlen = sizeof(http_content_type_html) - 1; |
| 260 | httpd_fs_open(http_404_html, &hs->file); |
| 261 | } |
| 262 | } else { |
| 263 | buffer_reset(&(hs->buf)); |
| 264 | } |
| 265 | } |
| 266 | break; |
| 267 | |
| 268 | default: |
| 269 | uip_len = 0; |
| 270 | break; |
| 271 | } |
| 272 | } |
| 273 | } |
| 274 | /*-----------------------------------------------------------------------------*/ |
| 275 | static void |
| 276 | acked(CC_REGISTER_ARG struct httpd_state *hs) |
| 277 | { |
| 278 | hs->poll = 0; |
| 279 | |
| 280 | |
| 281 | switch(hs->state) { |
| 282 | |
| 283 | case STATE_SEND_HEADERS: |
| 284 | if(hs->count >= uip_mss()) { |
| 285 | hs->count -= uip_mss(); |
| 286 | hs->dataptr += uip_mss(); |
| 287 | } else { |
| 288 | hs->count = 0; |
| 289 | } |
| 290 | if(hs->count == 0) { |
| 291 | hs->state = STATE_SEND_CONTENT_TYPE; |
| 292 | hs->count = hs->contentlen; |
| 293 | hs->dataptr = (char *)hs->contenttype; |
| 294 | } |
| 295 | break; |
| 296 | |
| 297 | case STATE_SEND_CONTENT_TYPE: |
| 298 | if(hs->count >= uip_mss()) { |
| 299 | hs->count -= uip_mss(); |
| 300 | hs->dataptr += uip_mss(); |
| 301 | } else { |
| 302 | hs->count = 0; |
| 303 | } |
| 304 | if(hs->count == 0) { |
| 305 | hs->state = STATE_SEND_DATA; |
| 306 | hs->count = hs->file.len; |
| 307 | hs->dataptr = hs->file.data; |
| 308 | } |
| 309 | break; |
| 310 | |
| 311 | case STATE_SEND_DATA: |
| 312 | if(hs->count >= uip_mss()) { |
| 313 | hs->count -= uip_mss(); |
| 314 | hs->dataptr += uip_mss(); |
| 315 | } else { |
| 316 | hs->count = 0; |
| 317 | } |
| 318 | if(hs->count == 0) { |
| 319 | uip_close(); |
| 320 | dealloc_state(hs); |
| 321 | } |
| 322 | break; |
| 323 | } |
| 324 | } |
| 325 | /*-----------------------------------------------------------------------------*/ |
| 326 | static void |
| 327 | senddata(CC_REGISTER_ARG struct httpd_state *hs) |
| 328 | { |
| 329 | if(hs->state == STATE_SEND_HEADERS || |
| 330 | hs->state == STATE_SEND_CONTENT_TYPE || |
| 331 | hs->state == STATE_SEND_DATA) { |
| 332 | uip_send(hs->dataptr, |
| 333 | hs->count > uip_mss()? uip_mss(): hs->count); |
| 334 | } |
| 335 | } |
| 336 | /*-----------------------------------------------------------------------------*/ |
adamdunkels | 0a08fda | 2004-07-04 18:33:07 +0000 | [diff] [blame] | 337 | void |
| 338 | httpd_appcall(void *state) |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 339 | { |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 340 | register struct httpd_state *hs; |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 341 | |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 342 | hs = (struct httpd_state *)(state); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 343 | |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 344 | |
| 345 | if(uip_connected()) { |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 346 | /* Since we've just been connected, the state pointer should be |
| 347 | NULL and we need to allocate a new state object. If we have run |
| 348 | out of memory for state objects, we'll have to abort the |
| 349 | connection and return. */ |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 350 | if(hs == NULL) { |
| 351 | hs = alloc_state(); |
| 352 | if(hs == NULL) { |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 353 | uip_close(); |
| 354 | return; |
| 355 | } |
adamdunkels | 0a08fda | 2004-07-04 18:33:07 +0000 | [diff] [blame] | 356 | tcp_markconn(uip_conn, (void *)hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 357 | } |
| 358 | /* Since we have just been connected with the remote host, we |
| 359 | reset the state for this connection. The ->count variable |
| 360 | contains the amount of data that is yet to be sent to the |
| 361 | remote host, and the ->state is set to HTTP_NOGET to signal |
| 362 | that we haven't received any HTTP GET request for this |
| 363 | connection yet. */ |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 364 | hs->state = STATE_INITIAL; |
| 365 | hs->count = 0; |
| 366 | hs->poll = 0; |
| 367 | buffer_reset(&(hs->buf)); |
| 368 | return; |
| 369 | } |
| 370 | |
| 371 | if(uip_closed() || |
| 372 | uip_aborted() || |
| 373 | uip_timedout()) { |
| 374 | if(hs != NULL) { |
| 375 | dealloc_state(hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 376 | } |
| 377 | return; |
| 378 | } else if(uip_poll()) { |
| 379 | /* If we are polled ten times, we abort the connection. This is |
| 380 | because we don't want connections lingering indefinately in |
| 381 | the system. */ |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 382 | if(hs != NULL) { |
| 383 | if(hs->state == STATE_DEALLOCATED) { |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 384 | uip_abort(); |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 385 | } else if(hs->poll++ >= 100) { |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 386 | uip_abort(); |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 387 | dealloc_state(hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 388 | } |
| 389 | } |
| 390 | return; |
| 391 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 392 | |
| 393 | if(uip_acked()) { |
| 394 | acked(hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 395 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 396 | |
| 397 | if(uip_newdata()) { |
| 398 | newdata(hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 399 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 400 | |
| 401 | if(uip_rexmit() || |
| 402 | uip_newdata() || |
| 403 | uip_acked()) { |
| 404 | senddata(hs); |
| 405 | } else if(uip_poll()) { |
| 406 | senddata(hs); |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 407 | } |
| 408 | |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 409 | |
adamdunkels | 39c5750 | 2003-08-09 13:14:33 +0000 | [diff] [blame] | 410 | } |
adamdunkels | 2aa45c4 | 2004-02-16 20:55:34 +0000 | [diff] [blame] | 411 | /*-----------------------------------------------------------------------------*/ |