flash_avr.c

Go to the documentation of this file.
00001 
00045 #include "flash_avr.h"
00046 
00047 #include <avr/io.h>
00048 #include <avr/boot.h>
00049 #include <avr/pgmspace.h>
00050 
00051 #include <cfg/macros.h> // MIN()
00052 #include <cfg/compiler.h>
00053 #include <cfg/debug.h>
00054 #include <cpu/irq.h>
00055 
00056 #include <drv/wdt.h>
00057 
00058 #include <kern/kfile.h>
00059 
00060 #include <string.h>
00061 
00065 typedef uint16_t avr_page_addr_t;
00066 typedef uint16_t avr_page_t;
00067 
00072 static uint8_t page_buf[SPM_PAGESIZE];
00073 
00077 bool page_modified;
00078 
00082 static avr_page_t curr_page = 0;
00083 
00084 /*
00085  * Private avr flush funtion.
00086  *
00087  * Write current buffered page in flash memory (if modified).
00088  * This function erase flash memory page before writing.
00089  *
00090  * This function is only use internaly in this module.
00091  */
00092 static void flash_avr_flush(void)
00093 {
00094     if (page_modified)
00095     {
00096         kprintf("Flushing page %d\n", curr_page);
00097 
00098         // Wait while the SPM instruction is busy.
00099         boot_spm_busy_wait();
00100 
00101         kprintf("Filling temparary page buffer...");
00102 
00103         // Fill the temporary buffer of the AVR
00104         for (avr_page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
00105         {
00106             uint16_t word = ((uint16_t)page_buf[page_addr + 1] << 8) | page_buf[page_addr];
00107 
00108             ATOMIC(boot_page_fill(page_addr, word));
00109         }
00110         kprintf("Done.\n");
00111 
00112         wdt_reset();
00113 
00114         kprintf("Erasing page, addr %u...", curr_page * SPM_PAGESIZE);
00115 
00116         /* Page erase */
00117         ATOMIC(boot_page_erase(curr_page * SPM_PAGESIZE));
00118 
00119         /* Wait until the memory is erased. */
00120         boot_spm_busy_wait();
00121 
00122         kprintf("Done.\n");
00123         kprintf("Writing page, addr %u...", curr_page * SPM_PAGESIZE);
00124 
00125         /* Store buffer in flash page. */
00126         ATOMIC(boot_page_write(curr_page * SPM_PAGESIZE));
00127         boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
00128 
00129         /*
00130         * Reenable RWW-section again. We need this if we want to jump back
00131         * to the application after bootloading.
00132         */
00133         ATOMIC(boot_rww_enable());
00134 
00135         page_modified = false;
00136         kprintf("Done.\n");
00137     }
00138 }
00139 
00140 
00147 static int flash_avr_kfileFlush(struct KFile * fd)
00148 {
00149     KFILE_ASSERT_GENERIC(fd);
00150     (void)fd;
00151     flash_avr_flush();
00152     return 0;
00153 }
00154 
00155 
00160 static void flash_avr_loadPage(avr_page_t page)
00161 {
00162     if (page != curr_page)
00163     {
00164         flash_avr_flush();
00165         // Load page
00166         memcpy_P(page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
00167         curr_page = page;
00168         kprintf("Loaded page %d\n", curr_page);
00169     }
00170 }
00171 
00177 static size_t flash_avr_write(struct KFile *fd, const void *_buf, size_t size)
00178 {
00179     KFILE_ASSERT_GENERIC(fd);
00180     const uint8_t *buf =(const uint8_t *)_buf;
00181 
00182     avr_page_t page;
00183     avr_page_addr_t page_addr;
00184     size_t total_write = 0;
00185 
00186     ASSERT(fd->seek_pos + size <= fd->size);
00187     size = MIN((uint32_t)size, fd->size - fd->seek_pos);
00188 
00189     kprintf("Writing at pos[%u]\n", fd->seek_pos);
00190     while (size)
00191     {
00192         page = fd->seek_pos / SPM_PAGESIZE;
00193         page_addr = fd->seek_pos % SPM_PAGESIZE;
00194 
00195         flash_avr_loadPage(page);
00196 
00197         size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
00198         memcpy(page_buf + page_addr, buf, wr_len);
00199         page_modified = true;
00200 
00201         buf += wr_len;
00202         fd->seek_pos += wr_len;
00203         size -= wr_len;
00204         total_write += wr_len;
00205     }
00206     kprintf("written %u bytes\n", total_write);
00207     return total_write;
00208 }
00209 
00215 static void flash_avr_open(struct KFile *fd)
00216 {
00217     KFILE_ASSERT_GENERIC(fd);
00218     curr_page = 0;
00219     memcpy_P(page_buf, (const char *)(curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
00220 
00221     fd->seek_pos = 0;
00222     fd->size = (uint16_t)(FLASHEND - CONFIG_BOOT_SIZE + 1);
00223     page_modified = false;
00224 
00225     kprintf("Flash file opened\n");
00226 }
00227 
00231 static int flash_avr_close(UNUSED_ARG(struct KFile *,fd))
00232 {
00233     KFILE_ASSERT_GENERIC(fd);
00234     flash_avr_flush();
00235     kprintf("Flash file closed\n");
00236     return 0;
00237 }
00238 
00242 static struct KFile *flash_avr_reopen(struct KFile *fd)
00243 {
00244     KFILE_ASSERT_GENERIC(fd);
00245     flash_avr_close(fd);
00246     flash_avr_open(fd);
00247     return fd;
00248 }
00249 
00250 
00255 static size_t flash_avr_read(struct KFile *fd, void *buf, size_t size)
00256 {
00257     KFILE_ASSERT_GENERIC(fd);
00258     ASSERT(fd->seek_pos + size <= fd->size);
00259     size = MIN((uint32_t)size, fd->size - fd->seek_pos);
00260 
00261     kprintf("Reading at pos[%u]\n", fd->seek_pos);
00262     // Flush current buffered page (if modified).
00263     flash_avr_flush();
00264 
00265     /*
00266      * AVR pointers are 16 bits wide, this hack is needed to avoid
00267      * compiler warning, cause fd->seek_pos is a 32bit offset.
00268      */
00269     const uint8_t *pgm_addr = (const uint8_t *)0;
00270     pgm_addr += fd->seek_pos;
00271 
00272     memcpy_P(buf, pgm_addr, size);
00273     fd->seek_pos += size;
00274     kprintf("Read %u bytes\n", size);
00275     return size;
00276 }
00277 
00281 void flash_avr_init(struct KFile *fd)
00282 {
00283     memset(fd, 0, sizeof(*fd));
00284     DB(fd->_type = KFT_GENERIC);
00285 
00286     // Set up flash programming functions.
00287     fd->reopen = flash_avr_reopen;
00288     fd->close = flash_avr_close;
00289     fd->read = flash_avr_read;
00290     fd->write = flash_avr_write;
00291     fd->seek = kfile_genericSeek;
00292     fd->flush = flash_avr_kfileFlush;
00293 
00294     flash_avr_open(fd);
00295 }
00296