flash_avr.c

Go to the documentation of this file.
00001 
00045 #include "flash_avr.h"
00046 
00047 #include "cfg/cfg_flash_avr.h"
00048 #include <cfg/macros.h> // MIN()
00049 #include <cfg/compiler.h>
00050 #include <cfg/debug.h>
00051 #include <cpu/irq.h>
00052 
00053 // Define log settings for cfg/log.h
00054 #define LOG_LEVEL    CONFIG_FLASH_AVR_LOG_LEVEL
00055 #define LOG_FORMAT   CONFIG_FLASH_AVR_LOG_FORMAT
00056 #include <cfg/log.h>
00057 
00058 #include <drv/wdt.h>
00059 
00060 #include <kern/kfile.h>
00061 
00062 #include <avr/io.h>
00063 #include <avr/boot.h>
00064 #include <avr/pgmspace.h>
00065 
00066 #include <string.h>
00067 
00068 
00072 typedef uint16_t avr_page_addr_t;
00073 
00074 
00075 
00076 
00085 static void flash_avr_flush(FlashAvr *fd)
00086 {
00087     if (fd->page_dirty)
00088     {
00089 
00090         LOG_INFO("Flushing page %d\n", fd->curr_page);
00091 
00092         // Wait while the SPM instruction is busy.
00093         boot_spm_busy_wait();
00094 
00095         LOG_INFO("Filling temparary page buffer...");
00096 
00097         // Fill the temporary buffer of the AVR
00098         for (avr_page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
00099         {
00100             uint16_t word = ((uint16_t)fd->page_buf[page_addr + 1] << 8) | fd->page_buf[page_addr];
00101 
00102             ATOMIC(boot_page_fill(page_addr, word));
00103         }
00104         LOG_INFO("Done.\n");
00105 
00106         wdt_reset();
00107 
00108         LOG_INFO("Erasing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
00109 
00110         /* Page erase */
00111         ATOMIC(boot_page_erase(fd->curr_page * SPM_PAGESIZE));
00112 
00113         /* Wait until the memory is erased. */
00114         boot_spm_busy_wait();
00115 
00116         LOG_INFO("Done.\n");
00117         LOG_INFO("Writing page, addr %u...", fd->curr_page * SPM_PAGESIZE);
00118 
00119         /* Store buffer in flash page. */
00120         ATOMIC(boot_page_write(fd->curr_page * SPM_PAGESIZE));
00121         boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
00122 
00123         /*
00124         * Reenable RWW-section again. We need this if we want to jump back
00125         * to the application after bootloading.
00126         */
00127         ATOMIC(boot_rww_enable());
00128 
00129         fd->page_dirty = false;
00130         LOG_INFO("Done.\n");
00131     }
00132 }
00133 
00134 
00141 static int flash_avr_kfileFlush(struct KFile *_fd)
00142 {
00143     FlashAvr *fd = FLASHAVR_CAST(_fd);
00144     flash_avr_flush(fd);
00145     return 0;
00146 }
00147 
00148 
00153 static void flash_avr_loadPage(FlashAvr *fd, avr_page_t page)
00154 {
00155     if (page != fd->curr_page)
00156     {
00157         flash_avr_flush(fd);
00158         // Load page
00159         memcpy_P(fd->page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
00160         fd->curr_page = page;
00161         LOG_INFO("Loaded page %d\n", fd->curr_page);
00162     }
00163 }
00164 
00170 static size_t flash_avr_write(struct KFile *_fd, const void *_buf, size_t size)
00171 {
00172     FlashAvr *fd = FLASHAVR_CAST(_fd);
00173     const uint8_t *buf =(const uint8_t *)_buf;
00174 
00175     avr_page_t page;
00176     avr_page_addr_t page_addr;
00177     size_t total_write = 0;
00178 
00179 
00180     ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
00181     size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
00182 
00183     LOG_INFO("Writing at pos[%u]\n", fd->fd.seek_pos);
00184     while (size)
00185     {
00186         page = fd->fd.seek_pos / SPM_PAGESIZE;
00187         page_addr = fd->fd.seek_pos % SPM_PAGESIZE;
00188 
00189         flash_avr_loadPage(fd, page);
00190 
00191         size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
00192         memcpy(fd->page_buf + page_addr, buf, wr_len);
00193         fd->page_dirty = true;
00194 
00195         buf += wr_len;
00196         fd->fd.seek_pos += wr_len;
00197         size -= wr_len;
00198         total_write += wr_len;
00199     }
00200     LOG_INFO("written %u bytes\n", total_write);
00201     return total_write;
00202 }
00203 
00209 static void flash_avr_open(struct FlashAvr *fd)
00210 {
00211     fd->curr_page = 0;
00212     memcpy_P(fd->page_buf, (const char *)(fd->curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
00213 
00214     fd->fd.seek_pos = 0;
00215     fd->fd.size = (uint16_t)(FLASHEND - CONFIG_FLASH_AVR_BOOTSIZE + 1);
00216     fd->page_dirty = false;
00217 
00218     LOG_INFO("Flash file opened\n");
00219 }
00220 
00224 static int flash_avr_close(struct KFile *_fd)
00225 {
00226     FlashAvr *fd = FLASHAVR_CAST(_fd);
00227     flash_avr_flush(fd);
00228     LOG_INFO("Flash file closed\n");
00229     return 0;
00230 }
00231 
00235 static struct KFile *flash_avr_reopen(struct KFile *fd)
00236 {
00237     FlashAvr *_fd = FLASHAVR_CAST(fd);
00238     flash_avr_close(fd);
00239     flash_avr_open(_fd);
00240     return fd;
00241 }
00242 
00243 
00248 static size_t flash_avr_read(struct KFile *_fd, void *buf, size_t size)
00249 {
00250     FlashAvr *fd = FLASHAVR_CAST(_fd);
00251     ASSERT(fd->fd.seek_pos + (kfile_off_t)size <= (kfile_off_t)fd->fd.size);
00252     size = MIN((kfile_off_t)size, fd->fd.size - fd->fd.seek_pos);
00253 
00254     LOG_INFO("Reading at pos[%u]\n", fd->fd.seek_pos);
00255     // Flush current buffered page (if modified).
00256     flash_avr_flush(fd);
00257 
00258     /*
00259      * AVR pointers are 16 bits wide, this hack is needed to avoid
00260      * compiler warning, cause fd->seek_pos is a 32bit offset.
00261      */
00262     const uint8_t *pgm_addr = (const uint8_t *)0;
00263     pgm_addr += fd->fd.seek_pos;
00264 
00265     memcpy_P(buf, pgm_addr, size);
00266     fd->fd.seek_pos += size;
00267     LOG_INFO("Read %u bytes\n", size);
00268     return size;
00269 }
00270 
00274 void flash_avr_init(struct FlashAvr *fd)
00275 {
00276     memset(fd, 0, sizeof(*fd));
00277     DB(fd->fd._type = KFT_FLASHAVR);
00278 
00279     // Set up flash programming functions.
00280     fd->fd.reopen = flash_avr_reopen;
00281     fd->fd.close = flash_avr_close;
00282     fd->fd.read = flash_avr_read;
00283     fd->fd.write = flash_avr_write;
00284     fd->fd.seek = kfile_genericSeek;
00285     fd->fd.flush = flash_avr_kfileFlush;
00286     fd->curr_page = 0;
00287 
00288     flash_avr_open(fd);
00289 }
00290 
00291