Contiki 2.6

checkpoint-arch.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010, Swedish Institute of 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 /**
00034  * \file
00035  *  Checkpoint library implementation for the Tmote Sky platform.
00036  *
00037  * \author
00038  *  Fredrik Osterlind <fros@sics.se>
00039  */
00040 
00041 #include "contiki.h"
00042 
00043 #include "lib/crc16.h"
00044 #include "lib/checkpoint.h"
00045 
00046 #include "sys/rtimer.h"
00047 #include "sys/mt.h"
00048 #include "sys/energest.h"
00049 #include "sys/compower.h"
00050 #include "dev/leds.h"
00051 #include "dev/watchdog.h"
00052 #include "dev/serial-line.h"
00053 #include "dev/uart1.h"
00054 #include "dev/cc2420.h"
00055 #include "dev/button-sensor.h"
00056 #include "cfs/cfs.h"
00057 #include "cfs/cfs-coffee.h"
00058 #include <stdio.h>
00059 
00060 #define DEBUG 0
00061 #if DEBUG
00062 #define PRINTF(...) printf(__VA_ARGS__)
00063 #else
00064 #define PRINTF(...)
00065 #endif
00066 
00067 #ifndef CHECKPOINT_ROLLBACK_BUTTON
00068 #define CHECKPOINT_ROLLBACK_BUTTON 1 /* rollback "cp_wdt" on button click */
00069 #endif /* CHECKPOINT_ROLLBACK_BUTTON */
00070 
00071 #if CHECKPOINT_ROLLBACK_BUTTON
00072 PROCESS(checkpoint_button_process, "Rollback on button");
00073 #endif /* CHECKPOINT_ROLLBACK_BUTTON */
00074 
00075 #define WITH_SERIAL_COMMANDS 0 /* checkpoint via serial port */
00076 #if WITH_SERIAL_COMMANDS
00077 #if UART1_CONF_TX_WITH_INTERRUPT
00078 #error TX_WITH_INTERRUPTS must be 0
00079 #endif /* UART1_CONF_TX_WITH_INTERRUPT */
00080 #define PRINTF_COMMAND(...) printf(__VA_ARGS__)
00081 #else /* WITH_SERIAL_COMMANDS */
00082 #define PRINTF_COMMAND(...)
00083 #endif /* WITH_SERIAL_COMMANDS */
00084 
00085 #define COMMAND_ROLLBACK 1
00086 #define COMMAND_CHECKPOINT 2
00087 #define COMMAND_METRICS 3
00088 
00089 #define INCLUDE_RAM 1 /* Less then 10240 bytes */
00090 #define INCLUDE_TIMERS 1 /* 16 bytes */
00091 #define INCLUDE_LEDS 1 /* 1 bytes */
00092 /* ... */
00093 
00094 /* 10kb memory */
00095 #define RAM_START 0x1100
00096 #define RAM_END 0x3900
00097 
00098 #define PAUSE_TIME() \
00099   TACTL &= ~(MC1); \
00100   TBCTL &= ~(MC1); \
00101   watchdog_stop();
00102 #define RESUME_TIME() \
00103   TACTL |= MC1; \
00104   TBCTL |= MC1; \
00105   TACCR1 = clock_fine_max(); \
00106   watchdog_start();
00107 #define PAUSE_TIME_INT() \
00108   dint(); \
00109   PAUSE_TIME();
00110 #define RESUME_TIME_INT() \
00111   RESUME_TIME(); \
00112   eint();
00113 
00114 static struct mt_thread checkpoint_thread;
00115 static uint8_t preset_cmd;
00116 static int preset_fd;
00117 
00118 /* bookkeeping */
00119 #if WITH_SERIAL_COMMANDS
00120 static int nr_pongs=0;
00121 #endif /* WITH_SERIAL_COMMANDS */
00122 static int nr_checkpoints=0, nr_rollbacks=0, nr_metrics=0;
00123 
00124 /*---------------------------------------------------------------------------*/
00125 typedef union {
00126   unsigned char u8[2];
00127   unsigned short u16;
00128 } word_union_t;
00129 /*---------------------------------------------------------------------------*/
00130 static int
00131 write_byte(int fd, uint8_t c)
00132 {
00133   return cfs_write(fd, &c, 1);
00134 }
00135 /*---------------------------------------------------------------------------*/
00136 static void
00137 write_word(int fd, uint16_t w)
00138 {
00139   word_union_t tmp;
00140   tmp.u16 = w;
00141   write_byte(fd, tmp.u8[0]);
00142   write_byte(fd, tmp.u8[1]);
00143 }
00144 /*---------------------------------------------------------------------------*/
00145 static uint8_t
00146 read_byte(int fd)
00147 {
00148   uint8_t c;
00149   cfs_read(fd, &c, 1);
00150   return c;
00151 }
00152 /*---------------------------------------------------------------------------*/
00153 static uint16_t
00154 read_word(int fd)
00155 {
00156   word_union_t tmp;
00157   tmp.u8[0] = read_byte(fd);
00158   tmp.u8[1] = read_byte(fd);
00159   return tmp.u16;
00160 }
00161 /*---------------------------------------------------------------------------*/
00162 static void
00163 thread_checkpoint(int fd)
00164 {
00165 #if INCLUDE_RAM
00166   unsigned char *addr;
00167   uint16_t size = 0;
00168   unsigned char *thread_mem_start = (unsigned char *)&checkpoint_thread.thread.stack;
00169   unsigned char *thread_mem_end = thread_mem_start + sizeof(checkpoint_thread.thread.stack) - 1;
00170   unsigned char *coffee_mem_start = cfs_coffee_get_protected_mem(&size);
00171   unsigned char *coffee_mem_end = coffee_mem_start + size - 1;
00172 #endif /* INCLUDE_RAM */
00173 
00174   /*PRINTF("protected thread memory: %u, size=%u\n", (uint16_t) thread_mem_start, sizeof(checkpoint_thread.thread.stack));*/
00175   /*PRINTF("protected coffee memory: %u, size=%u\n", (uint16_t) coffee_mem_start, size);*/
00176 
00177   /* RAM */
00178 #if INCLUDE_RAM
00179   for(addr = (unsigned char *)RAM_START;
00180   addr < (unsigned char *)RAM_END;
00181   addr++) {
00182 
00183     if((addr >= thread_mem_start && addr <= thread_mem_end)) {
00184       /* Skip */
00185       continue;
00186     }
00187 
00188     if((addr >= coffee_mem_start && addr <= coffee_mem_end)) {
00189       /* Skip */
00190       continue;
00191     }
00192 
00193     /* TODO Use write_array() */
00194     write_byte(fd, *addr);
00195 
00196     /*if(((int)addr % 512) == 0) {
00197       PRINTF(".");
00198     }*/
00199   }
00200 
00201 #endif /* INCLUDE_RAM */
00202 
00203   /* Timers */
00204 #if INCLUDE_TIMERS
00205   write_word(fd, TACTL);
00206   write_word(fd, TACCTL1);
00207   write_word(fd, TACCR1);
00208   write_word(fd, TAR);
00209 
00210   write_word(fd, TBCTL);
00211   write_word(fd, TBCCTL1);
00212   write_word(fd, TBCCR1);
00213   write_word(fd, TBR);
00214 #endif /* INCLUDE_TIMERS */
00215 
00216   /* LEDs */
00217 #if INCLUDE_LEDS
00218   write_byte(fd, leds_arch_get());
00219 #endif /* INCLUDE_LEDS */
00220 
00221   /* Radio */
00222   /* ADC */
00223   /* ... */
00224 
00225   write_byte(fd, -1); /* Coffee padding byte */
00226 }
00227 /*---------------------------------------------------------------------------*/
00228 static void
00229 thread_rollback(int fd)
00230 {
00231 #if INCLUDE_RAM
00232   unsigned char *addr;
00233   uint16_t size = 0;
00234   unsigned char *thread_mem_start = (unsigned char *)&checkpoint_thread.thread.stack;
00235   unsigned char *thread_mem_end = thread_mem_start + sizeof(checkpoint_thread.thread.stack) - 1;
00236   unsigned char *coffee_mem_start = cfs_coffee_get_protected_mem(&size);
00237   unsigned char *coffee_mem_end = coffee_mem_start + size - 1;
00238 #endif /* INCLUDE_RAM */
00239 
00240   /*PRINTF("protected thread memory: %u, size=%u\n", (uint16_t) thread_mem_start, sizeof(checkpoint_thread.thread.stack));*/
00241   /*PRINTF("protected coffee memory: %u, size=%u\n", (uint16_t) coffee_mem_start, size);*/
00242 
00243   /* RAM */
00244 #if INCLUDE_RAM
00245   for(addr = (unsigned char *)RAM_START;
00246   addr < (unsigned char *)RAM_END;
00247   addr++) {
00248     if((addr >= thread_mem_start && addr <= thread_mem_end)) {
00249       /* Skip */
00250       continue;
00251     }
00252 
00253     if((addr >= coffee_mem_start && addr <= coffee_mem_end)) {
00254       /* Skip */
00255       continue;
00256     }
00257 
00258     *addr = read_byte(fd);
00259   }
00260 #endif /* INCLUDE_RAM */
00261 
00262   /* Timers */
00263 #if INCLUDE_TIMERS
00264   TACTL = read_word(fd);
00265   TACCTL1 = read_word(fd);
00266   TACCR1 = read_word(fd);
00267   TAR = read_word(fd);
00268 
00269   TBCTL = read_word(fd);
00270   TBCCTL1 = read_word(fd);
00271   TBCCR1 = read_word(fd);
00272   TBR = read_word(fd);
00273 #endif /* INCLUDE_TIMERS */
00274 
00275   /* LEDs */
00276 #if INCLUDE_LEDS
00277   leds_arch_set(read_byte(fd));
00278 #endif /* INCLUDE_LEDS */
00279 
00280   /* Radio */
00281   /* ADC */
00282   /* ... */
00283 
00284   read_byte(fd); /* Coffee padding byte */
00285 }
00286 /*---------------------------------------------------------------------------*/
00287 #if WITH_SERIAL_COMMANDS
00288 static uint32_t
00289 thread_metric_tx(void)
00290 {
00291   energest_flush();
00292   return energest_type_time(ENERGEST_TYPE_TRANSMIT);
00293 }
00294 /*---------------------------------------------------------------------------*/
00295 static uint32_t
00296 thread_metric_rx(void)
00297 {
00298   energest_flush();
00299   return energest_type_time(ENERGEST_TYPE_LISTEN);
00300 }
00301 #endif /* WITH_SERIAL_COMMANDS */
00302 /*---------------------------------------------------------------------------*/
00303 static void
00304 thread_metrics(void)
00305 {
00306   PRINTF_COMMAND("METRICS:START\n");
00307   PRINTF_COMMAND("M:RTIMER_NOW:%u\n", RTIMER_NOW()); /* TODO extract */
00308   PRINTF_COMMAND("M:ENERGY_TX:%lu\n", thread_metric_tx());
00309   PRINTF_COMMAND("M:ENERGY_RX:%lu\n", thread_metric_rx());
00310   PRINTF_COMMAND("M:RTIMER_NOW2:%u\n", RTIMER_NOW());
00311   nr_metrics++;
00312   PRINTF_COMMAND("METRICS:DONE %u\n", nr_metrics);
00313 }
00314 /*---------------------------------------------------------------------------*/
00315 static void
00316 checkpoint_thread_loop(void *data)
00317 {
00318   uint8_t cmd;
00319   int fd;
00320 
00321   while(1) {
00322     /* Store command and file descriptor on stack */
00323     cmd = preset_cmd;
00324     fd = preset_fd;
00325 
00326     /* Handle command */
00327     if(cmd == COMMAND_ROLLBACK) {
00328       PRINTF_COMMAND("RB:START\n");
00329       thread_rollback(fd);
00330       nr_rollbacks++;
00331       PRINTF_COMMAND("RB:DONE %u\n", nr_rollbacks);
00332       /* TODO Synch before leaving this thread. */
00333     } else if(cmd == COMMAND_CHECKPOINT) {
00334       PRINTF_COMMAND("CP:START\n");
00335       thread_checkpoint(fd);
00336       thread_metrics();
00337       nr_checkpoints++;
00338       PRINTF_COMMAND("CP:DONE %u\n", nr_checkpoints);
00339     } else if(cmd == COMMAND_METRICS) {
00340       thread_metrics();
00341     } else {
00342       printf("ERROR: Unknown thread command: %u\n", cmd);
00343     }
00344 
00345     /* Return to Contiki */
00346     mt_yield();
00347   }
00348 }
00349 /*---------------------------------------------------------------------------*/
00350 int
00351 checkpoint_arch_size()
00352 {
00353   return 10258;
00354 }
00355 /*---------------------------------------------------------------------------*/
00356 void
00357 checkpoint_arch_checkpoint(int fd)
00358 {
00359   PAUSE_TIME_INT();
00360 
00361   preset_cmd = COMMAND_CHECKPOINT;
00362   preset_fd = fd;
00363   mt_exec(&checkpoint_thread);
00364 
00365   RESUME_TIME_INT();
00366 }
00367 /*---------------------------------------------------------------------------*/
00368 void
00369 checkpoint_arch_rollback(int fd)
00370 {
00371   PAUSE_TIME_INT();
00372 
00373   preset_cmd = COMMAND_ROLLBACK;
00374   preset_fd = fd;
00375   mt_exec(&checkpoint_thread);
00376 
00377   RESUME_TIME_INT();
00378 }
00379 /*---------------------------------------------------------------------------*/
00380 static uint8_t inited = 0;
00381 void
00382 checkpoint_arch_init(void)
00383 {
00384   if(inited) {
00385     return;
00386   }
00387 
00388   mt_init();
00389   mt_start(&checkpoint_thread, checkpoint_thread_loop, NULL);
00390   inited = 1;
00391 
00392 #if CHECKPOINT_ROLLBACK_BUTTON
00393   process_start(&checkpoint_button_process, NULL);
00394 #endif /* CHECKPOINT_ROLLBACK_BUTTON */
00395 
00396   /*mt_stop(&checkpoint_thread);*/
00397   /*mt_remove();*/
00398 }
00399 /*---------------------------------------------------------------------------*/
00400 struct power_log {
00401   uint32_t transmit;
00402   uint32_t listen;
00403 };
00404 /*---------------------------------------------------------------------------*/
00405 #if WITH_SERIAL_COMMANDS
00406 static void
00407 serial_interrupt_checkpoint()
00408 {
00409   int fd = 0;
00410   PAUSE_TIME();
00411 
00412   if(SPI_IS_ENABLED()) {
00413     /* SPI is busy, abort */
00414     PRINTF_COMMAND("CP:SPIBUSY\n");
00415     RESUME_TIME();
00416     return;
00417   }
00418 
00419   /* Open file */
00420   cfs_remove("cp");
00421   cfs_coffee_reserve("cp", checkpoint_arch_size());
00422   fd = cfs_open("cp", CFS_WRITE);
00423 
00424   if(fd < 0) {
00425     printf("ERROR: No file access (cp)\n");
00426     RESUME_TIME();
00427     return;
00428   }
00429 
00430   /* Checkpoint */
00431   preset_cmd = COMMAND_CHECKPOINT;
00432   preset_fd = fd;
00433   mt_exec(&checkpoint_thread);
00434 
00435   /* Close file */
00436   cfs_close(fd);
00437 
00438   RESUME_TIME();
00439 }
00440 /*---------------------------------------------------------------------------*/
00441 static void
00442 serial_interrupt_rollback()
00443 {
00444   int fd = 0;
00445   PAUSE_TIME();
00446 
00447   if(SPI_IS_ENABLED()) {
00448     /* SPI is busy, abort */
00449     PRINTF_COMMAND("RB:SPIBUSY\n");
00450     RESUME_TIME();
00451     return;
00452   }
00453 
00454   /* Open file */
00455   fd = cfs_open("cp", CFS_READ);
00456 
00457   if(fd < 0) {
00458     printf("ERROR: No file access (rb)\n");
00459     RESUME_TIME();
00460     return;
00461   }
00462 
00463   /* Rollback */
00464   preset_cmd = COMMAND_ROLLBACK;
00465   preset_fd = fd;
00466   mt_exec(&checkpoint_thread);
00467 
00468   /* Close file */
00469   cfs_close(fd);
00470 
00471   RESUME_TIME();
00472 }
00473 /*---------------------------------------------------------------------------*/
00474 static void
00475 serial_interrupt_metrics()
00476 {
00477   PAUSE_TIME();
00478 
00479   preset_cmd = COMMAND_METRICS;
00480   preset_fd = -1;
00481   mt_exec(&checkpoint_thread);
00482 
00483   RESUME_TIME();
00484 }
00485 /*---------------------------------------------------------------------------*/
00486 static const unsigned char command_checkpoint[] = { 'c', 'p', '\n' };
00487 static const unsigned char command_rollback[] = { 'r', 'b', '\n' };
00488 static const unsigned char command_metrics[] = { 'm', 't', '\n' };
00489 static volatile int command_checkpoint_state = 0;
00490 static volatile int command_rollback_state = 0;
00491 static volatile int command_metrics_state = 0;
00492 /*---------------------------------------------------------------------------*/
00493 static int
00494 serial_input_byte_intercept(unsigned char c)
00495 {
00496   /* Detect checkpoint request */
00497   if(command_checkpoint[command_checkpoint_state] == c) {
00498     command_checkpoint_state++;
00499 
00500     if(command_checkpoint_state == sizeof(command_checkpoint)) {
00501       serial_interrupt_checkpoint();
00502       command_checkpoint_state = 0;
00503     }
00504   } else {
00505     command_checkpoint_state = 0;
00506   }
00507 
00508   /* Detect rollback request */
00509   if(command_rollback[command_rollback_state] == c) {
00510     command_rollback_state++;
00511 
00512     if(command_rollback_state == sizeof(command_rollback)) {
00513       serial_interrupt_rollback();
00514       command_rollback_state = 0;
00515     }
00516   } else {
00517     command_rollback_state = 0;
00518   }
00519 
00520   /* Detect metrics request */
00521   if(command_metrics[command_metrics_state] == c) {
00522     command_metrics_state++;
00523 
00524     if(command_metrics_state == sizeof(command_metrics)) {
00525       serial_interrupt_metrics();
00526       command_metrics_state = 0;
00527     }
00528   } else {
00529     command_metrics_state = 0;
00530   }
00531 
00532   /* Forward to serial line input byte */
00533   return serial_line_input_byte(c);
00534 }
00535 /*---------------------------------------------------------------------------*/
00536 static void
00537 handle_get_command(void)
00538 {
00539   int fd = 0;
00540   fd = cfs_open("cp", CFS_READ);
00541   if(fd < 0) {
00542     printf("ERROR: No file access (get)\n");
00543   } else {
00544     PRINTF_COMMAND("GET:START\n");
00545     char data[8];
00546     int offset=0, size=0, read=8;
00547     unsigned short crc = 0;
00548     cfs_seek(fd, offset, CFS_SEEK_SET);
00549 
00550     while (read == 8) {
00551       int i;
00552 
00553       /*if(offset != cfs_seek(fd, offset, CFS_SEEK_SET)) {
00554         printf("bad seek, breaking\n");
00555         break;
00556       }*/
00557       read = cfs_read(fd, data, 8);
00558       size += read;
00559 
00560       printf("%04i: ", offset); /*REMOVE*/
00561       for (i=0; i < read; i++) {
00562         crc = crc16_add((uint8_t) data[i], crc);
00563         printf("%02x", (uint8_t) (0xff&data[i]));
00564       }
00565       printf("\n");
00566 
00567       offset += 8;
00568     }
00569 
00570     PRINTF_COMMAND("GET:DONE CRC=%u\n", crc);
00571     cfs_close(fd);
00572   }
00573 }
00574 /*---------------------------------------------------------------------------*/
00575 static int
00576 hex_decode_char(char c)
00577 {
00578   if(c >= 'A' && c <= 'F') {
00579     return c - 'A' + 10;
00580   } else if(c >= 'a' && c <= 'f') {
00581     return c - 'a' + 10;
00582   } else if(c >= '0' && c <= '9') {
00583     return c - '0';
00584   } else {
00585     printf("WARN: bad hex: %c\n", c);
00586     return 0;
00587   }
00588 }
00589 /*---------------------------------------------------------------------------*/
00590 PROCESS(checkpoint_serial_process, "Checkpoint via serial commands");
00591 PROCESS_THREAD(checkpoint_serial_process, ev, data)
00592 {
00593   static int set_fd = -1;
00594   static int set_count = -1;
00595 
00596   PROCESS_BEGIN();
00597 
00598   /* Note: 'cp', 'rb', and 'mt' commands are intercepted */
00599   PROCESS_PAUSE();
00600   uart1_set_input(serial_input_byte_intercept);
00601 
00602   /* Format Coffee? */
00603   PRINTF("Formatting Coffee\n");
00604   cfs_coffee_format();
00605   PRINTF("Formatting Coffee... done!\n");
00606 
00607   while(1) {
00608     PROCESS_WAIT_EVENT_UNTIL(ev == serial_line_event_message && data != NULL);
00609 
00610     if(strcmp("set", data) == 0) {
00611       /* TODO Handle set command */
00612       /* Open file */
00613       cfs_remove("cp");
00614       cfs_coffee_reserve("cp", checkpoint_arch_size());
00615       set_fd = cfs_open("cp", CFS_WRITE);
00616       set_count = 0;
00617       if(set_fd < 0) {
00618         printf("SET:FSBUSY\n");
00619       } else {
00620         printf("SET:LINE\n");
00621       }
00622     } else if(set_fd >= 0 && strcmp("set:done", data) == 0) {
00623         cfs_close(set_fd);
00624         set_fd = -1;
00625         if(set_count == 9862) {
00626           printf("SET:DONE\n");
00627         } else {
00628           printf("SET:WRONGSIZE\n");
00629         }
00630     } else if(set_fd >= 0) {
00631       /* We are ready for another line */
00632       printf("SET:LINE\n");
00633       /* Set command: parse hex data */
00634       int len = strlen((char*)data);
00635       if(len > 16 || (len%2)!=0) {
00636         printf("WARN: bad set data: %s\n", (char*)data);
00637       } else {
00638         int i;
00639         for (i=0; i < len; i+=2) {
00640           uint8_t b =
00641             (hex_decode_char(((char*)data)[i]) << 4) +
00642             (hex_decode_char(((char*)data)[i+1]));
00643 
00644           PRINTF("Parsing set command: writing to CFS: %02x\n", b);
00645           write_byte(set_fd, b); /* TODO Check return value */
00646           set_count++;
00647         }
00648       }
00649     } else if(strcmp("", data) == 0 ||
00650         strcmp("cp", data) == 0 ||
00651         strcmp("rb", data) == 0 ||
00652         strcmp("mt", data) == 0) {
00653       /* ignore commands: handled by interrupt */
00654     } else if(strcmp("ping", data) == 0) {
00655       nr_pongs++;
00656       printf("pong %u\n", nr_pongs);
00657     } else if(strcmp("get", data) == 0) {
00658       handle_get_command();
00659     } else {
00660       printf("WARN: Unknown command: '%s'\n", (char*)data);
00661     }
00662   }
00663 
00664   PROCESS_END();
00665 }
00666 #endif /* WITH_SERIAL_COMMANDS */
00667 /*---------------------------------------------------------------------------*/
00668 #if CHECKPOINT_ROLLBACK_BUTTON
00669 PROCESS_THREAD(checkpoint_button_process, ev, data)
00670 {
00671   PROCESS_BEGIN();
00672 
00673   button_sensor.configure(SENSORS_ACTIVE, 1);
00674 
00675   while(1) {
00676     PROCESS_WAIT_EVENT();
00677 
00678     if(ev == sensors_event && data == &button_sensor) {
00679       int fd = 0;
00680 
00681       /* Rollback from Coffee file "cp_wdt" */
00682       fd = cfs_open("cp_wdt", CFS_READ);
00683       if(fd >= 0) {
00684         checkpoint_rollback(fd);
00685         cfs_close(fd);
00686       }
00687     }
00688   }
00689 
00690   PROCESS_END();
00691 }
00692 #endif /* CHECKPOINT_ROLLBACK_BUTTON */