Contiki 2.5
cfs-coffee-arch.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, Swedish Institute of 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 /**
34  * \file
35  * Coffee architecture-dependent functionality for the AVR-Raven 1284p platform.
36  * \author
37  * Nicolas Tsiftes <nvt@sics.se>
38  * Frederic Thepaut <frederic.thepaut@inooi.com>
39  * David Kopf <dak664@embarqmail.com>
40  */
41 
42 #include <avr/boot.h>
43 #include <avr/interrupt.h>
44 #include <avr/pgmspace.h>
45 #include <string.h>
46 #include "dev/watchdog.h"
47 
48 #include "cfs-coffee-arch.h"
49 
50 #define DEBUG 0
51 #if DEBUG
52 #include <stdio.h>
53 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
54 #else
55 #define PRINTF(...)
56 #endif
57 
58 #define TESTCOFFEE 1
59 #define DEBUG_CFS 1
60 #if TESTCOFFEE
61 #if DEBUG_CFS
62 #include <stdio.h>
63 #define PRINTF_CFS(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
64 #else
65 #define PRINTF_CFS(...)
66 #endif
67 
68 #include "cfs/cfs.h"
69 #include "cfs/cfs-coffee.h"
70 #include "lib/crc16.h"
71 #include "lib/random.h"
72 #include <stdio.h>
73 
74 #define FAIL(x) error = (x); goto end;
75 
76 #define FILE_SIZE 512
77 
78 int
79 coffee_file_test(void)
80 {
81  int error;
82  int wfd, rfd, afd;
83  unsigned char buf[256], buf2[11];
84  int32_t r, i, j, total_read;
85  CFS_CONF_OFFSET_TYPE offset;
86 
87  cfs_remove("T1");
88  cfs_remove("T2");
89  cfs_remove("T3");
90  cfs_remove("T4");
91  cfs_remove("T5");
92 
93  wfd = rfd = afd = -1;
94 
95  for(r = 0; r < sizeof(buf); r++) {
96  buf[r] = r;
97  }
98 
99  /* Test 1: Open for writing. */
100  wfd = cfs_open("T1", CFS_WRITE);
101  if(wfd < 0) {
102  FAIL(-1);
103  }
104 
105  /* Test 2: Write buffer. */
106  r = cfs_write(wfd, buf, sizeof(buf));
107  if(r < 0) {
108  FAIL(-2);
109  } else if(r < sizeof(buf)) {
110  FAIL(-3);
111  }
112 
113  /* Test 3: Deny reading. */
114  r = cfs_read(wfd, buf, sizeof(buf));
115  if(r >= 0) {
116  FAIL(-4);
117  }
118 
119  /* Test 4: Open for reading. */
120  rfd = cfs_open("T1", CFS_READ);
121  if(rfd < 0) {
122  FAIL(-5);
123  }
124 
125  /* Test 5: Write to read-only file. */
126  r = cfs_write(rfd, buf, sizeof(buf));
127  if(r >= 0) {
128  FAIL(-6);
129  }
130 
131  /* Test 7: Read the buffer written in Test 2. */
132  memset(buf, 0, sizeof(buf));
133  r = cfs_read(rfd, buf, sizeof(buf));
134  if(r < 0) {
135  FAIL(-8);
136  } else if(r < sizeof(buf)) {
137  PRINTF_CFS("r=%d\n", r);
138  FAIL(-9);
139  }
140 
141  /* Test 8: Verify that the buffer is correct. */
142  for(r = 0; r < sizeof(buf); r++) {
143  if(buf[r] != r) {
144  PRINTF_CFS("r=%d. buf[r]=%d\n", r, buf[r]);
145  FAIL(-10);
146  }
147  }
148 
149  /* Test 9: Seek to beginning. */
150  if(cfs_seek(wfd, 0, CFS_SEEK_SET) != 0) {
151  FAIL(-11);
152  }
153 
154  /* Test 10: Write to the log. */
155  r = cfs_write(wfd, buf, sizeof(buf));
156  if(r < 0) {
157  FAIL(-12);
158  } else if(r < sizeof(buf)) {
159  FAIL(-13);
160  }
161 
162  /* Test 11: Read the data from the log. */
163  cfs_seek(rfd, 0, CFS_SEEK_SET);
164  memset(buf, 0, sizeof(buf));
165  r = cfs_read(rfd, buf, sizeof(buf));
166  if(r < 0) {
167  FAIL(-14);
168  } else if(r < sizeof(buf)) {
169  FAIL(-15);
170  }
171 
172  /* Test 12: Verify that the data is correct. */
173  for(r = 0; r < sizeof(buf); r++) {
174  if(buf[r] != r) {
175  FAIL(-16);
176  }
177  }
178 
179  /* Test 13: Write a reversed buffer to the file. */
180  for(r = 0; r < sizeof(buf); r++) {
181  buf[r] = sizeof(buf) - r - 1;
182  }
183  if(cfs_seek(wfd, 0, CFS_SEEK_SET) != 0) {
184  FAIL(-17);
185  }
186  r = cfs_write(wfd, buf, sizeof(buf));
187  if(r < 0) {
188  FAIL(-18);
189  } else if(r < sizeof(buf)) {
190  FAIL(-19);
191  }
192  if(cfs_seek(rfd, 0, CFS_SEEK_SET) != 0) {
193  FAIL(-20);
194  }
195 
196  /* Test 14: Read the reversed buffer. */
197  cfs_seek(rfd, 0, CFS_SEEK_SET);
198  memset(buf, 0, sizeof(buf));
199  r = cfs_read(rfd, buf, sizeof(buf));
200  if(r < 0) {
201  FAIL(-21);
202  } else if(r < sizeof(buf)) {
203  PRINTF_CFS("r = %d\n", r);
204  FAIL(-22);
205  }
206 
207  /* Test 15: Verify that the data is correct. */
208  for(r = 0; r < sizeof(buf); r++) {
209  if(buf[r] != sizeof(buf) - r - 1) {
210  FAIL(-23);
211  }
212  }
213 
214  cfs_close(rfd);
215  cfs_close(wfd);
216 
217  if(cfs_coffee_reserve("T2", FILE_SIZE) < 0) {
218  FAIL(-24);
219  }
220 
221  /* Test 16: Test multiple writes at random offset. */
222  for(r = 0; r < 100; r++) {
223  wfd = cfs_open("T2", CFS_WRITE | CFS_READ);
224  if(wfd < 0) {
225  FAIL(-25);
226  }
227 
228  offset = random_rand() % FILE_SIZE;
229 
230  for(r = 0; r < sizeof(buf); r++) {
231  buf[r] = r;
232  }
233 
234  if(cfs_seek(wfd, offset, CFS_SEEK_SET) != offset) {
235  FAIL(-26);
236  }
237 
238  if(cfs_write(wfd, buf, sizeof(buf)) != sizeof(buf)) {
239  FAIL(-27);
240  }
241 
242  if(cfs_seek(wfd, offset, CFS_SEEK_SET) != offset) {
243  FAIL(-28);
244  }
245 
246  memset(buf, 0, sizeof(buf));
247  if(cfs_read(wfd, buf, sizeof(buf)) != sizeof(buf)) {
248  FAIL(-29);
249  }
250 
251  for(i = 0; i < sizeof(buf); i++) {
252  if(buf[i] != i) {
253  PRINTF_CFS("buf[%d] != %d\n", i, buf[i]);
254  FAIL(-30);
255  }
256  }
257  }
258  /* Test 17: Append data to the same file many times. */
259 #define APPEND_BYTES 3000
260 #define BULK_SIZE 10
261  for (i = 0; i < APPEND_BYTES; i += BULK_SIZE) {
262  afd = cfs_open("T3", CFS_WRITE | CFS_APPEND);
263  if (afd < 0) {
264  FAIL(-31);
265  }
266  for (j = 0; j < BULK_SIZE; j++) {
267  buf[j] = 1 + ((i + j) & 0x7f);
268  }
269  if ((r = cfs_write(afd, buf, BULK_SIZE)) != BULK_SIZE) {
270  PRINTF_CFS("Count:%d, r=%d\n", i, r);
271  FAIL(-32);
272  }
273  cfs_close(afd);
274  }
275 
276  /* Test 18: Read back the data written in Test 17 and verify that it
277  is correct. */
278  afd = cfs_open("T3", CFS_READ);
279  if(afd < 0) {
280  FAIL(-33);
281  }
282  total_read = 0;
283  while((r = cfs_read(afd, buf2, sizeof(buf2))) > 0) {
284  for(j = 0; j < r; j++) {
285  if(buf2[j] != 1 + ((total_read + j) & 0x7f)) {
286  FAIL(-34);
287  }
288  }
289  total_read += r;
290  }
291  if(r < 0) {
292  PRINTF_CFS("FAIL:-35 r=%d\n",r);
293  FAIL(-35);
294  }
295  if(total_read != APPEND_BYTES) {
296  PRINTF_CFS("FAIL:-35 total_read=%d\n",total_read);
297  FAIL(-35);
298  }
299  cfs_close(afd);
300 
301 /***************T4********************/
302 /* file T4 and T5 writing forces to use garbage collector in greedy mode
303  * this test is designed for 10kb of file system
304  * */
305 #define APPEND_BYTES_1 2000
306 #define BULK_SIZE_1 10
307  for (i = 0; i < APPEND_BYTES_1; i += BULK_SIZE_1) {
308  afd = cfs_open("T4", CFS_WRITE | CFS_APPEND);
309  if (afd < 0) {
310  FAIL(-36);
311  }
312  for (j = 0; j < BULK_SIZE_1; j++) {
313  buf[j] = 1 + ((i + j) & 0x7f);
314  }
315 
316 
317  if ((r = cfs_write(afd, buf, BULK_SIZE_1)) != BULK_SIZE_1) {
318  PRINTF_CFS("Count:%d, r=%d\n", i, r);
319  FAIL(-37);
320  }
321  cfs_close(afd);
322  }
323 
324  afd = cfs_open("T4", CFS_READ);
325  if(afd < 0) {
326  FAIL(-38);
327  }
328  total_read = 0;
329  while((r = cfs_read(afd, buf2, sizeof(buf2))) > 0) {
330  for(j = 0; j < r; j++) {
331  if(buf2[j] != 1 + ((total_read + j) & 0x7f)) {
332  PRINTF_CFS("FAIL:-39, total_read=%d r=%d\n",total_read,r);
333  FAIL(-39);
334  }
335  }
336  total_read += r;
337  }
338  if(r < 0) {
339  PRINTF_CFS("FAIL:-40 r=%d\n",r);
340  FAIL(-40);
341  }
342  if(total_read != APPEND_BYTES_1) {
343  PRINTF_CFS("FAIL:-41 total_read=%d\n",total_read);
344  FAIL(-41);
345  }
346  cfs_close(afd);
347  /***************T5********************/
348 #define APPEND_BYTES_2 1000
349 #define BULK_SIZE_2 10
350  for (i = 0; i < APPEND_BYTES_2; i += BULK_SIZE_2) {
351  afd = cfs_open("T5", CFS_WRITE | CFS_APPEND);
352  if (afd < 0) {
353  FAIL(-42);
354  }
355  for (j = 0; j < BULK_SIZE_2; j++) {
356  buf[j] = 1 + ((i + j) & 0x7f);
357  }
358 
359  if ((r = cfs_write(afd, buf, BULK_SIZE_2)) != BULK_SIZE_2) {
360  PRINTF_CFS("Count:%d, r=%d\n", i, r);
361  FAIL(-43);
362  }
363 
364  cfs_close(afd);
365  }
366 
367  afd = cfs_open("T5", CFS_READ);
368  if(afd < 0) {
369  FAIL(-44);
370  }
371  total_read = 0;
372  while((r = cfs_read(afd, buf2, sizeof(buf2))) > 0) {
373  for(j = 0; j < r; j++) {
374  if(buf2[j] != 1 + ((total_read + j) & 0x7f)) {
375  PRINTF_CFS("FAIL:-45, total_read=%d r=%d\n",total_read,r);
376  FAIL(-45);
377  }
378  }
379  total_read += r;
380  }
381  if(r < 0) {
382  PRINTF_CFS("FAIL:-46 r=%d\n",r);
383  FAIL(-46);
384  }
385  if(total_read != APPEND_BYTES_2) {
386  PRINTF_CFS("FAIL:-47 total_read=%d\n",total_read);
387  FAIL(-47);
388  }
389  cfs_close(afd);
390 
391  error = 0;
392 end:
393  cfs_close(wfd); cfs_close(rfd); cfs_close(afd);
394 
395  return error;
396 }
397 #endif /* TESTCOFFEE */
398 
399 /*---------------------------------------------------------------------------*/
400 /*---------------------------EEPROM ROUTINES---------------------------------*/
401 /*---------------------------------------------------------------------------*/
402 #ifdef COFFEE_AVR_EEPROM
403 
404 /* Letting .bss initialize nullb to zero saves COFFEE_SECTOR_SIZE of flash */
405 //static const unsigned char nullb[COFFEE_SECTOR_SIZE] = {0};
406 static const unsigned char nullb[COFFEE_SECTOR_SIZE];
407 
408 /*---------------------------------------------------------------------------*/
409 /* Erase EEPROM sector
410  */
411 void
412 avr_eeprom_erase(uint16_t sector)
413 {
414  eeprom_write(COFFEE_START + sector * COFFEE_SECTOR_SIZE,
415  (unsigned char *)nullb, sizeof(nullb));
416 }
417 #endif /* COFFEE_AVR_EEPROM */
418 
419 #ifdef COFFEE_AVR_FLASH
420 /*---------------------------------------------------------------------------*/
421 /*---------------------------FLASH ROUTINES----------------------------------*/
422 /*---------------------------------------------------------------------------*/
423 /*
424  * Read from flash info buf. addr contains starting flash byte address
425  */
426 void
427 avr_flash_read(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
428 {
429  uint32_t addr32=COFFEE_START+addr;
430  uint16_t isize=size;
431 #if DEBUG
432  unsigned char *bufo=(unsigned char *)buf;
433  uint8_t i;
434  uint16_t w=addr32>>1; //Show progmem word address for debug
435  PRINTF("r0x%04x(%u) ",w,size);
436 #endif
437 #ifndef FLASH_WORD_READS
438  for (;isize>0;isize--) {
439 #if FLASH_COMPLEMENT_DATA
440  *buf++=~(uint8_t)pgm_read_byte_far(addr32++);
441 #else
442  *buf++=(uint8_t)pgm_read_byte_far(addr32++);
443 #endif /*FLASH_COMPLEMENT_DATA*/
444  }
445 #else
446 /* 130 bytes more PROGMEM, but faster */
447  if (isize&0x01) { //handle first odd byte
448 #if FLASH_COMPLEMENT_DATA
449  *buf++=~(uint8_t)pgm_read_byte_far(addr32++);
450 #else
451  *buf++=(uint8_t)pgm_read_byte_far(addr32++);
452 #endif /*FLASH_COMPLEMENT_DATA*/
453  isize--;
454  }
455  for (;isize>1;isize-=2) {//read words from flash
456 #if FLASH_COMPLEMENT_DATA
457  *(uint16_t *)buf=~(uint16_t)pgm_read_word_far(addr32);
458 #else
459  *(uint16_t *)buf=(uint16_t)pgm_read_word_far(addr32);
460 #endif /*FLASH_COMPLEMENT_DATA*/
461  buf+=2;
462  addr32+=2;
463  }
464  if (isize) { //handle last odd byte
465 #if FLASH_COMPLEMENT_DATA
466  *buf++=~(uint8_t)pgm_read_byte_far(addr32);
467 #else
468  *buf++=(uint8_t)pgm_read_byte_far(addr32);
469 #endif /*FLASH_COMPLEMENT_DATA*/
470  }
471 #endif /* FLASH_WORD_READS */
472 
473 #if DEBUG>1
474  PRINTF("\nbuf=");
475 // PRINTF("%s",bufo);
476 // for (i=0;i<16;i++) PRINTF("%2x ",*bufo++);
477 #endif
478 }
479 /*---------------------------------------------------------------------------*/
480 /*
481  Erase the flash page(s) corresponding to the coffee sector.
482  This is done by calling the write routine with a null buffer and any address
483  within each page of the sector (we choose the first byte).
484  */
485 BOOTLOADER_SECTION
486 void avr_flash_erase(coffee_page_t sector) {
487  coffee_page_t i;
488 
489 #if FLASH_COMPLEMENT_DATA
490  uint32_t addr32;
491  volatile uint8_t sreg;
492 
493  // Disable interrupts.
494  sreg = SREG;
495  cli();
496 
497  for (i = 0; i < COFFEE_SECTOR_SIZE / COFFEE_PAGE_SIZE; i++) {
498  for (addr32 = COFFEE_START + (((sector + i) * COFFEE_PAGE_SIZE)
499  & ~(COFFEE_PAGE_SIZE - 1)); addr32 < (COFFEE_START + (((sector
500  + i + 1) * COFFEE_PAGE_SIZE) & ~(COFFEE_PAGE_SIZE - 1))); addr32
501  += SPM_PAGESIZE) {
502  boot_page_erase(addr32);
503  boot_spm_busy_wait();
504 
505  }
506  }
507  //RE-enable interrupts
508  boot_rww_enable();
509  SREG = sreg;
510 #else
511  for (i=0;i<COFFEE_SECTOR_SIZE/COFFEE_PAGE_SIZE;i++) {
512  avr_flash_write((sector+i)*COFFEE_PAGE_SIZE,0,0);
513  }
514 #endif
515 
516 #if 0
517 #if TESTCOFFEE
518 /* Defining TESTCOFFEE is a convenient way of testing a new configuration.
519  * It is triggered by an erase of the last sector.
520  * Note this routine will be reentered during the test! */
521 
522  if ((sector+i)==COFFEE_PAGES-1) {
523  int j=(int)(COFFEE_START>>1),k=(int)((COFFEE_START>>1)+(COFFEE_SIZE>>1)),l=(int)(COFFEE_SIZE/1024UL);
524  printf_P(PSTR("\nTesting coffee filesystem [0x%08x -> 0x%08x (%uKb)] ..."),j,k,l);
525  int r= coffee_file_test();
526  if (r<0) {
527  printf_P(PSTR("\nFailed with return %d! :-(\n"),r);
528  } else {
529  printf_P(PSTR("Passed! :-)\n"));
530  }
531  }
532 #endif /* TESTCOFFEE */
533 #endif
534 }
535 
536 /*httpd-fs routines
537  getchar is straigtforward.
538  strcmp only needs to handle file names for fs_open. Note filename in buf will not be zero terminated
539  if it fills the coffee name field, so a pseudo strcmp is done here.
540  strchr searches for script starts so must handle arbitrarily large strings
541  */
542 char avr_httpd_fs_getchar(char *addr) {
543  char r;
544  avr_flash_read((CFS_CONF_OFFSET_TYPE) addr, (uint8_t*) &r, 1);
545  return r;
546 }
547 int avr_httpd_fs_strcmp (char *ram, char *addr) {
548  uint8_t i,*in,buf[32];
549  avr_flash_read((CFS_CONF_OFFSET_TYPE)addr, buf, sizeof(buf));
550 //return strcmp(ram, (char *)buf);
551  in=(uint8_t *)ram;
552  for (i=0;i<32;i++) {
553  if (buf[i]==0) return(0);
554  if (buf[i]!=*in) break;
555  in++;
556  }
557 /* A proper strcmp would return a + or minus number based on the last comparison*/
558 //if (buf[i]>*in) return(i); else return(-i);
559  return(i);
560 }
561 char * avr_httpd_fs_strchr (char *addr, int character) {
562  char buf[129],*pptr;
563  buf[128]=character;
564  while (1) {
565  avr_flash_read((CFS_CONF_OFFSET_TYPE)addr, (uint8_t *) buf, 128);
566  pptr=strchr(buf, character);
567  if (pptr!=&buf[128]) {
568  if (pptr==0) return 0;
569  return (addr+(pptr-buf));
570  }
571  addr+=128;
572  }
573 
574 }
575 
576 /*---------------------------------------------------------------------------*/
577 /*
578  * Transfer buf[size] from RAM to flash, starting at addr.
579  * If buf is null, just erase the flash page
580  * Note this routine has to be in the bootloader NRWW part of program memory,
581  * and that writing to NRWW (last 32 pages on the 1284p) will halt the CPU.
582  */
583 BOOTLOADER_SECTION
584 void
585 avr_flash_write(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
586 {
587  uint32_t addr32;
588  uint16_t w;
589  uint8_t bb,ba,sreg;
590 
591  /* Disable interrupts, make sure no eeprom write in progress */
592  sreg = SREG;
593  cli();
594  eeprom_busy_wait();
595 
596  /* Calculate the starting address of the first flash page being
597  modified (will be on a page boundary) and the number of
598  unaltered bytes before and after the data to be written. */
599 #if 0 //this is 8 bytes longer
600  uint16_t startpage=addr/COFFEE_PAGE_SIZE;
601  addr32=COFFEE_START+startpage*COFFEE_PAGE_SIZE;
602 #else
603  addr32=(COFFEE_ADDRESS&~(SPM_PAGESIZE-1))+(addr&~(SPM_PAGESIZE-1));
604 #endif
605  bb=addr & (SPM_PAGESIZE-1);
606  ba=COFFEE_PAGE_SIZE-((addr+size)&0xff);
607 
608 #if DEBUG
609  uint16_t startpage=addr/COFFEE_PAGE_SIZE;
610  w=addr32>>1; //Show progmem word address for debug
611  if (buf) {
612  PRINTF("w0x%04x %u %u %u",w,size,bb,ba);
613  } else {
614  PRINTF("e0x%04x %u ",w,startpage);
615  }
616 #endif
617 
618  /* If buf not null, modify the page(s) */
619  if (buf) {
620  if (size==0) return; //nothing to write
621  /*Copy the first part of the existing page into the write buffer */
622  while (bb>1) {
623  w=pgm_read_word_far(addr32);
624  boot_page_fill(addr32,w);
625  addr32+=2;
626  bb-=2;
627  }
628  /* Transfer the bytes to be modified */
629  while (size>1) {
630  if (bb) { //handle odd byte boundary
631  w=pgm_read_word_far(addr32);
632 #if FLASH_COMPLEMENT_DATA
633  w = ~w;
634 #endif /*FLASH_COMPLEMENT_DATA*/
635  w &= 0xff;
636  bb=0;
637  size++;
638  } else {
639  w = *buf++;
640  }
641  w += (*buf++) << 8;
642 #if FLASH_COMPLEMENT_DATA
643  w = ~w;
644 #endif /*FLASH_COMPLEMENT_DATA*/
645  boot_page_fill(addr32, w);
646  size-=2;
647 /* Below ought to work but writing to 0xnnnnnnfe modifies the NEXT flash page
648  for some reason, at least in the AVR Studio simulator.
649  if ((addr32&0x000000ff)==0x000000fe) { //handle page boundary
650  if (size) {
651  boot_page_erase(addr32);
652  boot_spm_busy_wait();
653  boot_page_write(addr32);
654  boot_spm_busy_wait();
655  }
656  }
657  addr32+=2;
658 */
659 
660 /* This works...*/
661  addr32+=2;
662  if ((addr32&0x000000ff)==0) { //handle page boundary
663  if (size) {
664  addr32-=0x42; //get an address within the page
665  boot_page_erase(addr32);
666  boot_spm_busy_wait();
667  boot_page_write(addr32);
668  boot_spm_busy_wait();
669  addr32+=0x42;
670  }
671  }
672  }
673  /* Copy the remainder of the existing page */
674  while (ba>1) {
675  w=pgm_read_word_far(addr32);
676  if (size) { //handle odd byte boundary
677  w &= 0xff00;
678 #if FLASH_COMPLEMENT_DATA
679  w +=~(*buf);
680 #else
681  w +=*buf;
682 #endif /*FLASH_COMPLEMENT_DATA*/
683  size=0;
684  }
685  boot_page_fill(addr32,w);
686  addr32+=2;
687  ba-=2;
688  }
689  /* If buf is null, erase the page to zero */
690  } else {
691 #if FLASH_COMPLEMENT_DATA
692  addr32+=2*SPM_PAGESIZE;
693 #else
694  for (w=0;w<SPM_PAGESIZE;w++) {
695  boot_page_fill(addr32, 0);
696  addr32+=2;
697  }
698 #endif /*FLASH_COMPLEMENT_DATA*/
699  }
700 /* Write the last (or only) page */
701  addr32-=0x42; //get an address within the page
702  boot_page_erase(addr32);
703  boot_spm_busy_wait();
704 #if FLASH_COMPLEMENT_DATA
705  if (buf) { //don't write zeroes to erased page
706  boot_page_write(addr32);
707  boot_spm_busy_wait();
708  }
709 #else
710  boot_page_write(addr32);
711  boot_spm_busy_wait();
712 #endif /*FLASH_COMPLEMENT_DATA*/
713  /* Reenable RWW-section again. We need this if we want to jump back
714  * to the application after bootloading. */
715  boot_rww_enable();
716 
717  /* Re-enable interrupts (if they were ever enabled). */
718  SREG = sreg;
719 }
720 
721 #endif /* COFFEE_AVR_FLASH */
722 
723 
724 #ifdef COFFEE_AVR_EXTERNAL
725 
726 #include "interfaces/flash-at45db.h"
727 
728 void external_flash_write_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t * buf, CFS_CONF_OFFSET_TYPE size) {
729  PRINTF("external_flash_write_page(page %u, offset %u, buf %p, size %u) \n", page, offset, buf, size);
730 
731  if( size < 1 ) {
732  return;
733  }
734 
735  if( page > COFFEE_PAGES ) {
736  return;
737  }
738 
739  if( (size + offset) > COFFEE_PAGE_SIZE ) {
740  return;
741  }
742 
743  unsigned char buffer[COFFEE_PAGE_SIZE];
744 
745  // Now read the current content of that page
746  at45db_read_page_bypassed(page, 0, buffer, COFFEE_PAGE_SIZE);
747 
748  watchdog_periodic();
749 
750  // Copy over the new content
751  memcpy(buffer + offset, buf, size);
752 
753  // And write the page again
754  at45db_write_page(page, 0, buffer, COFFEE_PAGE_SIZE);
755 
756  watchdog_periodic();
757 
758  PRINTF("Page %u programmed with %u bytes (%u new)\n", page, COFFEE_PAGE_SIZE, size);
759 }
760 
761 void external_flash_write(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size) {
762  PRINTF(">>>>> external_flash_write(addr %u, buf %p, size %u)\n", addr, buf, size);
763 
764  if( addr > COFFEE_SIZE ) {
765  return;
766  }
767 
768  // Which is the first page we will be programming
769  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
770  CFS_CONF_OFFSET_TYPE written = 0;
771 
772  while(written < size) {
773  // get the start address of the current page
774  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
775  CFS_CONF_OFFSET_TYPE offset = 0;
776 
777  if( addr > page_start ) {
778  // Frist page offset
779  offset = addr - page_start;
780  }
781 
782  CFS_CONF_OFFSET_TYPE length = size - written;
783 
784  if( length > (COFFEE_PAGE_SIZE - offset) ) {
785  length = COFFEE_PAGE_SIZE - offset;
786  }
787 
788  external_flash_write_page(current_page, offset, buf + written, length);
789  written += length;
790  current_page++;
791  }
792 
793 #if DEBUG
794  int g;
795  printf("WROTE: ");
796  for(g=0; g<size; g++) {
797  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
798  }
799  printf("\n");
800 #endif
801 }
802 
803 void external_flash_read_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t *buf, CFS_CONF_OFFSET_TYPE size) {
804  PRINTF("external_flash_read_page(page %u, offset %u, buf %p, size %u)\n", page, offset, buf, size );
805 
806  if( page > COFFEE_PAGES ) {
807  return;
808  }
809 
810  at45db_read_page_bypassed(page, offset, buf, size);
811  watchdog_periodic();
812 
813 #if DEBUG
814  int g;
815  printf("READ: ");
816  for(g=0; g<size; g++) {
817  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
818  }
819  printf("\n");
820 #endif
821 }
822 
823 void external_flash_read(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size) {
824  PRINTF(">>>>> external_flash_read(addr %u, buf %p, size %u)\n", addr, buf, size );
825 
826  if( size < 1 ) {
827  return;
828  }
829 
830  if( addr > COFFEE_SIZE ) {
831  return;
832  }
833 
834  // First of all: find out what the number of the page is
835  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
836  CFS_CONF_OFFSET_TYPE read = 0;
837 
838  while( read < size ) {
839  // get the start address of the current page
840  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
841  CFS_CONF_OFFSET_TYPE offset = 0;
842 
843  if( addr > page_start ) {
844  // Frist page offset
845  offset = addr - page_start;
846  }
847 
848  CFS_CONF_OFFSET_TYPE length = size - read;
849 
850  if( length > (COFFEE_PAGE_SIZE - offset) ) {
851  length = (COFFEE_PAGE_SIZE - offset);
852  }
853 
854  external_flash_read_page(current_page, offset, buf + read, length);
855 
856  PRINTF("Page %u read with %u bytes (offset %u)\n", h, length, offset);
857 
858  read += length;
859  current_page++;
860  }
861 
862 #if DEBUG
863  int g;
864  printf("READ: ");
865  for(g=0; g<size; g++) {
866  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
867  }
868  printf("\n");
869 #endif
870 }
871 
872 void external_flash_erase(coffee_page_t page) {
873  if( page > COFFEE_PAGES ) {
874  return;
875  }
876 
877  PRINTF("external_flash_erase(page %u)\n", page);
878 
879  at45db_erase_page(page);
880  watchdog_periodic();
881 }
882 
883 /*
884 void external_flash_erase(coffee_page_t sector) {
885  if( sector > COFFEE_SECTORS ) {
886  return;
887  }
888 
889  // This has to erase the contents of a whole sector
890  // AT45DB cannot directly delete a sector, we have to do it manually
891  PRINTF("external_flash_erase(sector %u)\n", sector);
892  CFS_CONF_OFFSET_TYPE h;
893 
894  coffee_page_t start = sector * COFFEE_BLOCKS_PER_SECTOR;
895  coffee_page_t end = start + COFFEE_BLOCKS_PER_SECTOR;
896 
897  for(h=start; h<end; h++) {
898  PRINTF("Deleting block %u\n", h);
899 
900  at45db_erase_block(h);
901  watchdog_periodic();
902  }
903 }
904 */
905 
906 #endif /* COFFEE_AVR_EXTERNAL */