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