Contiki-Inga 3.x
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 INGA.
36  * \author
37  * Nicolas Tsiftes <nvt@sics.se>
38  * Frederic Thepaut <frederic.thepaut@inooi.com>
39  * David Kopf <dak664@embarqmail.com>
40  * Enrico Joerns <e.joerns@tu-bs.de>
41  */
42 
43 #include <avr/boot.h>
44 #include <avr/interrupt.h>
45 #include <avr/pgmspace.h>
46 #include <string.h>
47 #include "dev/watchdog.h"
48 
49 #include "cfs-coffee-arch.h"
50 #include "httpd-fs-arch.h"
51 
52 #define DEBUG 0
53 /* Note: Debuglevel > 1 will also printout data read. */
54 #if DEBUG
55 #include <stdio.h>
56 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
57 #else
58 #define PRINTF(...)
59 #endif
60 /*---------------------------------------------------------------------------*/
61 /*---------------------------EEPROM ROUTINES---------------------------------*/
62 /*---------------------------------------------------------------------------*/
63 #ifdef COFFEE_INGA_EEPROM
64 
65 /* Letting .bss initialize nullb to zero saves COFFEE_SECTOR_SIZE of flash */
66 //static const unsigned char nullb[COFFEE_SECTOR_SIZE] = {0};
67 static const unsigned char nullb[COFFEE_SECTOR_SIZE];
68 
69 /*---------------------------------------------------------------------------*/
70 /* Erase EEPROM sector
71  */
72 void
73 avr_eeprom_erase(uint16_t sector)
74 {
75  eeprom_write(COFFEE_START + sector * COFFEE_SECTOR_SIZE,
76  (unsigned char *) nullb, sizeof (nullb));
77 }
78 #endif /* COFFEE_INGA_EEPROM */
79 
80 #ifdef COFFEE_INGA_FLASH
81 /*---------------------------------------------------------------------------*/
82 /*-----------------------Internal FLASH ROUTINES-----------------------------*/
83 /*---------------------------------------------------------------------------*/
84 /*
85  * Read from flash info buf. addr contains starting flash byte address
86  */
87 void
88 avr_flash_read(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
89 {
90  uint32_t addr32 = COFFEE_START + addr;
91  uint16_t isize = size;
92 #if DEBUG
93  unsigned char *bufo = (unsigned char *) buf;
94  uint8_t i;
95  uint16_t w = addr32 >> 1; //Show progmem word address for debug
96  PRINTF("r0x%04x(%u) ", w, size);
97 #endif
98 #ifndef FLASH_WORD_READS
99  for (; isize > 0; isize--) {
100 #if FLASH_COMPLEMENT_DATA
101  *buf++ = ~(uint8_t) pgm_read_byte_far(addr32++);
102 #else
103  *buf++ = (uint8_t) pgm_read_byte_far(addr32++);
104 #endif /* FLASH_COMPLEMENT_DATA */
105  }
106 #else /* FLASH_WORD_READS */
107  /* 130 bytes more PROGMEM, but faster */
108  if (isize & 0x01) { //handle first odd byte
109 #if FLASH_COMPLEMENT_DATA
110  *buf++ = ~(uint8_t) pgm_read_byte_far(addr32++);
111 #else
112  *buf++ = (uint8_t) pgm_read_byte_far(addr32++);
113 #endif /* FLASH_COMPLEMENT_DATA */
114  isize--;
115  }
116  for (; isize > 1; isize -= 2) {//read words from flash
117 #if FLASH_COMPLEMENT_DATA
118  *(uint16_t *) buf = ~(uint16_t) pgm_read_word_far(addr32);
119 #else
120  *(uint16_t *) buf = (uint16_t) pgm_read_word_far(addr32);
121 #endif /* FLASH_COMPLEMENT_DATA */
122  buf += 2;
123  addr32 += 2;
124  }
125  if (isize) { //handle last odd byte
126 #if FLASH_COMPLEMENT_DATA
127  *buf++ = ~(uint8_t) pgm_read_byte_far(addr32);
128 #else
129  *buf++ = (uint8_t) pgm_read_byte_far(addr32);
130 #endif /* FLASH_COMPLEMENT_DATA */
131  }
132 #endif /* FLASH_WORD_READS */
133 
134 #if DEBUG>1
135  PRINTF("\nbuf=");
136  // PRINTF("%s",bufo);
137  // for (i=0;i<16;i++) PRINTF("%2x ",*bufo++);
138 #endif /* DEBUG */
139 }
140 /*---------------------------------------------------------------------------*/
141 /*
142  Erase the flash page(s) corresponding to the coffee sector.
143  This is done by calling the write routine with a null buffer and any address
144  within each page of the sector (we choose the first byte).
145  */
146 BOOTLOADER_SECTION
147 void
148 avr_flash_erase(coffee_page_t sector)
149 {
150  coffee_page_t i;
151 
152 #if FLASH_COMPLEMENT_DATA
153  uint32_t addr32;
154  volatile uint8_t sreg;
155 
156  // Disable interrupts.
157  sreg = SREG;
158  cli();
159 
160  for (i = 0; i < COFFEE_SECTOR_SIZE / COFFEE_PAGE_SIZE; i++) {
161  for (addr32 = COFFEE_START + (((sector + i) * COFFEE_PAGE_SIZE)
162  & ~(COFFEE_PAGE_SIZE - 1)); addr32 < (COFFEE_START + (((sector
163  + i + 1) * COFFEE_PAGE_SIZE) & ~(COFFEE_PAGE_SIZE - 1))); addr32
164  += SPM_PAGESIZE) {
165  boot_page_erase(addr32);
166  boot_spm_busy_wait();
167 
168  }
169  }
170  //RE-enable interrupts
171  boot_rww_enable();
172  SREG = sreg;
173 #else /* FLASH_COMPLEMENT_DATA */
174  for (i = 0; i < COFFEE_SECTOR_SIZE / COFFEE_PAGE_SIZE; i++) {
175  avr_flash_write((sector + i) * COFFEE_PAGE_SIZE, 0, 0);
176  }
177 #endif /* FLASH_COMPLEMENT_DATA */
178 }
179 /*---------------------------------------------------------------------------*/
180 /*
181  * Transfer buf[size] from RAM to flash, starting at addr.
182  * If buf is null, just erase the flash page
183  * Note this routine has to be in the bootloader NRWW part of program memory,
184  * and that writing to NRWW (last 32 pages on the 1284p) will halt the CPU.
185  */
186 BOOTLOADER_SECTION
187 void
188 avr_flash_write(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
189 {
190  uint32_t addr32;
191  uint16_t w;
192  uint8_t bb, ba, sreg;
193 
194  /* Disable interrupts, make sure no eeprom write in progress */
195  sreg = SREG;
196  cli();
197  eeprom_busy_wait();
198 
199  /* Calculate the starting address of the first flash page being
200  modified (will be on a page boundary) and the number of
201  unaltered bytes before and after the data to be written. */
202 #if 0 //this is 8 bytes longer
203  uint16_t startpage = addr / COFFEE_PAGE_SIZE;
204  addr32 = COFFEE_START + startpage*COFFEE_PAGE_SIZE;
205 #else
206  addr32 = (COFFEE_ADDRESS&~(SPM_PAGESIZE - 1))+(addr&~(SPM_PAGESIZE - 1));
207 #endif
208  bb = addr & (SPM_PAGESIZE - 1);
209  ba = COFFEE_PAGE_SIZE - ((addr + size)&0xff);
210 
211 #if DEBUG
212  uint16_t startpage = addr / COFFEE_PAGE_SIZE;
213  w = addr32 >> 1; //Show progmem word address for debug
214  if (buf) {
215  PRINTF("w0x%04x %u %u %u", w, size, bb, ba);
216  } else {
217  PRINTF("e0x%04x %u ", w, startpage);
218  }
219 #endif /* DEBUG */
220 
221  /* If buf not null, modify the page(s) */
222  if (buf) {
223  if (size == 0) return; //nothing to write
224  /*Copy the first part of the existing page into the write buffer */
225  while (bb > 1) {
226  w = pgm_read_word_far(addr32);
227  boot_page_fill(addr32, w);
228  addr32 += 2;
229  bb -= 2;
230  }
231  /* Transfer the bytes to be modified */
232  while (size > 1) {
233  if (bb) { //handle odd byte boundary
234  w = pgm_read_word_far(addr32);
235 #if FLASH_COMPLEMENT_DATA
236  w = ~w;
237 #endif /* FLASH_COMPLEMENT_DATA */
238  w &= 0xff;
239  bb = 0;
240  size++;
241  } else {
242  w = *buf++;
243  }
244  w += (*buf++) << 8;
245 #if FLASH_COMPLEMENT_DATA
246  w = ~w;
247 #endif /* FLASH_COMPLEMENT_DATA */
248  boot_page_fill(addr32, w);
249  size -= 2;
250  /* Below ought to work but writing to 0xnnnnnnfe modifies the NEXT flash page
251  for some reason, at least in the AVR Studio simulator.
252  if ((addr32&0x000000ff)==0x000000fe) { //handle page boundary
253  if (size) {
254  boot_page_erase(addr32);
255  boot_spm_busy_wait();
256  boot_page_write(addr32);
257  boot_spm_busy_wait();
258  }
259  }
260  addr32+=2;
261  */
262 
263  /* This works...*/
264  addr32 += 2;
265  if ((addr32 & 0x000000ff) == 0) { //handle page boundary
266  if (size) {
267  addr32 -= 0x42; //get an address within the page
268  boot_page_erase(addr32);
269  boot_spm_busy_wait();
270  boot_page_write(addr32);
271  boot_spm_busy_wait();
272  addr32 += 0x42;
273  }
274  }
275  }
276  /* Copy the remainder of the existing page */
277  while (ba > 1) {
278  w = pgm_read_word_far(addr32);
279  if (size) { //handle odd byte boundary
280  w &= 0xff00;
281 #if FLASH_COMPLEMENT_DATA
282  w += ~(*buf);
283 #else /* FLASH_COMPLEMENT_DATA */
284  w += *buf;
285 #endif /* FLASH_COMPLEMENT_DATA */
286  size = 0;
287  }
288  boot_page_fill(addr32, w);
289  addr32 += 2;
290  ba -= 2;
291  }
292  /* If buf is null, erase the page to zero */
293  } else {
294 #if FLASH_COMPLEMENT_DATA
295  addr32 += 2 * SPM_PAGESIZE;
296 #else
297  for (w = 0; w < SPM_PAGESIZE; w++) {
298  boot_page_fill(addr32, 0);
299  addr32 += 2;
300  }
301 #endif /* FLASH_COMPLEMENT_DATA */
302  }
303  /* Write the last (or only) page */
304  addr32 -= 0x42; //get an address within the page
305  boot_page_erase(addr32);
306  boot_spm_busy_wait();
307 #if FLASH_COMPLEMENT_DATA
308  if (buf) { //don't write zeroes to erased page
309  boot_page_write(addr32);
310  boot_spm_busy_wait();
311  }
312 #else /* FLASH_COMPLEMENT_DATA */
313  boot_page_write(addr32);
314  boot_spm_busy_wait();
315 #endif /* FLASH_COMPLEMENT_DATA */
316  /* Reenable RWW-section again. We need this if we want to jump back
317  * to the application after bootloading. */
318  boot_rww_enable();
319 
320  /* Re-enable interrupts (if they were ever enabled). */
321  SREG = sreg;
322 }
323 
324 #endif /* COFFEE_INGA_FLASH */
325 
326 
327 /*---------------------------------------------------------------------------*/
328 /*-----------------------External FLASH ROUTINES-----------------------------*/
329 /*---------------------------------------------------------------------------*/
330 #ifdef COFFEE_INGA_EXTERNAL
331 
332 #include "dev/at45db.h"
333 void
334 external_flash_write_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t * buf, CFS_CONF_OFFSET_TYPE size)
335 {
336  PRINTF("external_flash_write_page(page %u, offset %u, buf %p, size %u) \n", page, offset, buf, size);
337 
338  if (size < 1) {
339  return;
340  }
341 
342  if (page > COFFEE_PAGES) {
343  return;
344  }
345 
346  unsigned char buffer[COFFEE_PAGE_SIZE];
347 
348  // Now read the current content of that page
349  at45db_read_page_bypassed(page, 0, buffer, COFFEE_PAGE_SIZE);
350 
352 
353  // Copy over the new content
354  memcpy(buffer + offset, buf, size);
355 
356  // And write the page again
357  at45db_write_page(page, 0, buffer, COFFEE_PAGE_SIZE);
358 
360 
361  PRINTF("Page %u programmed with %u bytes (%u new)\n", page, COFFEE_PAGE_SIZE, size);
362 }
363 /*----------------------------------------------------------------------------*/
364 void
365 external_flash_write(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
366 {
367  PRINTF(">>>>> external_flash_write(addr %u, buf %p, size %u)\n", addr, buf, size);
368 
369  if (addr > COFFEE_SIZE) {
370  return;
371  }
372 
373  // Which is the first page we will be programming
374  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
375  CFS_CONF_OFFSET_TYPE written = 0;
376 
377  while (written < size) {
378  // get the start address of the current page
379  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
380  CFS_CONF_OFFSET_TYPE offset = 0;
381 
382  if (addr > page_start) {
383  // Frist page offset
384  offset = addr - page_start;
385  }
386 
387  CFS_CONF_OFFSET_TYPE length = size - written;
388 
389  if (length > (COFFEE_PAGE_SIZE - offset)) {
390  length = COFFEE_PAGE_SIZE - offset;
391  }
392 
393  external_flash_write_page(current_page, offset, buf + written, length);
394  written += length;
395  current_page++;
396  }
397 
398 #if DEBUG
399  int g;
400  printf("WROTE: ");
401  for (g = 0; g < size; g++) {
402  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
403  }
404  printf("\n");
405 #endif /* DEBUG */
406 }
407 /*----------------------------------------------------------------------------*/
408 void
409 external_flash_read_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
410 {
411  PRINTF("external_flash_read_page(page %u, offset %u, buf %p, size %u)\n", page, offset, buf, size);
412 
413  if (page > COFFEE_PAGES) {
414  return;
415  }
416 
417  at45db_read_page_bypassed(page, offset, buf, size);
419 
420 #if DEBUG > 1
421  int g;
422  printf("READ: ");
423  for (g = 0; g < size; g++) {
424  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
425  }
426  printf("\n");
427 #endif /* DEBUG */
428 }
429 /*----------------------------------------------------------------------------*/
430 void
431 external_flash_read(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
432 {
433  PRINTF(">>>>> external_flash_read(addr %u, buf %p, size %u)\n", addr, buf, size);
434 
435  if (size < 1) {
436  return;
437  }
438 
439  if (addr > COFFEE_SIZE) {
440  return;
441  }
442 
443  // First of all: find out what the number of the page is
444  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
445  CFS_CONF_OFFSET_TYPE read = 0;
446 
447  while (read < size) {
448  // get the start address of the current page
449  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
450  CFS_CONF_OFFSET_TYPE offset = 0;
451 
452  if (addr > page_start) {
453  // Frist page offset
454  offset = addr - page_start;
455  }
456 
457  CFS_CONF_OFFSET_TYPE length = size - read;
458 
459  if (length > (COFFEE_PAGE_SIZE - offset)) {
460  length = (COFFEE_PAGE_SIZE - offset);
461  }
462 
463  external_flash_read_page(current_page, offset, buf + read, length);
464 
465 // PRINTF("Page %u read with %u bytes (offset %u)\n", h, length, offset);
466 
467  read += length;
468  current_page++;
469  }
470 
471 #if DEBUG > 1
472  int g;
473  printf("READ: ");
474  for (g = 0; g < size; g++) {
475  printf("%02X %c ", buf[g] & 0xFF, buf[g] & 0xFF);
476  }
477  printf("\n");
478 #endif /* DEBUG */
479 }
480 /*----------------------------------------------------------------------------*/
481 void
482 external_flash_erase(coffee_page_t page)
483 {
484  if (page > COFFEE_PAGES) {
485  return;
486  }
487 
488  PRINTF("external_flash_erase(page %u)\n", page);
489 
490  at45db_erase_page(page);
492 }
493 
494 /*
495 void external_flash_erase(coffee_page_t sector) {
496  if( sector > COFFEE_SECTORS ) {
497  return;
498  }
499 
500  // This has to erase the contents of a whole sector
501  // AT45DB cannot directly delete a sector, we have to do it manually
502  PRINTF("external_flash_erase(sector %u)\n", sector);
503  CFS_CONF_OFFSET_TYPE h;
504 
505  coffee_page_t start = sector * COFFEE_BLOCKS_PER_SECTOR;
506  coffee_page_t end = start + COFFEE_BLOCKS_PER_SECTOR;
507 
508  for(h=start; h<end; h++) {
509  PRINTF("Deleting block %u\n", h);
510 
511  at45db_erase_block(h);
512  watchdog_periodic();
513  }
514 }
515  */
516 
517 #endif /* COFFEE_INGA_EXTERNAL */
518 
519 #ifdef COFFEE_INGA_SDCARD
520 
521 #include "cfs/fat/diskio.h"
522 
523 static uint8_t cfs_buffer[512];
524 struct diskio_device_info *cfs_info = 0;
525 void
526 sd_write_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t * buf, CFS_CONF_OFFSET_TYPE size)
527 {
528  PRINTF("sd_write_page(page %lu, offset %lu, buf %p, size %lu) \n", page, offset, buf, size);
529 
530  if (size < 1) {
531  return;
532  }
533 
534  if (page > COFFEE_PAGES) {
535  return;
536  }
537 
538  // Now read the current content of that page
539  diskio_read_block(cfs_info, page, cfs_buffer);
540 
542 
543  // Copy over the new content
544  memcpy(cfs_buffer + offset, buf, size);
545 
546  // And write the page again
547  diskio_write_block(cfs_info, page, cfs_buffer);
548 
550 
551  PRINTF("Page %lu programmed with %lu bytes (%lu new)\n", page, COFFEE_PAGE_SIZE, size);
552 }
553 /*----------------------------------------------------------------------------*/
554 void
555 sd_write(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
556 {
557  PRINTF(">>>>> sd_write(addr %lu, buf %p, size %lu)\n", addr, buf, size);
558 
559  if (addr > COFFEE_SIZE) {
560  return;
561  }
562 
563  // Which is the first page we will be programming
564  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
565  CFS_CONF_OFFSET_TYPE written = 0;
566 
567  while (written < size) {
568  // get the start address of the current page
569  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
570  CFS_CONF_OFFSET_TYPE offset = 0;
571 
572  if (addr > page_start) {
573  // Frist page offset
574  offset = addr - page_start;
575  }
576 
577  CFS_CONF_OFFSET_TYPE length = size - written;
578 
579  if (length > (COFFEE_PAGE_SIZE - offset)) {
580  length = COFFEE_PAGE_SIZE - offset;
581  }
582 
583  sd_write_page(current_page, offset, buf + written, length);
584  written += length;
585  current_page++;
586  }
587 
588 #if DEBUG
589  int g;
590  printf("WROTE: ");
591  for (g = 0; g < size; g++) {
592  printf("%02X ", buf[g] & 0xFF, buf[g] & 0xFF);
593  }
594  printf("\n");
595 #endif /* DEBUG */
596 }
597 /*----------------------------------------------------------------------------*/
598 void
599 sd_read_page(coffee_page_t page, CFS_CONF_OFFSET_TYPE offset, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
600 {
601  PRINTF("sd_read_page(page %lu, offset %lu, buf %p, size %lu)\n", page, offset, buf, size);
602 
603  if (page > COFFEE_PAGES) {
604  return;
605  }
606 
607  diskio_read_block(cfs_info, page, cfs_buffer);
608  memcpy(buf, cfs_buffer + offset, size);
609 
610 #if DEBUG
611  int g;
612  printf("READ: ");
613  for (g = 0; g < size; g++) {
614  printf("%02X ", buf[g] & 0xFF, buf[g] & 0xFF);
615  }
616  printf("\n");
617 #endif /* DEBUG */
618 
619 }
620 /*----------------------------------------------------------------------------*/
621 void
622 sd_read(CFS_CONF_OFFSET_TYPE addr, uint8_t *buf, CFS_CONF_OFFSET_TYPE size)
623 {
624  PRINTF(">>>>> sd_read(addr %lu, buf %p, size %lu)\n", addr, buf, size);
625 
626  if (size < 1) {
627  return;
628  }
629 
630  if (addr > COFFEE_SIZE) {
631  return;
632  }
633 
634  // First of all: find out what the number of the page is
635  coffee_page_t current_page = addr / COFFEE_PAGE_SIZE;
636  CFS_CONF_OFFSET_TYPE read = 0;
637 
638  while (read < size) {
639  // get the start address of the current page
640  CFS_CONF_OFFSET_TYPE page_start = current_page * COFFEE_PAGE_SIZE;
641  CFS_CONF_OFFSET_TYPE offset = 0;
642 
643  if (addr > page_start) {
644  // Frist page offset
645  offset = addr - page_start;
646  }
647 
648  CFS_CONF_OFFSET_TYPE length = size - read;
649 
650  if (length > (COFFEE_PAGE_SIZE - offset)) {
651  length = (COFFEE_PAGE_SIZE - offset);
652  }
653 
654  sd_read_page(current_page, offset, buf + read, length);
655 
656  PRINTF("Page %lu read with %lu bytes (offset %lu)\n", current_page, length, offset);
657 
658  read += length;
659  current_page++;
660  }
661 
662 #if DEBUG > 1
663  int g;
664  printf("READ: ");
665  for (g = 0; g < size; g++) {
666  printf("%02X ", buf[g] & 0xFF, buf[g] & 0xFF);
667  }
668  printf("\n");
669 #endif /* DEBUG */
670 }
671 /*----------------------------------------------------------------------------*/
672 void
673 sd_erase(coffee_page_t page)
674 {
675  if (page > COFFEE_PAGES) {
676  return;
677  }
678 
679  PRINTF("sd_erase(page %lu)\n", page);
680  memset(cfs_buffer, 0, 512);
681 
682  diskio_write_block(cfs_info, page, cfs_buffer);
684 }
685 
686 #endif /* COFFEE_INGA_SDCARD */
687