Contiki 3.x
store.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include "store.h"
5 #include "contiki.h"
6 #include "cfs/cfs.h"
7 #include "cfs/cfs-coffee.h"
8 #include "cfs-coffee-arch.h"
9 #include "pb_decode.h"
10 #include "pb_encode.h"
11 #include "math.h"
12 
13 #ifdef SPI_LOCKING
14  #include "cc1120.h"
15  #include "cc1120-arch.h"
16 #endif
17 
18 #define DEBUG_ON
19 #include "debug.h"
20 
21 /**
22  * Filename of the config file
23  */
24 #define CONFIG_FILENAME "conf"
25 
26 /**
27  * Maximum length of a file name is Coffee's max length (including null terminator)
28  */
29 #define FILENAME_LENGTH COFFEE_NAME_LENGTH
30 
31 // Ensure that filenames are long enough to store the config
32 // FILENAME_LENGTH - 1 to include null terminator
33 _Static_assert(strlen(CONFIG_FILENAME) <= (FILENAME_LENGTH - 1), "FILENAME_LENGTH too small to store config filename");
34 
35 // Ensure that filenames are long enough to store the highest amount of samples we can have (1 per page)
36 // pow(10.0, x) is 1 more than the max value that fits in x digits
37 _Static_assert((COFFEE_SIZE / COFFEE_PAGE_SIZE) <= (pow(10.0, (double) FILENAME_LENGTH - 1) - 1), "FILENAME_LENGTH too small to store all samples");
38 
39 /**
40  * Directory we store things in. Coffee only supports one directory
41  */
42 #define DIRECTORY "/"
43 
44 /**
45  * Magic value we append to all files we write.
46  * This ensures trailing NULL bytes are not removed when the
47  * file is read from flash again (this only happens if
48  * the file isn't cached - ie on a fresh boot, or if it hasn't
49  * been openned in a while).
50  */
51 static const uint8_t END_CANARY = 0xAF;
52 
53 /**
54  * Identifier of the last sample.
55  */
56 static uint16_t last_id;
57 
58 /**
59  * Lock the radio for cfs access.
60  */
61 static void radio_lock(void);
62 
63 /**
64  * Release the radio after cfs access.
65  */
66 static void radio_release(void);
67 
68 /**
69  * Find the id of the latest sample.
70  */
71 static uint16_t find_latest_sample(void);
72 
73 /**
74  * Read a given file.
75  * @return The number of bytes read succesfully from filename, or false if the file could not be read.
76  * NOTE: This will strip the trailing canary byte appended by `write_file`.
77  */
78 static uint8_t read_file(char *filename, uint8_t *buffer, uint8_t length);
79 
80 /**
81  * Write to a given file.
82  * @return true if length bytes have been successfully written to filename, false otherwise.
83  * NOTE: This will append a trailing canary byte that will be stripped by `read_file`.
84  */
85 static bool write_file(char *filename, uint8_t *buffer, uint8_t length);
86 
87 /**
88  * Convert a sample id to a filename.
89  * @return The pointer to filename(usefull for avoiding temp vars).
90  */
91 static char* id_to_file(uint16_t id, char* filename);
92 
93 /**
94  * Convert a filename to a sample id.
95  * @param id Pointer to write the parsed id to. Only used when the filename is a sample id.
96  * @return True if the filename is a sample id, false otherwise.
97  */
98 static bool file_to_id(char *filename, uint16_t *id);
99 
100 uint16_t store_save_sample(Sample *sample) {
101  pb_ostream_t pb_ostream;
102  uint8_t pb_buffer[Sample_size];
103  char filename[FILENAME_LENGTH];
104 
105  last_id++;
106 
107  sample->id = last_id;
108 
109  DEBUG("Attempting to save reading with id %d\n", last_id);
110 
111  pb_ostream = pb_ostream_from_buffer(pb_buffer, sizeof(pb_buffer));
112  if (!pb_encode_delimited(&pb_ostream, Sample_fields, sample)) {
113  last_id--;
114  return false;
115  }
116 
117  radio_lock();
118 
119  if (!write_file(id_to_file(last_id, filename), pb_buffer, pb_ostream.bytes_written)) {
120  DEBUG("Failed to save reading %d\n", last_id);
121  last_id--;
122  radio_release();
123  return false;
124  }
125 
126  radio_release();
127 
128  return last_id;
129 }
130 
131 bool store_get_latest_sample(Sample *sample) {
132  return store_get_sample(last_id, sample);
133 }
134 
135 uint8_t store_get_latest_raw_sample(uint8_t buffer[Sample_size]) {
136  return store_get_raw_sample(last_id, buffer);
137 }
138 
139 bool store_get_sample(uint16_t id, Sample *sample) {
140  pb_istream_t pb_istream;
141  uint8_t pb_buffer[Sample_size];
142 
143  if (store_get_raw_sample(id, pb_buffer)) {
144  return false;
145  }
146 
147  pb_istream = pb_istream_from_buffer(pb_buffer, sizeof(pb_buffer));
148  if (!pb_decode_delimited(&pb_istream, Sample_fields, sample)) {
149  return false;
150  }
151 
152  return true;
153 }
154 
155 uint8_t store_get_raw_sample(uint16_t id, uint8_t buffer[Sample_size]) {
156  char filename[FILENAME_LENGTH];
157  uint8_t bytes;
158 
159  DEBUG("Attempting to get sample %d\n", id);
160 
161  radio_lock();
162 
163  bytes = read_file(id_to_file(id, filename), buffer, Sample_size);
164 
165  radio_release();
166 
167  return bytes;
168 }
169 
171  return last_id;
172 }
173 
174 bool store_delete_sample(uint16_t sample) {
175  int fd = 0;
176  char filename[FILENAME_LENGTH];
177 
178  if (sample < 1) {
179  DEBUG("Attempting to delete invalid sample %d\n", sample);
180  return false;
181  }
182 
183  DEBUG("Attempting to delete sample %d\n", sample);
184 
185  id_to_file(sample, filename);
186 
187  radio_lock();
188 
189  if (cfs_remove(filename) == -1) {
190  DEBUG("Error deleting sample %d\n", sample);
191  radio_release();
192  return false;
193  }
194 
195  // If this file used to be last_id, find the previous existing file (if any)
196  if (sample == last_id) {
197  DEBUG("Sample %d is last known sample. Searching for previous sample...\n", sample);
198 
199  last_id--;
200 
201  id_to_file(last_id, filename);
202 
203  // Keep going until we reach a file that exists
204  while (last_id != 0 && (fd = cfs_open(filename, CFS_READ)) < 0) {
205  //DEBUG("Sample %d does not exist, continuing..\n", last_id);
206  last_id--;
207  id_to_file(last_id, filename);
208  }
209 
210  cfs_close(fd);
211  }
212 
213  DEBUG("Sample %d deleted. Last_id is now %d\n", sample, last_id);
214 
215  radio_release();
216 
217  return true;
218 }
219 
220 bool store_save_config(SensorConfig *config) {
221  pb_ostream_t pb_ostream;
222  uint8_t pb_buffer[SensorConfig_size];
223 
224  DEBUG("Attempting to save config\n");
225 
226  pb_ostream = pb_ostream_from_buffer(pb_buffer, sizeof(pb_buffer));
227 
228  if (!pb_encode_delimited(&pb_ostream, SensorConfig_fields, config)) {
229  return false;
230  }
231 
232  radio_lock();
233 
234  // Delete the old config first, avoids the need for micro-logs
235  if (cfs_remove(CONFIG_FILENAME) == -1) {
236  DEBUG("Error deleting old config\n");
237  }
238 
239  if (!write_file(CONFIG_FILENAME, pb_buffer, pb_ostream.bytes_written)) {
240  radio_release();
241  return false;
242  }
243 
244  radio_release();
245 
246  return true;
247 }
248 
249 bool store_get_config(SensorConfig *config) {
250  pb_istream_t pb_istream;
251  uint8_t pb_buffer[SensorConfig_size];
252 
253  if (!store_get_raw_config(pb_buffer)) {
254  return false;
255  }
256 
257  pb_istream = pb_istream_from_buffer(pb_buffer, sizeof(pb_buffer));
258 
259  return pb_decode_delimited(&pb_istream, SensorConfig_fields, config);
260 }
261 
262 uint8_t store_get_raw_config(uint8_t buffer[SensorConfig_size]) {
263  uint8_t bytes;
264 
265  DEBUG("Attempting to get config\n");
266 
267  radio_lock();
268 
269  bytes = read_file(CONFIG_FILENAME, buffer, SensorConfig_size);
270 
271  radio_release();
272 
273  return bytes;
274 }
275 
276 uint8_t read_file(char *filename, uint8_t *buffer, uint8_t length) {
277  int fd = cfs_open(filename, CFS_READ);
278 
279  if (fd < 0) {
280  DEBUG("Failed to open file %s\n", filename);
281  return false;
282  }
283 
284  uint8_t bytes = cfs_read(fd, buffer, length);
285 
286  cfs_close(fd);
287 
288  if (bytes <= 0) {
289  DEBUG("Failed to read file %s\n", filename);
290  return false;
291  }
292 
293  // Ignore the end canary value if there is one (backwards compability for files that don't have a canary)
294  if (buffer[bytes-1] == END_CANARY) {
295  bytes--;
296  }
297 
298  DEBUG("%d bytes read\n", bytes);
299 
300  return bytes;
301 }
302 
303 bool write_file(char *filename, uint8_t *buffer, uint8_t length) {
304  int fd;
305  int bytes;
306 
307  if (cfs_coffee_reserve(filename, length + sizeof(END_CANARY)) < 0) {
308  DEBUG("Failed to reserve space for file %s\n", filename);
309  }
310 
311  fd = cfs_open(filename, CFS_WRITE);
312 
313  if (fd < 0) {
314  DEBUG("Failed to open file %s for writing\n", filename);
315  return false;
316  }
317 
318  // Instruct coffee to never extend a file - we've always reserved enough space
320 
321  bytes = cfs_write(fd, buffer, length);
322 
323  if (bytes != length) {
324  DEBUG("Failed to write file to %s, wrote %d bytes\n", filename, bytes);
325  return false;
326  }
327 
328  // Write the trailing magic value
329  if (cfs_write(fd, &END_CANARY, 1) == -1) {
330  DEBUG("Failed to write trailing canary to file %s\n", filename);
331  return false;
332  }
333 
334  DEBUG("%d bytes written\n", bytes);
335  cfs_close(fd);
336  return true;
337 }
338 
339 void store_init(void) {
340  DEBUG("Initializing...\n");
341  radio_lock();
342  last_id = find_latest_sample();
343  radio_release();
344  printf("Store initialized. %d previous files found.\n", last_id);
345 }
346 
347 void radio_lock(void) {
348 #ifdef SPI_LOCKING
349  NETSTACK_MAC.off(0);
350  cc1120_arch_interrupt_disable();
351  CC1120_LOCK_SPI();
352  DEBUG("Radio Locked\n");
353 #endif
354 }
355 
356 void radio_release(void) {
357 #ifdef SPI_LOCKING
358  CC1120_RELEASE_SPI();
359  cc1120_arch_interrupt_enable();
360  NETSTACK_MAC.on();
361  DEBUG("Radio Unlocked\n");
362 #endif
363 }
364 
365 uint16_t find_latest_sample(void) {
366  struct cfs_dirent dirent;
367  struct cfs_dir dir;
368  uint16_t max_id = 0;
369 
370  DEBUG("Refreshing filename cache\n");
371 
372  if (cfs_opendir(&dir, "/") == 0) {
373 
374  while (cfs_readdir(&dir, &dirent) != -1) {
375  uint16_t file_id;
376 
377  if (file_to_id(dirent.name, &file_id)) {
378 
379  if(file_id > max_id) {
380  max_id = file_id;
381  }
382 
383  //DEBUG("File %s has sample id %u (Max id %u)\n", dirent.name, file_id, max_id);
384  }
385  }
386 
387  cfs_closedir(&dir);
388  }
389 
390  return max_id;
391 }
392 
393 char* id_to_file(uint16_t id, char* filename) {
394  sprintf(filename, "%u", id);
395  return filename;
396 }
397 
398 bool file_to_id(char *filename, uint16_t *id) {
399  if (strlen(filename) <= 0) {
400  return false;
401  }
402 
403  bool is_sample = true;
404 
405  // Check all the characters are digits
406  int i;
407  for (i = 0; i < strlen(filename); i++) {
408  // isdigit() returns > 0 on success (but not 1)
409  is_sample &= (isdigit((unsigned char) filename[i]) > 0);
410  }
411 
412  if (is_sample) {
413  *id = atoi(filename);
414  }
415 
416  return is_sample;
417 }
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:1011
#define CFS_WRITE
Specify that cfs_open() should open a file for writing.
Definition: cfs.h:104
bool store_get_latest_sample(Sample *sample)
Get the most recent sample from the flash.
Definition: store.c:131
bool store_save_config(SensorConfig *config)
Save the configuration to flash.
Definition: store.c:220
int cfs_opendir(struct cfs_dir *dir, const char *name)
Open a directory for reading directory entries.
Definition: cfs-coffee.c:1283
void cfs_close(int fd)
Close an open file.
Definition: cfs-coffee.c:1047
bool store_get_config(SensorConfig *config)
Get the configuration from the flash.
Definition: store.c:249
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
#define COFFEE_SIZE
Total size in bytes of the file system.
uint8_t store_get_raw_sample(uint16_t id, uint8_t buffer[Sample_size])
Get a given sample from the flash, in the form of an encoded protocol buffer.
Definition: store.c:155
uint16_t store_save_sample(Sample *sample)
Store a sample in the flash.
Definition: store.c:100
int cfs_coffee_set_io_semantics(int fd, unsigned flags)
Set the I/O semantics for accessing a file.
bool store_get_sample(uint16_t id, Sample *sample)
Get a given sample from the flash,.
Definition: store.c:139
Convenience layer for storing readings and the config.
bool store_delete_sample(uint16_t id)
Delete a given sample from the flash.
Definition: store.c:174
void store_init(void)
Initialize the data store.
Definition: store.c:339
int cfs_readdir(struct cfs_dir *dir, struct cfs_dirent *record)
Read a directory entry.
Definition: cfs-coffee.c:1294
#define CFS_COFFEE_IO_FIRM_SIZE
Instruct Coffee not to attempt to extend the file upon a request to write past the reserved file size...
Definition: cfs-coffee.h:64
uint8_t store_get_raw_config(uint8_t buffer[SensorConfig_size])
Get the configuration from the flash.
Definition: store.c:262
int cfs_remove(const char *name)
Remove a file.
Definition: cfs-coffee.c:1094
#define COFFEE_PAGE_SIZE
Logical page size.
Header for the Coffee file system.
int cfs_coffee_reserve(const char *name, cfs_offset_t size)
Reserve space for a file.
Definition: cfs-coffee.c:1326
void cfs_closedir(struct cfs_dir *dir)
Close a directory opened with cfs_opendir().
Definition: cfs-coffee.c:1320
CFS header file.
uint8_t store_get_latest_raw_sample(uint8_t buffer[Sample_size])
Get the most recent sample from the flash, in the form of an encoded protocol buffer.
Definition: store.c:135
uint16_t store_get_latest_sample_id(void)
Get the identifer of the most recent sample stored.
Definition: store.c:170