Contiki 3.x
disco.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, Loughborough University - Computer Science
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.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  */
31 
32 /**
33  * \file
34  * Disco server sources
35  * (embedded part of the DISCOBALL project)
36  *
37  * It objective is to receive a code file over UDP, store it in
38  * external flash and disseminate it to other nodes of the
39  * 6LoWPAN network.
40  *
41  * For this to work, the image must be co-hosted with the BooTTY!
42  * bootloader, which will move the image from external to internal
43  * flash.
44  *
45  * To link this application in your contiki image, all you need to
46  * do is to add this line:
47  * OFFSET_FIRMWARE=1
48  * to your project's makefile
49  *
50  * \author
51  * George Oikonomou - <oikonomou@users.sourceforge.net>
52  */
53 
54 #include "contiki.h"
55 #include "contiki-net.h"
56 #include "sys/clock.h"
57 #include "sys/ctimer.h"
58 #include "dev/watchdog.h"
59 
60 #include "dev/n740.h"
61 #include "dev/m25p16.h"
62 
63 #include "disco.h"
64 /*---------------------------------------------------------------------------*/
65 #define DEBUG DEBUG_NONE
66 #include "net/ip/uip-debug.h"
67 /*---------------------------------------------------------------------------*/
68 #if BATMON_CONF_ENABLED
69 void batmon_log(uint8_t trigger);
70 
71 #define LOG_TRIGGER_OAP_DISCO_START 0x01
72 #define LOG_TRIGGER_OAP_DISCO_DONE 0x02
73 #define LOG_TRIGGER_OAP_DISCO_ABORT 0x03
74 #else
75 #define batmon_log(t) do { } while(0);
76 #endif
77 /*---------------------------------------------------------------------------*/
78 static struct uip_udp_conn *server_conn;
79 static struct disco_request_pdu *req;
80 static struct disco_response_pdu resp;
81 static struct disco_seed seed;
82 static uint8_t state;
83 static uint8_t sector;
84 static uint16_t interval;
85 static struct ctimer disco_timer;
86 
87 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
88 #define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
89 
90 extern uint16_t uip_len;
91 extern void *uip_appdata;
92 
93 __xdata __at(BOOTTY_CMD_LOCATION) static uint8_t bd;
94 /*---------------------------------------------------------------------------*/
95 static void timer_handler(void *p);
96 /*---------------------------------------------------------------------------*/
97 static void
98 abort() CC_NON_BANKED
99 {
100  PRINTF("Disco: Abort @ %lu\n", clock_seconds());
101  n740_analog_deactivate();
102  m25p16_dp();
103  n740_analog_activate();
104  state = DISCO_STATE_LISTENING;
105  memset(&seed, 0, sizeof(seed));
106  ctimer_stop(&disco_timer);
107  batmon_log(LOG_TRIGGER_OAP_DISCO_ABORT);
108 }
109 /*---------------------------------------------------------------------------*/
110 static void
111 restart_timer(uint16_t t) CC_NON_BANKED
112 {
113  interval = t;
114  ctimer_stop(&disco_timer);
115  ctimer_set(&disco_timer, interval, timer_handler, &state);
116 }
117 /*---------------------------------------------------------------------------*/
118 static void
119 timer_handler(void *p)
120 {
121  uint8_t *s = p;
122  uint8_t wip;
123 
124  PRINTF("Disco: @ %lu, s: %u\n", clock_seconds(), *s);
125 
126  if(*s == DISCO_STATE_PREPARING) {
127  n740_analog_deactivate();
128  wip = M25P16_WIP();
129  n740_analog_activate();
130 
131  if(wip) {
132  restart_timer(DISCO_TIMEOUT_PREPARE);
133  } else {
134  PRINTF("Disco: Erased %u\n", sector);
135  if((sector & 1) == 0) {
136  sector++;
137  PRINTF("Disco: Next %u\n", sector);
138  n740_analog_deactivate();
139  m25p16_se(sector);
140  n740_analog_activate();
141  restart_timer(DISCO_TIMEOUT_PREPARE);
142  } else {
143  PRINTF("Disco: Ready\n");
144  *s = DISCO_STATE_READY;
145  resp.status = DISCO_CMD_INIT;
146  restart_timer(DISCO_TIMEOUT_ABORT);
147  server_conn->rport = seed.port;
148  uip_ipaddr_copy(&server_conn->ripaddr, &seed.addr);
149  uip_udp_packet_send(server_conn, &resp, DISCO_RESP_LEN_INIT);
150 
151  /* Restore server connection to allow data from any node */
152  uip_create_unspecified(&server_conn->ripaddr);
153  server_conn->rport = 0;
154  }
155  }
156  } else if(*s == DISCO_STATE_READY) {
157  abort();
158  } else if(*s == DISCO_STATE_REBOOTING) {
159  watchdog_reboot();
160  }
161 }
162 /*---------------------------------------------------------------------------*/
163 static uint8_t
164 is_protected(uint8_t a) CC_NON_BANKED
165 {
166  uint8_t bp = M25P16_BP() >> 2;
167 
168  if(bp > 5) {
169  return SECTOR_PROTECTED;
170  }
171 
172  bp -= 1;
173 
174  if(a >= (32 - (1 << bp))) {
175  return SECTOR_PROTECTED;
176  }
177  return SECTOR_UNPROTECTED;
178 }
179 /*---------------------------------------------------------------------------*/
180 static uint8_t
181 cmd_init() CC_NON_BANKED
182 {
183  PRINTF("Disco: Init 0x%02x\n", req->addr[0]);
184  if(uip_datalen() != DISCO_LEN_INIT) {
185  PRINTF("Disco: Bad len (%u)\n", uip_datalen());
186  resp.status = DISCO_ERR_BAD_LEN;
187  return DISCO_RESP_LEN_ERR;
188  }
189  n740_analog_deactivate();
190  m25p16_res();
191  sector = 2 * req->addr[0];
192  if(is_protected(sector) == SECTOR_PROTECTED
193  || is_protected(sector + 1) == SECTOR_PROTECTED) {
194  resp.status = DISCO_ERR_PROTECTED;
195  n740_analog_activate();
196  return DISCO_RESP_LEN_ERR;
197  }
198  m25p16_se(sector);
199  n740_analog_activate();
200  state = DISCO_STATE_PREPARING;
201  restart_timer(DISCO_TIMEOUT_PREPARE);
202 
203  /* Store the sender's address/port so we can reply when ready */
204  seed.port = UIP_UDP_BUF->srcport;
205  uip_ipaddr_copy(&seed.addr, &UIP_IP_BUF->srcipaddr);
206  PRINTF("Disco: OK\n");
207 
208  batmon_log(LOG_TRIGGER_OAP_DISCO_START);
209 
210  return DISCO_RESPONSE_NONE;
211 }
212 /*---------------------------------------------------------------------------*/
213 static uint8_t
214 cmd_write() CC_NON_BANKED
215 {
216  PRINTF("Disco: Write 0x%02x%02x%02x\n", req->addr[0], req->addr[1],
217  req->addr[2]);
218  if(uip_datalen() != DISCO_LEN_WRITE) {
219  resp.status = DISCO_ERR_BAD_LEN;
220  return DISCO_RESP_LEN_ERR;
221  }
222  restart_timer(DISCO_TIMEOUT_ABORT);
223  n740_analog_deactivate();
224  m25p16_pp(req->addr, req->data, DISCO_FLEN_DATA);
226  while(M25P16_WIP());
227  n740_analog_activate();
228  resp.status = DISCO_CMD_WRITE;
229  memcpy(resp.addr, req->addr, DISCO_FLEN_ADDR);
230  return DISCO_RESP_LEN_WRITE;
231 }
232 /*---------------------------------------------------------------------------*/
233 static uint8_t
234 cmd_switch() CC_NON_BANKED
235 {
236  PRINTF("Disco: Switch 0x%02x\n", req->addr[0]);
237  if(uip_datalen() != DISCO_LEN_SWITCH) {
238  resp.status = DISCO_ERR_BAD_LEN;
239  return DISCO_RESP_LEN_ERR;
240  }
241  if(req->addr[0] > 15) {
242  resp.status = DISCO_ERR_BAD_OFFSET;
243  return DISCO_RESP_LEN_ERR;
244  }
245 
246  bd = BOOTTY_CMD_COPY_IMAGE;
247  bd |= req->addr[0];
248 
249  resp.status = DISCO_CMD_SWITCH;
250  resp.addr[0] = req->addr[0];
251 
252  restart_timer(DISCO_TIMEOUT_REBOOT);
253  state = DISCO_STATE_REBOOTING;
254 
255  return DISCO_RESP_LEN_SWITCH;
256 }
257 /*---------------------------------------------------------------------------*/
258 static uint8_t
259 cmd_done() CC_NON_BANKED
260 {
261  PRINTF("Disco: Done\n");
262  if(uip_datalen() != DISCO_LEN_DONE) {
263  resp.status = DISCO_ERR_BAD_LEN;
264  return DISCO_RESP_LEN_ERR;
265  }
266  resp.status = DISCO_CMD_DONE;
267 
268  batmon_log(LOG_TRIGGER_OAP_DISCO_DONE);
269 
270  return DISCO_RESP_LEN_DONE;
271 }
272 /*---------------------------------------------------------------------------*/
273 static uint8_t
274 event_handler(process_event_t ev) CC_NON_BANKED
275 {
276  uint8_t rv = DISCO_RESPONSE_NONE;
277 
278  if(ev != tcpip_event) {
279  return rv;
280  }
281 
282  /* Always accept CMD_DONE */
283  if(req->cmd == DISCO_CMD_DONE) {
284  return cmd_done();
285  }
286 
287  /* Always accept switch too */
288  if(req->cmd == DISCO_CMD_SWITCH) {
289  return cmd_switch();
290  }
291 
292  switch(state) {
293  case DISCO_STATE_LISTENING:
294  req = uip_appdata;
295  if(req->cmd == DISCO_CMD_INIT) {
296  rv = cmd_init();
297  }
298  break;
299  case DISCO_STATE_PREPARING:
300  PRINTF("Disco: Not Ready\n");
301  resp.status = DISCO_ERR_NOT_READY;
302  rv = DISCO_RESP_LEN_ERR;
303  break;
304  case DISCO_STATE_READY:
305  req = uip_appdata;
306  if(req->cmd == DISCO_CMD_WRITE) {
307  rv = cmd_write();
308  } else if(req->cmd == DISCO_CMD_INIT) {
309  resp.status = DISCO_ERR_INIT_DONE;
310  rv = DISCO_RESP_LEN_ERR;
311  } else if(req->cmd == DISCO_CMD_SWITCH) {
312  rv = cmd_switch();
313  }
314  break;
315  }
316  return rv;
317 }
318 /*---------------------------------------------------------------------------*/
319 PROCESS(disco_process, "Disco Server Process");
320 /*---------------------------------------------------------------------------*/
321 PROCESS_THREAD(disco_process, ev, data)
322 {
323  uint8_t len;
324 
325  PROCESS_BEGIN();
326 
327  PRINTF("Disco Server\n");
328 
329  server_conn = udp_new(NULL, UIP_HTONS(0), NULL);
330  udp_bind(server_conn, UIP_HTONS(DISCO_UDP_PORT));
331 
332  state = DISCO_STATE_LISTENING;
333 
334  while(1) {
335  PROCESS_YIELD();
336  len = event_handler(ev);
337 
338  if(len > 0) {
339  server_conn->rport = UIP_UDP_BUF->srcport;
340  uip_ipaddr_copy(&server_conn->ripaddr, &UIP_IP_BUF->srcipaddr);
341  uip_udp_packet_send(server_conn, &resp, len);
342  /* Restore server connection to allow data from any node */
343  uip_create_unspecified(&server_conn->ripaddr);
344  server_conn->rport = 0;
345  }
346  }
347 
348  PROCESS_END();
349 }
350 /*---------------------------------------------------------------------------*/
#define uip_create_unspecified(a)
set IP address a to unspecified
Definition: uip.h:2007
uip_len
The length of the packet in the uip_buf buffer.
Definition: tcp_loader.c:75
uint16_t rport
The remote port number in network byte order.
Definition: uip.h:1397
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
CCIF struct uip_udp_conn * udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Create a new UDP connection.
Header file for the Disco server (embedded part of the DISCOBALL project) ...
#define NULL
The null pointer.
Header file for the control of the M25P16 on sensinode N740s.
void m25p16_pp(uint8_t *addr, uint8_t *buff, uint8_t buff_len)
Program Page (PP) instruction.
Definition: m25p16.c:224
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1238
void watchdog_reboot(void)
Keeps control until the WDT throws a reset signal.
Definition: watchdog.c:128
uip_ipaddr_t ripaddr
The IP address of the remote peer.
Definition: uip.h:1395
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define UIP_IP_BUF
Pointer to IP header.
Definition: uip-nd6.c:104
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
Header File for the module which controls the Sensinode N740 8-bit serial-in/serial or parall...
void watchdog_periodic(void)
Writes the WDT clear sequence.
Definition: watchdog.c:64
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1026
#define M25P16_BP()
Retrieve Block Protect Bits from the status register.
Definition: m25p16.h:107
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
Header file for the callback timer
void m25p16_dp()
Deep Power Down (DP) instruction.
Definition: m25p16.c:273
CCIF unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition: clock.c:57
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:651
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#define PROCESS_YIELD()
Yield the currently running process.
Definition: process.h:164
void m25p16_se(uint8_t s)
Sector Erase (SE) instruction.
Definition: m25p16.c:248
#define udp_bind(conn, port)
Bind a UDP connection to a local port.
Definition: tcpip.h:262
A set of debugging macros.
Representation of a uIP UDP connection.
Definition: uip.h:1394
__xdata __at(0x0000)
Each iteration is ~1.0xy usec, so this function delays for roughly len usec.
Definition: clock.c:55
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition: ctimer.c:142
#define M25P16_WIP()
Check for Write in Progress.
Definition: m25p16.h:116
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
void m25p16_res()
Release from Deep Power Down (RES) instruction.
Definition: m25p16.c:284