/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2004 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_SMP #define __SMP__ #endif /*--- #ifdef CONFIG_SMP ---*/ #include #include #include #include /*--- #include ---*/ #include #include #include #include #include #include #include #include #include #include #include #include /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ /*--- #define TFFS_DEBUG ---*/ #include #include "tffs_local.h" #include "tffs_legacy.h" #ifdef SPI_PANIC_LOG int tffs_spi_read(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int len); int tffs_spi_write(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int len); #if defined(CONFIG_VR9) || defined(CONFIG_AR10) #define SPI_PANIC_MODE (tffs_panic_mode && tffs_spi_mode) #endif /*--- #if defined(CONFIG_VR9) ---*/ #endif /*--- #ifdef(SPI_PANIC_LOG) ---*/ /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static struct _TFFS_Entry *TFFS_Global_Index[FLASH_FS_ID_LAST]; extern int tffs_mtd_offset[2]; unsigned char *TFFS3_Cleanup_Buffer = NULL; static int TFFS3_LGCY_Reindex(tffs_device *tffs_dev); static int TFFS3_LGCY_Cleanup(tffs_device *tffs_dev, void *handle); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline int MTD_READ(struct tffs_mtd *local_TFFS, loff_t from, size_t len, size_t *retlen, u_char *buf) { // pr_err("[%s] Called\n", __func__); if((local_TFFS == NULL )|| local_TFFS->mtd->read == NULL){ pr_err("[%s] mtd_info/mtd_info->read is NULL\n", __func__); return -EFAULT; } #ifdef SPI_PANIC_LOG if(SPI_PANIC_MODE){ *retlen = tffs_spi_read((unsigned int)from, ((current_mtd == avail_mtd[0]) ? 0 : 1), buf, len); return 0; } #endif return (*local_TFFS->mtd->read)(local_TFFS->mtd, from, len, retlen, buf); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline int MTD_WRITE(struct tffs_mtd *local_TFFS, loff_t from, size_t len, size_t *retlen, u_char *buf) { // pr_err("[%s] Called\n", __func__); #ifdef SPI_PANIC_LOG if(SPI_PANIC_MODE){ *retlen = tffs_spi_write((unsigned int)from, ((current_mtd == avail_mtd[0]) ? 0 : 1), buf, len); return 0; } #endif /*--- #ifdef SPI_PANIC_LOG ---*/ if((local_TFFS == NULL )|| (local_TFFS->mtd->write == NULL)){ pr_err("[%s] mtd_info/mtd_info->write is NULL\n", __func__); return -EFAULT; } return (*local_TFFS->mtd->write)(local_TFFS->mtd, from, len, retlen, buf); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline int get_mtd_device_wrapped(struct tffs_mtd *tffs_mtd, unsigned int mtd_idx) { pr_err("[%s] Called\n", __func__); tffs_mtd->mtd = get_mtd_device(NULL, mtd_idx); if(IS_ERR(tffs_mtd->mtd)){ return -1; } tffs_mtd->mtd_idx = mtd_idx; tffs_mtd->size = tffs_mtd->mtd->size; return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline void put_mtd_device_wrapped(struct tffs_mtd *tffs_mtd) { pr_err("[%s] Called\n", __func__); put_mtd_device(tffs_mtd->mtd); } #if 0 /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void TFFS_Deinit(void) { DBG((KERN_INFO "TFFS_Deinit()\n")); if(!TFFS_mtd.use_bdev) put_mtd_device_wrapped(&TFFS_mtd); TFFS_mtd_number = (unsigned int) -1; TFFS_mtd.tffs.mtd = NULL; } #endif /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void *TFFS3_LGCY_Open(tffs_device *tffs_dev, struct _tffs_open *open_data) { struct tffs_lgcy_ctx *ctx; struct TFFS_LGCY_State *state; // pr_err("[%s] Called\n", __func__); ctx = (struct tffs_lgcy_ctx *) tffs_dev->priv; open_data->max_segment_size = (32 * 1024) + 16; // when opened in panic mode, use static state struct and if there is a special panic // mtd_info set up, use that one state = NULL; if(open_data->mode == tffs3_mode_panic){ if(ctx->in_panic_mode == 0){ state = &(ctx->panic_state); ctx->in_panic_mode = 1; } } else { state = kmalloc(sizeof(*state), GFP_KERNEL); } if(state == NULL) { pr_err("[%s] malloc(%u) failed\n", __func__, sizeof(*state)); goto err_out; } memset(state, 0x0, sizeof(*state)); if(ctx->idx_created == 0){ TFFS3_LGCY_Reindex(tffs_dev); ctx->idx_created = 1; } err_out: return state; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Close(tffs_device *tffs_dev, void *handle) { struct tffs_lgcy_ctx *ctx; struct TFFS_LGCY_State *state; // pr_err("[%s] Called\n", __func__); ctx = (struct tffs_lgcy_ctx *) tffs_dev->priv; if(ctx && ctx->active_mtd && ctx->active_mtd->mtd->sync){ ctx->active_mtd->mtd->sync(ctx->active_mtd->mtd); } state = (struct TFFS_LGCY_State *) handle; if(ctx->in_panic_mode == 0){ kfree(state); } return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void TFFS3_LGCY_Format_Callback(struct erase_info *instr) { DBG(("TFFS: mtd_erase_callback\n")); switch(instr->state){ case MTD_ERASE_PENDING: break; case MTD_ERASING: break; case MTD_ERASE_SUSPEND: break; case MTD_ERASE_FAILED: case MTD_ERASE_DONE: wake_up((wait_queue_head_t *) instr->priv); break; } return; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Format(struct tffs_mtd *tffs_mtd) { struct erase_info *erase; int ret = 0; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; pr_err("[%s] Called\n", __func__); init_waitqueue_head(&wait_q); erase = (struct erase_info *) kzalloc(sizeof(struct erase_info), GFP_KERNEL); if(erase == NULL ){ DBG((KERN_ERR "TFFS_LGCY_Format: malloc(%u) failed\n", sizeof(struct erase_info))); return -ENOMEM; } DBG((KERN_INFO "TFFS_LGCY_Format: malloc(%u) success\n", sizeof(struct erase_info))); erase->mtd = tffs_mtd->mtd; erase->addr = 0; erase->len = tffs_mtd->mtd->size; erase->callback = TFFS3_LGCY_Format_Callback; erase->priv = (u_long) &wait_q; erase->next = NULL; DBG((KERN_INFO "TFFS_LGCY_Format(handle=0x%x): erase: addr %llx len %llx\n", (unsigned int)handle, erase->addr, erase->len)); ret = tffs_mtd->mtd->erase(tffs_mtd->mtd, erase); DBG((KERN_INFO "TFFS_LGCY_Format(handle=0x%x): erase: ret 0x%x\n", (unsigned int)handle, ret)); if(!ret){ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&wait_q, &wait); if(erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED){ schedule(); } remove_wait_queue(&wait_q, &wait); set_current_state(TASK_RUNNING); ret = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; if(ret){ DBG((KERN_ERR "Failed (callback) to erase mtd, region [0x%llx, 0x%llx]\n", erase->addr,erase->len)); } }else{ DBG((KERN_ERR "Failed to erase mtd, region [0x%llx, 0x%llx]\n", erase->addr,erase->len)); ret = -EIO; } kfree(erase); return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ enum _tffs_memcmp { tffs_memcmp_equal, tffs_memcmp_writeable, tffs_memcmp_clear_required }; /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static enum _tffs_memcmp TFFS3_Memcmp(unsigned char *FlashMemory, unsigned char *NewMemory, unsigned int Length) { unsigned int ret; unsigned short *_Flash = (unsigned short *) FlashMemory; unsigned short *_neuerWert = (unsigned short *) NewMemory; pr_err("[%s] Called\n", __func__); ret = memcmp(FlashMemory, NewMemory, Length); if(ret == 0) return tffs_memcmp_equal; #if 1 Length = (Length + 1) >> 1; while(Length--){ if((((*_Flash++) | ((~(*_neuerWert++)) & 0xFFFF)) != 0xFFFF)){ return tffs_memcmp_clear_required; } } return tffs_memcmp_writeable; #else return tffs_memcmp_clear_required; #endif } #if 0 static int find_entry(struct tffs_lgcy_ctx *ctx, enum _tffs_id Id, loff_t from, loff_t *at, unsigned short *found) { struct _TFFS_Entry Entry; int result; size_t retlen; result = -ENOENT; do{ result = MTD_READ(ctx->active_mtd, from, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(result != 0){ result = -EIO; break; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ *at = from; *found = Entry.ID; result = 0; from += sizeof(struct _TFFS_Entry) + ((Entry.Length + 3) & ~0x03); }while(*found != Id && *found != FLASH_FS_ID_FREE && (from + sizeof(struct _TFFS_Entry)) < ctx->active_mtd->size); return result; } #endif static int do_write(struct tffs_lgcy_ctx *ctx, enum _tffs_id Id, uint8_t *write_buffer, size_t write_length, size_t *rlen) { int result, retlen; struct _TFFS_Entry *E, Entry; struct _TFFS_Entry *E_ToClear = NULL, Entry_ToClear; unsigned int Len; uint8_t *rd_buf; *rlen = write_length; rd_buf = NULL; result = 0; Len = 0; E = (struct _TFFS_Entry *) 0; while((unsigned int) E + sizeof(struct _TFFS_Entry) + write_length < ctx->active_mtd->size){ result = MTD_READ(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(result != 0){ goto err_out; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ /*---------------------------------------------------------------------------------------*\ * ID gefunden \*---------------------------------------------------------------------------------------*/ if(Entry.ID == (unsigned short) Id && Id < FLASH_FS_DROPABLE_DATA){ /*--- Alter Eintrag gefunden ----*/ DBG((KERN_INFO "alter Eintrag mit ID=%x gefunden ", Id)); /*-----------------------------------------------------------------------------------*\ * prüfen ob eintrag identisch oder überschreibbar \*-----------------------------------------------------------------------------------*/ if(Entry.Length == write_length){ rd_buf = kmalloc(Entry.Length, GFP_KERNEL); if(rd_buf == NULL){ DBG((KERN_ERR "TFFS_LGCY_Write: malloc(%u) failed\n", Entry.Length)); result = -ENOMEM; goto err_out; } DBG((KERN_INFO "TFFS_LGCY_Write: malloc(%u) success\n", Entry.Length)); result = MTD_READ(ctx->active_mtd, (unsigned long) E + sizeof(struct _TFFS_Entry), Entry.Length, &retlen, rd_buf); if(result){ DBG((KERN_ERR "TFFS_LGCY_Write: MTD_READ failed\n")); goto err_out; } tffs3_write_statistic(Entry.ID, retlen, 0); switch(TFFS3_Memcmp(rd_buf, write_buffer, Entry.Length)){ case tffs_memcmp_equal: DBG((KERN_INFO "alter Eintrag mit neuem identisch\n")); result = 0; goto err_out; break; case tffs_memcmp_writeable: DBG((KERN_INFO "alter Eintrag durch neuen überschreibbar\n")); result = MTD_WRITE(ctx->active_mtd, (unsigned long) E + sizeof(struct _TFFS_Entry), Entry.Length, &retlen, write_buffer); tffs3_write_statistic(Entry.ID, retlen, 1); goto err_out; break; default: case tffs_memcmp_clear_required: break; } kfree(rd_buf); rd_buf = NULL; } /*-----------------------------------------------------------------------------------*\ * gefundenen Eintrag auf FLASH_FS_ID_SKIP setzen \*-----------------------------------------------------------------------------------*/ Entry.ID = FLASH_FS_ID_SKIP; if(write_buffer == NULL ){ result = MTD_WRITE(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(result != 0 || (retlen != sizeof(struct _TFFS_Entry))){ DBG((KERN_ERR "TFFS: write TFFS_LGCY_Entry (id=SKIP) failed\n")); result = result ? result : -EIO; goto err_out; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ DBG((KERN_INFO "geloescht\n")); TFFS_Global_Index[Id] = (struct _TFFS_Entry *) FLASH_FS_ID_FREE; }else{ E_ToClear = E; Entry_ToClear = Entry; } /*-----------------------------------------------------------------------------------*\ * sollte es keine Daten für neuen satz geben dann sind wir fertig \*-----------------------------------------------------------------------------------*/ if(write_buffer == NULL ){ DBG((KERN_INFO "(1) nur loeschen kein neuer Eintrag\n")); result = 0; goto err_out; } } /*---------------------------------------------------------------------------------------*\ * freie ID gefunden \*---------------------------------------------------------------------------------------*/ if(Entry.ID == FLASH_FS_ID_FREE){ /*--- Freier Eintrag gefunden ----*/ DBG((KERN_INFO "freier Eintrag gefunden\n")); if(write_buffer == NULL ){ DBG((KERN_INFO "(2) nur loeschen kein neuer Eintrag\n")); result = 0; goto err_out; } Entry.ID = (unsigned short) Id; Entry.Length = (unsigned short) write_length; TFFS_Global_Index[Entry.ID] = E; result = MTD_WRITE(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(result != 0 || (retlen != sizeof(struct _TFFS_Entry))){ DBG((KERN_ERR "TFFS: write TFFS_LGCY_Entry (id=0x%x, ptr 0x%x %u bytes) failed, reason %d\n", Id, (unsigned int)E, sizeof(struct _TFFS_Entry), ret)); result = result ? result : -EIO; goto err_out; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ DBG((KERN_INFO "header mit id %x geschrieben\n", Id)); result = MTD_WRITE(ctx->active_mtd, (unsigned long) E + sizeof(struct _TFFS_Entry), Entry.Length, &retlen, write_buffer); if(result != 0 || (retlen != Entry.Length)){ DBG((KERN_ERR "TFFS: write data (id=0x%x, ptr 0x%x %u bytes) failed, reason %d\n", Id, (unsigned int)E + sizeof(struct _TFFS_Entry), Entry.Length, ret)); result = result ? result : -EIO; goto err_out; } tffs3_write_statistic(Entry.ID, retlen, 1); DBG((KERN_INFO "%d bytes daten geschrieben\n", write_length)); if(E_ToClear != NULL ){ result = MTD_WRITE(ctx->active_mtd, (unsigned long) E_ToClear, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry_ToClear); if(result || (retlen != sizeof(struct _TFFS_Entry))){ DBG((KERN_ERR "TFFS: write TFFS_LGCY_Entry (id=SKIP) failed\n")); result = result ? result : -EIO; goto err_out; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ DBG((KERN_INFO "geloescht (postum) ID=0x%x Len=%u\n", Entry_ToClear.ID, Entry_ToClear.Length)); } /*-------------------------------------------------------------------------*\ Prüfen ob Filesystem mehr als 75% Voll ist \*-------------------------------------------------------------------------*/ if(ctx->in_panic_mode == 0){ unsigned int Fuell = (unsigned int) E + Entry.Length; /*--- aktueller Fuellstand ---*/ Fuell *= 100; Fuell /= (unsigned int) ctx->active_mtd->size; DBG((KERN_INFO "fuellstand %u%%\n", Fuell)); if(Fuell > 75){ pr_err("TFFS: Fuellstand > 75 ... trigger Cleanup\n"); tffs3_send_event(TFFS_EVENT_CLEANUP); } } result = 0; goto err_out; } Len = (Entry.Length + 3) & ~0x03; E = (struct _TFFS_Entry *) ((unsigned int) E + sizeof(struct _TFFS_Entry) + Len); } return -ENFILE; err_out: if(rd_buf != NULL){ kfree(rd_buf); } return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Write(tffs_device *tffs_dev, void *handle, enum _tffs_id Id, uint8_t *write_buffer, size_t write_length, size_t *rlen, unsigned int final) { struct tffs_lgcy_ctx *ctx; int result; // pr_err("[%s] Called\n", __func__); if(Id >= FLASH_FS_ID_LAST){ pr_err("[%s] invalid tffs_id: 0x%x\n", __func__, Id); return -ENOENT; } if(!final){ return -ENOSPC; } ctx = (struct tffs_lgcy_ctx *)tffs_dev->priv; if(ctx == NULL){ return -EBADF; } result = do_write(ctx, Id, write_buffer, write_length, rlen); // no space in current segment. Retry after switching segments unless we are in panic mode if(result == -ENFILE && ctx->in_panic_mode == 0){ result = TFFS3_LGCY_Cleanup(tffs_dev, handle); if(result != 0){ result = -ENOSPC; goto err_out; } result = do_write(ctx, Id, write_buffer, write_length, rlen); } err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Read(tffs_device *tffs_dev, void *handle, enum _tffs_id Id, uint8_t *read_buffer, size_t *read_length) { struct tffs_lgcy_ctx *ctx; struct TFFS_LGCY_State *state; unsigned int retlen, ret; struct _TFFS_Entry *E, Entry; size_t Len; // pr_err("[%s] Called\n", __func__); ctx = (struct tffs_lgcy_ctx *)tffs_dev->priv; if(ctx == NULL){ return -EBADF; } state = (struct TFFS_LGCY_State *) handle; if(Id >= FLASH_FS_ID_LAST){ pr_err("[%s] invalid tffs_id: 0x%x\n", __func__, Id); return -ENOENT; } /*--- DBG((KERN_INFO "TFFS_LGCY_Read(handle=%x): id = 0x%x, max_length = %u\n", (unsigned int)handle, (unsigned int)Id, *read_length)); ---*/ E = TFFS_Global_Index[Id]; if(E != (struct _TFFS_Entry *) FLASH_FS_ID_FREE){ DBG((KERN_INFO "Eintrag gefunden\n")); ret = MTD_READ(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(ret){ return ret; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ if(Entry.Length > state->offset){ Len = min((unsigned int) (Entry.Length - state->offset), *read_length); ret = MTD_READ(ctx->active_mtd, (unsigned long) E + sizeof(struct _TFFS_Entry) + state->offset, Len, &retlen, read_buffer); if(ret){ return ret; } tffs3_write_statistic(Id, retlen, 0); *read_length = retlen; state->offset += Len; } else { *read_length = 0; } DBG((KERN_INFO "daten kopiert\n")); return 0; } DBG((KERN_INFO "Eintrag %i nicht gefunden\n", Id)); return -ENOENT; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Reindex(tffs_device *tffs_dev) { unsigned int retlen, ret; struct _TFFS_Entry *E, Entry; unsigned int Len; unsigned int i; struct tffs_lgcy_ctx *ctx; pr_err("[%s] Called\n", __func__); ctx = (struct tffs_lgcy_ctx *)tffs_dev->priv; if(ctx == NULL){ return -EBADF; } for(i = 0; i < FLASH_FS_ID_LAST; i++){ TFFS_Global_Index[i] = (struct _TFFS_Entry *) FLASH_FS_ID_FREE; } E = NULL; DBG((KERN_INFO "TFFS_LGCY_Create_Index(): ")); while((unsigned int) E + sizeof(struct _TFFS_Entry) < ctx->active_mtd->size){ ret = MTD_READ(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(ret){ return ret; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ if(Entry.ID >= (unsigned short) FLASH_FS_ID_LAST){ /*--- Eintrag gefunden ----*/ DBG((KERN_INFO " [end found]\n")); return 0; } DBG((KERN_INFO " [<0x%x> %u bytes]\n", Entry.ID, Entry.Length)); /*--- doppelter Eintrag gefunden, diesen Eintrag löschen ---*/ if(TFFS_Global_Index[Entry.ID] != (struct _TFFS_Entry *) FLASH_FS_ID_FREE){ if(Entry.ID != FLASH_FS_ID_SKIP){ printk(" [<0x%x> %u bytes, cleared]\n", Entry.ID, Entry.Length); printk("E = %#x\n", (unsigned int) E); Entry.ID = FLASH_FS_ID_SKIP; ret = MTD_WRITE(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(ret) return ret; tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ } }else{ TFFS_Global_Index[Entry.ID] = E; } Len = (Entry.Length + 3) & ~0x03; E = (struct _TFFS_Entry *) ((unsigned int) E + sizeof(struct _TFFS_Entry) + Len); } DBG((KERN_INFO " [filesystem full]\n")); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int Build_Cleanup_Buffer(struct tffs_lgcy_ctx *ctx, unsigned char *Cleanup_Buffer, unsigned int *Cleanup_Buffer_Len, unsigned int *SkipCount) { struct _TFFS_Entry *E, *pEntry; unsigned char *P; unsigned int Len, retlen, ret; pr_err("[%s] Called\n", __func__); E = (struct _TFFS_Entry *) 0; P = Cleanup_Buffer; while(((unsigned int) E) + sizeof(struct _TFFS_Entry) < ctx->active_mtd->size){ pEntry = (struct _TFFS_Entry *) P; ret = MTD_READ(ctx->active_mtd, (unsigned long) E, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) pEntry); if(ret){ return ret; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ if(pEntry->ID >= (unsigned short) FLASH_FS_ID_LAST){ /*--- Eintrag gefunden ----*/ pr_debug("[%s] [end found]\n", __func__); break; } Len = (pEntry->Length + 3) & ~0x03; if(pEntry->ID != FLASH_FS_ID_SKIP){ P += sizeof(struct _TFFS_Entry *); ret = MTD_READ(ctx->active_mtd, (unsigned long) E + sizeof(struct _TFFS_Entry), pEntry->Length, &retlen, (unsigned char *) P); if(ret){ return ret; } pr_debug("[%s] [<0x%x> %u bytes]\n", __func__, pEntry->ID, pEntry->Length); tffs3_write_statistic(pEntry->ID, retlen, 0); P += Len; }else{ (*SkipCount)++; pr_debug("[%s] [SKIP %u bytes]\n", __func__, pEntry->Length); } E = (struct _TFFS_Entry *) ((unsigned int) E + sizeof(struct _TFFS_Entry) + Len); } *Cleanup_Buffer_Len = P - Cleanup_Buffer; pr_debug("[%s] buffer_len %d\n", __func__, *Cleanup_Buffer_Len); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Cleanup(tffs_device *tffs_dev, void *handle) { unsigned int retlen, ret; unsigned int SkipCount = 0, Cleanup_Buffer_Len = 0; struct tffs_mtd *other; struct tffs_lgcy_ctx *ctx; union _tffs_segment_entry *pu; union _tffs_segment_entry u; uint32_t current_number; pr_err("[%s] Called\n", __func__); ctx = (struct tffs_lgcy_ctx *)tffs_dev->priv; if(ctx == NULL){ return -EBADF; } if(TFFS3_Cleanup_Buffer == NULL ){ TFFS3_Cleanup_Buffer = vmalloc(ctx->active_mtd->size); if(TFFS3_Cleanup_Buffer == NULL ){ pr_err("[%s] malloc(%u) failed\n", __func__, ctx->active_mtd->size); return -ENOMEM; } } /*------------------------------------------------------------------------------------------*\ * gültige Daten zusammesuchen, tffs-index bestimmen und tffs löschen, wenn es notwendig ist \*------------------------------------------------------------------------------------------*/ ret = Build_Cleanup_Buffer(ctx, TFFS3_Cleanup_Buffer, &Cleanup_Buffer_Len, &SkipCount); if(ret == 0 && SkipCount > 0){ pr_debug("[%s] [%u data records skiped]\n", __func__, SkipCount); other = (ctx->active_mtd == &(ctx->avail_mtd[0])) ? &(ctx->avail_mtd[1]) : &(ctx->avail_mtd[0]); pr_debug("[%s] close old mtd, open %s\n", __func__, other->mtd->name); if(get_mtd_device_wrapped(other, other->mtd_idx)){ panic("[%s] can't get mtd%u\n", __func__, other->mtd_idx); return -ENXIO; } pr_debug("[%s] formatting segment on %s\n", __func__, other->mtd->name); ret = TFFS3_LGCY_Format(other); if(ret){ pr_err("[%s] format failed\n", __func__); return ret; } }else{ pr_debug("[%s] no IDs skiped, leaving it as it is\n", __func__); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ pu = (union _tffs_segment_entry *) TFFS3_Cleanup_Buffer; if(pu->Entry.ID != FLASH_FS_ID_SEGMENT){ panic("TFFS_LGCY_Cleanup: flash segment %u file invalid\n", ctx->active_mtd->mtd_idx); } current_number = TFFS_GET_SEGMENT_VALUE(pu); DBG((KERN_INFO " [double buffer: read current number %u]\n", current_number)); TFFS_SET_SEGMENT_VALUE(pu, 0); /*------------------------------------------------------------------------------------------*\ * tffs neu schreiben \*------------------------------------------------------------------------------------------*/ DBG((KERN_INFO " [write filesystem]\n")); ret = MTD_WRITE(other, 0, Cleanup_Buffer_Len, &retlen, (unsigned char *) TFFS3_Cleanup_Buffer); if(ret){ return ret; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ /*------------------------------------------------------------------------------------------*\ * id setzen \*------------------------------------------------------------------------------------------*/ u.Entry.ID = FLASH_FS_ID_SEGMENT; u.Entry.Length = sizeof(unsigned int); current_number++; DBG((KERN_INFO " [double buffer: write current number %u]\n", current_number)); TFFS_SET_SEGMENT_VALUE(&u, current_number); ret = MTD_WRITE(other, 0, sizeof(union _tffs_segment_entry), &retlen, (unsigned char *) &u); if(ret){ return ret; } tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 1); /*--- internal write ---*/ /*------------------------------------------------------------------------------------------*\ * alles fertig, tffs umsetzen \*------------------------------------------------------------------------------------------*/ DBG((KERN_INFO "TFFS_LGCY_Cleanup: set mtd to %d\n", other)); put_mtd_device_wrapped(ctx->active_mtd); ctx->active_mtd = other; DBG((KERN_INFO " [recreate index]\n")); TFFS3_LGCY_Reindex(tffs_dev); DBG((KERN_INFO "TFFS_LGCY_Cleanup: success\n")); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Info(tffs_device *tffs_dev, unsigned int *Fill) { struct tffs_lgcy_ctx *ctx; unsigned int retlen, ret; struct _TFFS_Entry *E, Entry; unsigned int Count, Max = 0; DBG((KERN_INFO "TFFS_LGCY_Info(handle=%x): ", (unsigned int)handle)); ctx = (struct tffs_lgcy_ctx *) tffs_dev->priv; for(Count = 0, E = 0; Count < FLASH_FS_ID_LAST; Count++){ E = TFFS_Global_Index[Count]; if(E != (struct _TFFS_Entry *) FLASH_FS_ID_FREE){ DBG((KERN_INFO "E = 0x%x", (unsigned int)E)); if(Max < (unsigned int) E){ Max = (unsigned int) E; } } } if(!Max){ *Fill = 0; return 0; } DBG((KERN_INFO "Header des hoechsten Eintrags lesen (0x%x)\n", Max)); ret = MTD_READ(ctx->active_mtd, (unsigned long) Max, sizeof(struct _TFFS_Entry), &retlen, (unsigned char *) &Entry); if(ret) return ret; tffs3_write_statistic(FLASH_FS_ID_SKIP, retlen, 0); /*--- internal read ---*/ DBG((KERN_INFO " [<0x%x> %u bytes]\n", Entry.ID, Entry.Length)); Max += Entry.Length; *Fill = (Max * 100) / (unsigned int) (ctx->active_mtd->size); DBG((KERN_INFO "TFFS_LGCY_Info: Fill=%u%% success\n", *Fill)); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int TFFS3_LGCY_Setup(tffs_device *tffs_dev) { struct tffs_lgcy_ctx *ctx; int ret, retlen; union _tffs_segment_entry u; int result; ctx = (struct tffs_lgcy_ctx *) tffs_dev->priv; pr_err("[%s] Called\n", __func__); pr_err("[%s]mtd%u, mtd%u)\n", __func__, ctx->mtd_num[0], ctx->mtd_num[1]); result = get_mtd_device_wrapped(&(ctx->avail_mtd[0]), ctx->mtd_num[0]); if(result != 0){ pr_err("[%s] can't get mtd%u\n", __func__, ctx->mtd_num[0]); return -ENODEV; } result = get_mtd_device_wrapped(&(ctx->avail_mtd[1]), ctx->mtd_num[1]); if(result != 0){ pr_err("[%s] can't get mtd%u\n", __func__, ctx->mtd_num[1]); return -ENODEV; } /*------------------------------------------------------------------------------------------*\ * prüfen welcher buffer der "richtige" ist \*------------------------------------------------------------------------------------------*/ // TODO: potentieller Lesefehler wird nicht bearbeitet ret = MTD_READ(&(ctx->avail_mtd[0]), 0, sizeof(union _tffs_segment_entry), &retlen, u.Buffer); if(ret){ pr_err("[%s] MTD read failed, could not read a complete _tffs_segment_entry from %s\n", __func__, ctx->avail_mtd[0].mtd->name); } tffs3_write_statistic(u.Entry.ID, retlen, 0); if(u.Entry.ID == FLASH_FS_ID_SEGMENT){ ctx->avail_mtd[0].segment_id = TFFS_GET_SEGMENT_VALUE(&u); pr_info("[%s] double_buffer(0): segment value %u\n", __func__, ctx->avail_mtd[0].segment_id); }else{ pr_info("[%s] double_buffer(0): no SEGMENT VALUE (0x%x)\n", __func__, u.Entry.ID); } ret = MTD_READ(&(ctx->avail_mtd[1]), 0, sizeof(union _tffs_segment_entry), &retlen, u.Buffer); if(ret){ pr_err("[%s] MTD read failed, could not read a complete _tffs_segment_entry from %s\n", __func__, ctx->avail_mtd[1].mtd->name); } tffs3_write_statistic(u.Entry.ID, retlen, 0); if(u.Entry.ID == FLASH_FS_ID_SEGMENT){ ctx->avail_mtd[1].segment_id = TFFS_GET_SEGMENT_VALUE(&u); pr_info("[%s] double_buffer(1): segment value %u\n", __func__, ctx->avail_mtd[1].segment_id); }else{ pr_info("[%s] double_buffer(1): no SEGMENT VALUE (0x%x)\n", __func__, u.Entry.ID); } if(ctx->avail_mtd[0].segment_id == 0 && ctx->avail_mtd[1].segment_id == 0){ panic("TFFS: no valid filesystem"); } if(ctx->avail_mtd[0].segment_id > ctx->avail_mtd[1].segment_id){ put_mtd_device_wrapped(&(ctx->avail_mtd[1])); ctx->active_mtd = &(ctx->avail_mtd[0]); } else { put_mtd_device_wrapped(&(ctx->avail_mtd[0])); ctx->active_mtd = &(ctx->avail_mtd[1]); } pr_info("[%s] double_buffer: use segment %u (avail: %u + %u)\n", __func__, ctx->active_mtd->segment_id, ctx->avail_mtd[0].segment_id, ctx->avail_mtd[1].segment_id); pr_info("[%s] mtd%u size=0x%llx\n", __func__, ctx->active_mtd->mtd_idx, ctx->active_mtd->mtd->size); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_LGCY_Configure(tffs_device *tffs_dev, int mtd_num0, int mtd_num1) { struct tffs_lgcy_ctx *ctx; int result; pr_err("[%s] Called\n", __func__); result = -EINVAL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if(ctx == NULL){ pr_err("[%s] Out of memory error\n", __func__); result = -ENOMEM; goto err_out; } tffs_dev->setup = TFFS3_LGCY_Setup; tffs_dev->open = TFFS3_LGCY_Open; tffs_dev->close = TFFS3_LGCY_Close; tffs_dev->read = TFFS3_LGCY_Read; tffs_dev->write = TFFS3_LGCY_Write; tffs_dev->cleanup = TFFS3_LGCY_Cleanup; tffs_dev->reindex = TFFS3_LGCY_Reindex; tffs_dev->info = TFFS3_LGCY_Info; ctx->mtd_num[0] = mtd_num0; ctx->mtd_num[1] = mtd_num1; sema_init(&ctx->lock, 1); tffs_dev->priv = ctx; result = 0; err_out: return result; } EXPORT_SYMBOL(TFFS3_LGCY_Configure);