/*------------------------------------------------------------------------------------------*\ * * 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 \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_PROC_FS) #include #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ /*--- #define TFFS_DEBUG ---*/ #include "tffs_local.h" /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int tffs_mtd[2] = { CONFIG_TFFS_MTD_DEVICE_0 - 1, CONFIG_TFFS_MTD_DEVICE_1 - 1 }; MODULE_PARM_DESC (tffs_mtd, "MTD device used for tiny flash file system with double buffering"); MODULE_DESCRIPTION("TFFS 'tiny flash file system with double buffering' driver version 2.2"); MODULE_SUPPORTED_DEVICE("MTD[0..n]"); MODULE_LICENSE("GPL"); #define MODULE_VERSION "2.0" #ifdef CONFIG_PROC_FS static struct proc_dir_entry *tffs_proc; #endif /*--- #ifdef CONFIG_PROC_FS ---*/ /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void tffs_cleanup(void); struct file_operations tffs_fops = { owner: THIS_MODULE, open: tffs_open, release: tffs_release, read: tffs_read, write: tffs_write, ioctl: tffs_ioctl, }; struct _tffs tffs; struct semaphore tffs_sema; static struct semaphore tffs_event_sema; static struct semaphore tffs_event_lock_sema; static int tffs_thread_pid; int tffs_thread_event; static DECLARE_COMPLETION(tffs_evt_dead); enum _tffs_thread_state tffs_thread_state; unsigned int tffs_request_count = 0; /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int tffs_lock(void) { if(down_interruptible(&tffs_sema)) { return -1; } return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void tffs_unlock(void) { up(&tffs_sema); } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static int tffs_thread(void *reply_data) { unsigned int event; void *handle; tffs_thread_state = tffs_thread_state_init; lock_kernel(); daemonize(); unlock_kernel(); strcpy(current->comm, "tffsd_mtd_"); current->comm[9] = '0' + tffs_mtd[0]; handle = TFFS_Open(); tffs_thread_state = tffs_thread_state_idle; for( ;; ) { DBG((KERN_INFO "tffsd: wait for events\n")); if(down_interruptible(&tffs_event_sema)) { break; } tffs_thread_state = tffs_thread_state_process; /*--- lesen des Events ---*/ down(&tffs_event_lock_sema); event = tffs_thread_event; tffs_thread_event = 0; up(&tffs_event_lock_sema); tffs_request_count++; /*--- auswerten des Events ---*/ DBG((KERN_INFO "tffsd: process event 0x%x\n", event)); if(event & TFFS_EVENT_CLEANUP) { DBG((KERN_DEBUG "%s: tffsd: cleanup\n", MODULE_NAME)); if(down_interruptible(&tffs_sema)) { break; } TFFS_Cleanup(handle); up(&tffs_sema); } } TFFS_Close(handle); DBG((KERN_INFO "tffsd: event thread dead\n")); tffs_thread_state = tffs_thread_state_down; complete_and_exit(&tffs_evt_dead, 0); return 0; } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ void tffs_send_event(unsigned int event) { DBG((KERN_INFO "tffs_send_event(%x)\n", event)); down(&tffs_event_lock_sema); tffs_thread_event |= event; up(&tffs_event_lock_sema); up(&tffs_event_sema); } /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ int __init tffs_init(void) { static char tffs_mtd_string[6] = "mtd_"; DBG((KERN_INFO "%s: register_chrdev(%s) param=mtd%u\n", MODULE_NAME, MODULE_NAME, tffs_mtd[0])); DBG((KERN_INFO "%s: CONFIG_TFFS_MTD_DEVICE_0=%u CONFIG_TFFS_MTD_DEVICE_1=%u\n", MODULE_NAME, CONFIG_TFFS_MTD_DEVICE_0, CONFIG_TFFS_MTD_DEVICE_1)); tffs.major = register_chrdev(0 /*--- dynamic major ---*/, MODULE_NAME, &tffs_fops); if(tffs.major < 1) { DBG((KERN_ERR "%s: register_chrdrv failed: reason %d\n", MODULE_NAME, tffs.major)); return -ERESTARTSYS; } tffs.devfs_dir_handle = devfs_mk_dir(NULL, "tffs", NULL); tffs_mtd_string[3] = tffs_mtd[0] + '0'; tffs.minor = 0; tffs.devfs_handle = devfs_register(tffs.devfs_dir_handle, tffs_mtd_string, DEVFS_FL_DEFAULT, tffs.major, tffs.minor, S_IFCHR | S_IRUGO | S_IWUSR, &tffs_fops, NULL); if(TFFS_Init(tffs_mtd[0], tffs_mtd[1])) { tffs_cleanup(); return -ERESTARTSYS; } sema_init(&tffs_sema, 1); sema_init(&tffs_event_sema, 0); sema_init(&tffs_event_lock_sema, 1); tffs.devfs_ticfg_handle = devfs_register(NULL, "ticfg", DEVFS_FL_DEFAULT, tffs.major, FLASH_FS_ID_TICFG, S_IFCHR | S_IRUGO | S_IWUSR, &tffs_fops, NULL); /*--- tffs.devfs_shutdown_handle = devfs_register(NULL, "shutdown_cmd", DEVFS_FL_DEFAULT, tffs.major, FLASH_FS_ID_SHUTDOWN, ---*/ /*--- S_IFCHR | S_IRUGO | S_IWUSR, &tffs_fops, NULL); ---*/ DBG((KERN_INFO "%s: major %d (success)\n", MODULE_NAME, tffs.major)); #ifdef CONFIG_PROC_FS if ((tffs_proc = create_proc_entry( "tffs", 0, 0 ))) tffs_proc->read_proc = tffs_read_proc; tffs_proc->write_proc = tffs_write_proc; #endif tffs_thread_state = tffs_thread_state_off; tffs_thread_pid = kernel_thread(tffs_thread, &tffs_thread_event, CLONE_SIGHAND); printk("TFFS: tiny flash file system driver. GPL (c) AVM Berlin (Version %s)\n", MODULE_VERSION); printk(" mount on mtd%u and mtd%u (double buffering)\n", tffs_mtd[0], tffs_mtd[1]); #ifdef CONFIG_SYSCTL avm_urlader_env_init(); #endif /* CONFIG_SYSCTL */ return 0; } module_init(tffs_init); /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ static void tffs_cleanup(void) { #if defined(MODLE) DBG((KERN_INFO "%s: unregister_chrdev(%u)\n", MODULE_NAME, tffs.major)); if(down_interruptible(&tffs_sema)) { DBG((KERN_ERR "%s: tffs_cleanup: down_interruptible() failed\n", MODULE_NAME)); return; } #ifdef CONFIG_SYSCTL avm_urlader_env_exit(); #endif /* CONFIG_SYSCTL */ if(tffs_thread_pid) { int stat; DBG((KERN_INFO "%s: Terminating thread ", MODULE_NAME)); stat = kill_proc(tffs_thread_pid, SIGTERM, 1); if(!stat) { DBG(("waiting...")); wait_for_completion(&tffs_evt_dead); } DBG(("done.\n")); } TFFS_Deinit(); if(tffs_proc) remove_proc_entry( "tffs", 0); if(tffs.devfs_handle) devfs_unregister(tffs.devfs_handle); if(tffs.devfs_ticfg_handle) devfs_unregister(tffs.devfs_ticfg_handle); /*--- if(tffs.devfs_shutdown_handle) ---*/ /*--- devfs_unregister(tffs.devfs_shutdown_handle); ---*/ if(tffs.devfs_dir_handle) unregister_chrdev(tffs.major, MODULE_NAME); up(&tffs_sema); #endif /*--- #if defined(MODLE) ---*/ return; } module_exit(tffs_cleanup); EXPORT_NO_SYMBOLS; /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/