Contiki 2.6

disco.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010, Loughborough University - Computer Science
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * This file is part of the Contiki operating system.
00030  */
00031 
00032 /**
00033  * \file
00034  *         Disco server sources
00035  *         (embedded part of the DISCOBALL project)
00036  *
00037  *         It objective is to receive a code file over UDP, store it in
00038  *         external flash and disseminate it to other nodes of the
00039  *         6LoWPAN network.
00040  *
00041  *         For this to work, the image must be co-hosted with the BooTTY!
00042  *         bootloader, which will move the image from external to internal
00043  *         flash.
00044  *
00045  *         To link this application in your contiki image, all you need to
00046  *         do is to add this line:
00047  *         OFFSET_FIRMWARE=1
00048  *         to your project's makefile
00049  *
00050  * \author
00051  *         George Oikonomou - <oikonomou@users.sourceforge.net>
00052  */
00053 
00054 #include "contiki.h"
00055 #include "contiki-net.h"
00056 #include "sys/clock.h"
00057 #include "sys/ctimer.h"
00058 #include "dev/watchdog.h"
00059 
00060 #include "dev/n740.h"
00061 #include "dev/m25p16.h"
00062 
00063 #include "disco.h"
00064 /*---------------------------------------------------------------------------*/
00065 #define DEBUG DEBUG_NONE
00066 #include "net/uip-debug.h"
00067 /*---------------------------------------------------------------------------*/
00068 #if BATMON_CONF_ENABLED
00069 void batmon_log(uint8_t trigger);
00070 
00071 #define LOG_TRIGGER_OAP_DISCO_START  0x01
00072 #define LOG_TRIGGER_OAP_DISCO_DONE   0x02
00073 #define LOG_TRIGGER_OAP_DISCO_ABORT  0x03
00074 #else
00075 #define batmon_log(t) do { } while(0);
00076 #endif
00077 /*---------------------------------------------------------------------------*/
00078 static struct uip_udp_conn *server_conn;
00079 static struct disco_request_pdu * req;
00080 static struct disco_response_pdu resp;
00081 static struct disco_seed seed;
00082 static uint8_t state;
00083 static uint8_t sector;
00084 static uint16_t interval;
00085 static struct ctimer disco_timer;
00086 
00087 #define UIP_IP_BUF   ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
00088 #define UIP_UDP_BUF  ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
00089 
00090 extern uint16_t uip_len;
00091 extern void *uip_appdata;
00092 
00093 __xdata __at(BOOTTY_CMD_LOCATION) static uint8_t bd;
00094 /*---------------------------------------------------------------------------*/
00095 static void timer_handler(void * p);
00096 /*---------------------------------------------------------------------------*/
00097 static void
00098 abort() CC_NON_BANKED
00099 {
00100   PRINTF("Disco: Abort @ %lu\n", clock_seconds());
00101   n740_analog_deactivate();
00102   m25p16_dp();
00103   n740_analog_activate();
00104   state = DISCO_STATE_LISTENING;
00105   memset(&seed, 0, sizeof(seed));
00106   ctimer_stop(&disco_timer);
00107   batmon_log(LOG_TRIGGER_OAP_DISCO_ABORT);
00108 }
00109 /*---------------------------------------------------------------------------*/
00110 static void
00111 restart_timer(uint16_t t) CC_NON_BANKED
00112 {
00113   interval = t;
00114   ctimer_stop(&disco_timer);
00115   ctimer_set(&disco_timer, interval, timer_handler, &state);
00116 }
00117 /*---------------------------------------------------------------------------*/
00118 static void
00119 timer_handler(void * p)
00120 {
00121   uint8_t * s = p;
00122   uint8_t wip;
00123 
00124   PRINTF("Disco: @ %lu, s: %u\n", clock_seconds(), *s);
00125 
00126   if(*s == DISCO_STATE_PREPARING) {
00127     n740_analog_deactivate();
00128     wip = M25P16_WIP();
00129     n740_analog_activate();
00130 
00131     if(wip) {
00132       restart_timer(DISCO_TIMEOUT_PREPARE);
00133     } else {
00134       PRINTF("Disco: Erased %u\n", sector);
00135       if((sector & 1) == 0) {
00136         sector++;
00137         PRINTF("Disco: Next %u\n", sector);
00138         n740_analog_deactivate();
00139         m25p16_se(sector);
00140         n740_analog_activate();
00141         restart_timer(DISCO_TIMEOUT_PREPARE);
00142       } else {
00143         PRINTF("Disco: Ready\n");
00144         *s = DISCO_STATE_READY;
00145         resp.status = DISCO_CMD_INIT;
00146         restart_timer(DISCO_TIMEOUT_ABORT);
00147         server_conn->rport = seed.port;
00148         uip_ipaddr_copy(&server_conn->ripaddr, &seed.addr);
00149         uip_udp_packet_send(server_conn, &resp, DISCO_RESP_LEN_INIT);
00150 
00151         /* Restore server connection to allow data from any node */
00152         uip_create_unspecified(&server_conn->ripaddr);
00153         server_conn->rport = 0;
00154       }
00155     }
00156   } else if(*s == DISCO_STATE_READY) {
00157     abort();
00158   } else if(*s == DISCO_STATE_REBOOTING) {
00159     watchdog_reboot();
00160   }
00161 }
00162 /*---------------------------------------------------------------------------*/
00163 static uint8_t
00164 is_protected(uint8_t a) CC_NON_BANKED
00165 {
00166   uint8_t bp = M25P16_BP() >> 2;
00167 
00168   if(bp > 5) {
00169     return SECTOR_PROTECTED;
00170   }
00171 
00172   bp -= 1;
00173 
00174   if(a >= (32 - (1 << bp))) {
00175     return SECTOR_PROTECTED;
00176   }
00177   return SECTOR_UNPROTECTED;
00178 }
00179 /*---------------------------------------------------------------------------*/
00180 static uint8_t
00181 cmd_init() CC_NON_BANKED
00182 {
00183   PRINTF("Disco: Init 0x%02x\n", req->addr[0]);
00184   if(uip_datalen() != DISCO_LEN_INIT) {
00185     PRINTF("Disco: Bad len (%u)\n", uip_datalen());
00186     resp.status = DISCO_ERR_BAD_LEN;
00187     return DISCO_RESP_LEN_ERR;
00188   }
00189   n740_analog_deactivate();
00190   m25p16_res();
00191   sector = 2 * req->addr[0];
00192   if(is_protected(sector) == SECTOR_PROTECTED
00193       || is_protected(sector + 1) == SECTOR_PROTECTED) {
00194     resp.status = DISCO_ERR_PROTECTED;
00195     n740_analog_activate();
00196     return DISCO_RESP_LEN_ERR;
00197   }
00198   m25p16_se(sector);
00199   n740_analog_activate();
00200   state = DISCO_STATE_PREPARING;
00201   restart_timer(DISCO_TIMEOUT_PREPARE);
00202 
00203   /* Store the sender's address/port so we can reply when ready */
00204   seed.port = UIP_UDP_BUF->srcport;
00205   uip_ipaddr_copy(&seed.addr, &UIP_IP_BUF->srcipaddr);
00206   PRINTF("Disco: OK\n");
00207 
00208   batmon_log(LOG_TRIGGER_OAP_DISCO_START);
00209 
00210   return DISCO_RESPONSE_NONE;
00211 }
00212 /*---------------------------------------------------------------------------*/
00213 static uint8_t
00214 cmd_write() CC_NON_BANKED
00215 {
00216   PRINTF("Disco: Write 0x%02x%02x%02x\n", req->addr[0], req->addr[1], req->addr[2]);
00217   if(uip_datalen() != DISCO_LEN_WRITE) {
00218     resp.status = DISCO_ERR_BAD_LEN;
00219     return DISCO_RESP_LEN_ERR;
00220   }
00221   restart_timer(DISCO_TIMEOUT_ABORT);
00222   n740_analog_deactivate();
00223   m25p16_pp(req->addr, req->data, DISCO_FLEN_DATA);
00224   watchdog_periodic();
00225   while(M25P16_WIP());
00226   n740_analog_activate();
00227   resp.status = DISCO_CMD_WRITE;
00228   memcpy(resp.addr, req->addr, DISCO_FLEN_ADDR);
00229   return DISCO_RESP_LEN_WRITE;
00230 }
00231 /*---------------------------------------------------------------------------*/
00232 static uint8_t
00233 cmd_switch() CC_NON_BANKED
00234 {
00235   PRINTF("Disco: Switch 0x%02x\n", req->addr[0]);
00236   if(uip_datalen() != DISCO_LEN_SWITCH) {
00237     resp.status = DISCO_ERR_BAD_LEN;
00238     return DISCO_RESP_LEN_ERR;
00239   }
00240   if(req->addr[0] > 15) {
00241     resp.status = DISCO_ERR_BAD_OFFSET;
00242     return DISCO_RESP_LEN_ERR;
00243   }
00244 
00245   bd = BOOTTY_CMD_COPY_IMAGE;
00246   bd |= req->addr[0];
00247 
00248   resp.status = DISCO_CMD_SWITCH;
00249   resp.addr[0] = req->addr[0];
00250 
00251   restart_timer(DISCO_TIMEOUT_REBOOT);
00252   state = DISCO_STATE_REBOOTING;
00253 
00254   return DISCO_RESP_LEN_SWITCH;
00255 }
00256 /*---------------------------------------------------------------------------*/
00257 static uint8_t
00258 cmd_done() CC_NON_BANKED
00259 {
00260   PRINTF("Disco: Done\n");
00261   if(uip_datalen() != DISCO_LEN_DONE) {
00262     resp.status = DISCO_ERR_BAD_LEN;
00263     return DISCO_RESP_LEN_ERR;
00264   }
00265   resp.status = DISCO_CMD_DONE;
00266 
00267   batmon_log(LOG_TRIGGER_OAP_DISCO_DONE);
00268 
00269   return DISCO_RESP_LEN_DONE;
00270 }
00271 /*---------------------------------------------------------------------------*/
00272 static uint8_t
00273 event_handler(process_event_t ev) CC_NON_BANKED
00274 {
00275   uint8_t rv = DISCO_RESPONSE_NONE;
00276 
00277   if(ev != tcpip_event) {
00278     return rv;
00279   }
00280 
00281   /* Always accept CMD_DONE */
00282   if(req->cmd == DISCO_CMD_DONE) {
00283     return cmd_done();
00284   }
00285 
00286   /* Always accept switch too */
00287   if(req->cmd == DISCO_CMD_SWITCH) {
00288     return cmd_switch();
00289   }
00290 
00291   switch(state) {
00292   case DISCO_STATE_LISTENING:
00293     req = uip_appdata;
00294     if(req->cmd == DISCO_CMD_INIT) {
00295       rv = cmd_init();
00296     }
00297     break;
00298   case DISCO_STATE_PREPARING:
00299     PRINTF("Disco: Not Ready\n");
00300     resp.status = DISCO_ERR_NOT_READY;
00301     rv = DISCO_RESP_LEN_ERR;
00302     break;
00303   case DISCO_STATE_READY:
00304     req = uip_appdata;
00305     if(req->cmd == DISCO_CMD_WRITE) {
00306       rv = cmd_write();
00307     } else if(req->cmd == DISCO_CMD_INIT) {
00308       resp.status = DISCO_ERR_INIT_DONE;
00309       rv = DISCO_RESP_LEN_ERR;
00310     } else if(req->cmd == DISCO_CMD_SWITCH) {
00311       rv = cmd_switch();
00312     }
00313     break;
00314   }
00315   return rv;
00316 }
00317 /*---------------------------------------------------------------------------*/
00318 PROCESS(disco_process, "Disco Server Process");
00319 /*---------------------------------------------------------------------------*/
00320 PROCESS_THREAD(disco_process, ev, data)
00321 {
00322   uint8_t len;
00323 
00324   PROCESS_BEGIN();
00325 
00326   PRINTF("Disco Server\n");
00327 
00328   server_conn = udp_new(NULL, UIP_HTONS(0), NULL);
00329   udp_bind(server_conn, UIP_HTONS(DISCO_UDP_PORT));
00330 
00331   state = DISCO_STATE_LISTENING;
00332 
00333   while(1) {
00334     PROCESS_YIELD();
00335     len = event_handler(ev);
00336 
00337     if(len > 0) {
00338       server_conn->rport = UIP_UDP_BUF->srcport;
00339       uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr);
00340       uip_udp_packet_send(server_conn, &resp, len);
00341       /* Restore server connection to allow data from any node */
00342       uip_create_unspecified(&server_conn->ripaddr);
00343       server_conn->rport = 0;
00344     }
00345   }
00346 
00347   PROCESS_END();
00348 }
00349 /*---------------------------------------------------------------------------*/