/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2004-2014 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 "tffs_local.h" /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ struct _tffs3_cfg_funcs tffs3_cfg_funcs = { #if defined(CONFIG_TFFS_DEV_MTDNOR) .mtdnor = TFFS3_NOR_Configure, #endif #if defined(CONFIG_TFFS_DEV_MTDNAND) .mtdnand = TFFS3_NAND_Configure, #endif #if defined(CONFIG_TFFS_DEV_BDEV) .bdev = TFFS3_BDEV_Configure, #endif #if defined(CONFIG_TFFS_DEV_CACHE) .cache = TFFS3_CACHE_Configure, #endif #if defined(CONFIG_TFFS_DEV_REMOTE) .remote = TFFS3_REMOTE_Configure; #endif #if defined(CONFIG_TFFS_DEV_LEGACY) .legacy = TFFS3_LGCY_Configure, #endif }; static tffs_device TFFS_device; static int lock_device(tffs_device *device, struct _tffs_open *handle) { int result; result = 0; // if device is in panic mode, only the panic mode handle may access it. if(device->in_panic_mode && handle->mode != tffs3_mode_panic){ result = -EBUSY; goto err_out; } if(handle->mode == tffs3_mode_panic){ result = down_trylock(&(device->lock)); } else { result = down_interruptible(&(device->lock)); } if(result != 0){ result = (handle->mode == tffs3_mode_panic) ? -EBUSY : result; goto err_out; } err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Early_Init(void) { sema_init(&TFFS_device.lock, 1); TFFS_device.initialised = 1; pr_err("[%s] called\n", __func__); return 0; } arch_initcall(TFFS3_Early_Init); /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Register_NAND(unsigned int mtd_number, loff_t raw_offset) { int result; pr_err("[%s] Called\n", __func__); if(!TFFS_device.initialised){ pr_err("[%s] TFFS_device not initialised\n", __func__); return -EAGAIN; } result = -ENODEV; down(&TFFS_device.lock); if(!TFFS_device.config_done){ if(tffs3_cfg_funcs.mtdnand){ result = tffs3_cfg_funcs.mtdnand(&TFFS_device, mtd_number, raw_offset); if(result == 0){ TFFS_device.config_done = 1; } } else { pr_err("[%s] No config function registered for NAND\n", __func__); } } else { pr_err("[%s] Backing device already registered.\n", __func__); result = -EINVAL; } up(&TFFS_device.lock); return result; } int TFFS3_Register_Panic(enum tffs3_types type, union tffs3_panic_funcs *funcs) { int result = 0; switch(type){ case tffs3_type_mtdnand: TFFS_device.nand_fn = funcs->nand; break; case tffs3_type_mtdnor: TFFS_device.mtdnor_fn = funcs->mtdnor; break; default: pr_err("[%s] can not register panic funtions for unknown device type 0x%x\n", __func__, type); result = -EINVAL; break; } return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Init(void) { int result; result = -ENODEV; down(&TFFS_device.lock); #if defined(CONFIG_TFFS_DEV_LEGACY) if(!TFFS_device.config_done && tffs3_cfg_funcs.legacy){ pr_err("[%s] No storage module registered, trying legacy fallback\n", __func__); result = TFFS3_LGCY_Configure(&TFFS_device, tffs_mtd[0], tffs_mtd[1]); if(result == 0){ TFFS_device.config_done = 1; } } #endif if(TFFS_device.config_done && !TFFS_device.setup_done){ if(TFFS_device.setup){ result = TFFS_device.setup(&TFFS_device); if(result == 0){ TFFS_device.setup_done = 1; } } else { pr_err("[%s] No setup function registered for NAND\n", __func__); } } else { pr_err("[%s] Backing device either not configured or already set up.\n", __func__); result = -EINVAL; } up(&TFFS_device.lock); return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void TFFS3_Deinit(void) { // pr_err("[%s] called\n", __func__); } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void *TFFS3_Open(enum tffs3_handle_mode mode) { struct _tffs_open *handle; int result; // pr_err("[%s] called\n", __func__); handle = NULL; result = 0; if(mode == tffs3_mode_panic){ result = down_trylock(&TFFS_device.lock); result = (result == 0) ? 0 : -EBUSY; } else { result = down_interruptible(&TFFS_device.lock); result = (result == 0) ? 0 : -EINTR; } // refuse opening new handles while a panic mode handle exists if(TFFS_device.in_panic_mode != 0){ result = -EBUSY; } if(result != 0){ goto err_out; } if(mode == tffs3_mode_panic){ handle = &TFFS_device.panic_handle; TFFS_device.in_panic_mode = 1; } else { handle = kmalloc(sizeof(*handle), GFP_KERNEL); } if(handle == NULL) { pr_err("[%s] getting %shandle failed\n", __func__, TFFS_device.in_panic_mode ? "panic " : ""); result = -ENOMEM; goto err_out; } memset(handle, 0x0, sizeof(*handle)); handle->mode = mode; handle->core_priv = TFFS_device.open(&TFFS_device, handle); if(handle->core_priv == NULL){ result = -ENOMEM; } err_out: if(result != 0 && handle != NULL){ if(handle->core_priv != NULL){ result = TFFS_device.close(&TFFS_device, handle->core_priv); } if(TFFS_device.in_panic_mode != 0){ TFFS_device.in_panic_mode = 0; } else { kfree(handle); } handle = NULL; } up(&TFFS_device.lock); return handle; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Close(void *handle) { struct _tffs_open *tffs_handle; int result; // pr_err("[%s] called\n", __func__); if(handle == NULL){ result = -EBADF; goto err_out; } tffs_handle = (struct _tffs_open *)handle; result = lock_device(&TFFS_device, tffs_handle); if(result != 0){ goto err_out; } result = TFFS_device.close(&TFFS_device, tffs_handle->core_priv); if(result == 0){ if(tffs_handle->mode == tffs3_mode_panic){ TFFS_device.in_panic_mode = 0; } else { tffs_handle->core_priv = NULL; kfree(tffs_handle); } } up(&TFFS_device.lock); err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Clear(void *handle, enum _tffs_id id) { // pr_err("[%s] called\n", __func__); return TFFS3_Write(handle, id, NULL, 0, 0); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int TFFS3_Werkseinstellungen(void *handle) { struct _tffs_open *tmp_handle; enum _tffs_id id; unsigned int count; int result; // pr_err("[%s] called\n", __func__); if(TFFS_device.in_panic_mode){ return -EBUSY; } DBG((KERN_INFO "TFFS3_Werkseinstellungen(0x%x)\n", (int)handle)); result = 0; count = 0; for(id = FLASH_FS_ID_TICFG ; id <= FLASH_FS_ID_FIRMWARE_CONFIG_LAST ; id++){ tmp_handle = TFFS3_Open(tffs3_mode_write); if(tmp_handle == NULL){ result = -ENOMEM; goto err_out; } result = TFFS3_Clear(tmp_handle, id); TFFS3_Close(tmp_handle); if(result) { DBG((KERN_INFO "TFFS3_Werkseinstellungen(0x%x): clear id 0x%x failed (%u cleared)\n", (int)handle, (int)id, count)); goto err_out; } ++count; } DBG((KERN_INFO "TFFS3_Werkseinstellungen(0x%x): success (%u cleared)\n", (int)handle, count)); err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Write(void *handle, enum _tffs_id Id, uint8_t *write_buffer, size_t write_length, unsigned int final) { size_t written; struct _tffs_open *tffs_handle; int result; // pr_err("[%s] called\n", __func__); tffs_handle = (struct _tffs_open *)handle; result = lock_device(&TFFS_device, tffs_handle); if(result != 0){ goto err_out; } result = TFFS_device.write(&TFFS_device, tffs_handle->core_priv, Id, write_buffer, write_length, &written, final); up(&TFFS_device.lock); err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Read(void *handle, enum _tffs_id Id, uint8_t *read_buffer, size_t *read_length) { struct _tffs_open *tffs_handle; int result; // pr_err("[%s] called\n", __func__); tffs_handle = (struct _tffs_open *)handle; result = lock_device(&TFFS_device, tffs_handle); if(result != 0){ goto err_out; } result = TFFS_device.read(&TFFS_device, tffs_handle->core_priv, Id, read_buffer, read_length); up(&TFFS_device.lock); err_out: return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int TFFS3_Cleanup(void *handle) { struct _tffs_open *tffs_handle; int result; // pr_err("[%s] called\n", __func__); if(TFFS_device.in_panic_mode){ return -EBUSY; } tffs_handle = (struct _tffs_open *)handle; result = lock_device(&TFFS_device, tffs_handle); if(result != 0){ goto err_out; } result = TFFS_device.cleanup(&TFFS_device, tffs_handle->core_priv); up(&TFFS_device.lock); err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Create_Index(void) { int result; // pr_err("[%s] called\n", __func__); if(TFFS_device.in_panic_mode){ return -EBUSY; } result = down_interruptible(&(TFFS_device.lock)); if(result != 0){ goto err_out; } result = TFFS_device.reindex(&TFFS_device); up(&(TFFS_device.lock)); err_out: return result; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int TFFS3_Info(void *handle, unsigned int *Fill) { struct _tffs_open *tffs_handle; int result; // pr_err("[%s] called\n", __func__); tffs_handle = (struct _tffs_open *)handle; result = lock_device(&TFFS_device, tffs_handle); if(result != 0){ goto err_out; } result = TFFS_device.info(&TFFS_device, Fill); up(&TFFS_device.lock); err_out: return result; } EXPORT_SYMBOL(TFFS3_Open); EXPORT_SYMBOL(TFFS3_Close); EXPORT_SYMBOL(TFFS3_Read); EXPORT_SYMBOL(TFFS3_Write);