/* * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file linux_port.h * * OS_PORT ported to Linux (2.6.9+ for now) * */ /*! * @if USE_MAINPAGE * @mainpage ==Linux version of== Generic OS API for STC Drivers * @endif * * @section intro_sec Introduction * * This API / kernel programming environment blah blah. * * See @ref dkops "Driver-to-Kernel Operations" as a good place to start. */ #ifndef LINUX_PORT_H #define LINUX_PORT_H #define PORTABLE_OS_VERSION 101 /* Linux Kernel Includes */ #include /* Current version Linux kernel */ #if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) #include #endif #define MODVERSIONS #endif /*! * __NO_VERSION__ defined due to Kernel module possibly spanning multiple * files. */ #define __NO_VERSION__ #include /* Basic support for loadable modules, printk */ #include /* module_init, module_exit */ #include /* General kernel system calls */ #include /* for interrupt.h */ #include /* for inode */ #include #include #include #include #include /* kmalloc */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) #include /* used in dynamic power management */ #else #include /* used in dynamic power management */ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) #include /* clock en/disable for DPM */ #else #include /* clock en/disable for DPM */ #endif #include #include #include /* ioremap() */ #include #include /* copy_to_user(), copy_from_user() */ #include #ifndef TRUE /*! Useful symbol for unsigned values used as flags. */ #define TRUE 1 #endif #ifndef FALSE /*! Useful symbol for unsigned values used as flags. */ #define FALSE 0 #endif /* These symbols are defined in Linux 2.6 and later. Include here for minimal * support of 2.4 kernel. **/ #if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) /*! * Symbol defined somewhere in 2.5/2.6. It is the return signature of an ISR. */ #define irqreturn_t void /*! Possible return value of 'modern' ISR routine. */ #define IRQ_HANDLED /*! Method of generating value of 'modern' ISR routine. */ #define IRQ_RETVAL(x) #endif /*! * Type used for registering and deregistering interrupts. */ typedef int os_interrupt_id_t; /*! * Type used as handle for a process * * See #os_get_process_handle() and #os_send_signal(). */ /* * The following should be defined this way, but it gets compiler errors * on the current tool chain. * * typedef task_t *os_process_handle_t; */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) typedef task_t *os_process_handle_t; #else typedef struct task_struct *os_process_handle_t; #endif /*! * Generic return code for functions which need such a thing. * * No knowledge should be assumed of the value of any of these symbols except * that @c OS_ERROR_OK_S is guaranteed to be zero. */ typedef enum { OS_ERROR_OK_S = 0, /*!< Success */ OS_ERROR_FAIL_S = -EIO, /*!< Generic driver failure */ OS_ERROR_NO_MEMORY_S = -ENOMEM, /*!< Failure to acquire/use memory */ OS_ERROR_BAD_ADDRESS = -EFAULT /*!< Bad address */ } os_error_code; /*! * Handle to a lock. */ #ifdef CONFIG_PREEMPT_RT typedef raw_spinlock_t *os_lock_t; #else typedef spinlock_t *os_lock_t; #endif /*! * Context while locking. */ typedef unsigned long os_lock_context_t; /*! * Declare a wait object for sleeping/waking processes. */ #define OS_WAIT_OBJECT(name) \ DECLARE_WAIT_QUEUE_HEAD(name##_qh) /*! * Driver registration handle * * Used with #os_driver_init_registration(), #os_driver_add_registration(), * and #os_driver_complete_registration(). */ typedef struct { unsigned reg_complete; /*!< TRUE if next inits succeeded. */ dev_t dev; /*!< dev_t for register_chrdev() */ struct file_operations fops; /*!< struct for register_chrdev() */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) struct class_simple *cs; /*!< results of class_simple_create() */ #else struct class *cs; /*!< results of class_create() */ #endif struct class_device *cd; /*!< Result of class_simple_device_add() */ unsigned power_complete; /*!< TRUE if next inits succeeded */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) struct device_driver dd; /*!< struct for register_driver() */ #else struct platform_driver dd; /*!< struct for register_driver() */ #endif struct platform_device pd; /*!< struct for platform_register_device() */ } os_driver_reg_t; /* * Function types which can be associated with driver entry points. * * Note that init and shutdown are absent. */ /*! @{ */ /*! Keyword for registering open() operation handler. */ #define OS_FN_OPEN open /*! Keyword for registering close() operation handler. */ #define OS_FN_CLOSE release /*! Keyword for registering read() operation handler. */ #define OS_FN_READ read /*! Keyword for registering write() operation handler. */ #define OS_FN_WRITE write /*! Keyword for registering ioctl() operation handler. */ #define OS_FN_IOCTL ioctl /*! Keyword for registering mmap() operation handler. */ #define OS_FN_MMAP mmap /*! @} */ /*! * Function signature for the portable interrupt handler * * While it would be nice to know which interrupt is being serviced, the * Least Common Denominator rule says that no arguments get passed in. * * @return Zero if not handled, non-zero if handled. */ typedef int (*os_interrupt_handler_t) (int, void *); /*! * @defgroup dkops Driver-to-Kernel Operations * * These are the operations which drivers should call to get the OS to perform * services. */ /*! @addtogroup dkops */ /*! @{ */ /*! * Register an interrupt handler. * * @param driver_name The name of the driver * @param interrupt_id The interrupt line to monitor (type * #os_interrupt_id_t) * @param function The function to be called to handle an interrupt * * @return #os_error_code */ #define os_register_interrupt(driver_name, interrupt_id, function) \ request_irq(interrupt_id, function, 0, driver_name, NULL) /*! * Deregister an interrupt handler. * * @param interrupt_id The interrupt line to stop monitoring * * @return #os_error_code */ #define os_deregister_interrupt(interrupt_id) \ free_irq(interrupt_id, NULL) /*! * INTERNAL implementation of os_driver_init_registration() * * @return An os error code. */ inline static int os_drv_do_init_reg(os_driver_reg_t * handle) { memset(handle, sizeof(*handle), 0); handle->fops.owner = THIS_MODULE; handle->power_complete = FALSE; handle->reg_complete = FALSE; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) handle->dd.name = NULL; #else handle->dd.driver.name = NULL; #endif return OS_ERROR_OK_S; } /*! * Initialize driver registration. * * If the driver handles open(), close(), ioctl(), read(), write(), or mmap() * calls, then it needs to register their location with the kernel so that they * get associated with the device. * * @param handle The handle object to be used with this registration. The * object must live (be in memory somewhere) at least until * os_driver_remove_registration() is called. * * @return A handle for further driver registration, or NULL if failed. */ #define os_driver_init_registration(handle) \ os_drv_do_init_reg(&handle) /*! * Add a function registration to driver registration. * * @param handle A handle initialized by #os_driver_init_registration(). * @param name Which function is being supported. * @param function The result of a call to a @c _REF version of one of the * driver function signature macros * @return void */ #define os_driver_add_registration(handle, name, function) \ do {handle.fops.name = (void*)(function); } while (0) /*! * Record 'power suspend' function for the device. * * @param handle A handle initialized by #os_driver_init_registration(). * @param function Name of function to call on power suspend request * * Status: Provisional * * @return void */ #define os_driver_register_power_suspend(handle, function) \ handle.dd.suspend = function /*! * Record 'power resume' function for the device. * * @param handle A handle initialized by #os_driver_init_registration(). * @param function Name of function to call on power resume request * * Status: Provisional * * @return void */ #define os_driver_register_resume(handle, function) \ handle.dd.resume = function /*! * INTERNAL function of the Linux port of the OS API. Implements the * os_driver_complete_registration() function. * * @param handle The handle used with #os_driver_init_registration(). * @param major The major device number to be associated with the driver. * If this value is zero, a major number may be assigned. * See #os_driver_get_major() to determine final value. * #os_driver_remove_registration(). * @param driver_name The driver name. Can be used as part of 'device node' * name on platforms which support such a feature. * * @return An error code */ inline static int os_drv_do_reg(os_driver_reg_t * handle, unsigned major, char *driver_name) { os_error_code code = OS_ERROR_NO_MEMORY_S; char *name = kmalloc(strlen(driver_name) + 1, 0); if (name != NULL) { memcpy(name, driver_name, strlen(driver_name) + 1); code = OS_ERROR_OK_S; /* OK so far */ /* If any chardev/POSIX routines were added, then do chrdev part */ if (handle->fops.open || handle->fops.release || handle->fops.read || handle->fops.write || handle->fops.ioctl || handle->fops.mmap) { code = register_chrdev(major, driver_name, &handle->fops); if (code < 0) { code = OS_ERROR_FAIL_S; } else { if (code != 0) { /* Zero was passed in for major; code is actual value */ handle->dev = MKDEV(code, 0); } else { handle->dev = MKDEV(major, 0); } code = OS_ERROR_OK_S; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) handle->cs = class_simple_create(THIS_MODULE, driver_name); if (IS_ERR(handle->cs)) { code = (os_error_code) handle->cs; handle->cs = NULL; } else { handle->cd = class_simple_device_add(handle->cs, handle->dev, NULL, driver_name); if (IS_ERR(handle->cd)) { class_simple_device_remove (handle->dev); unregister_chrdev(MAJOR (handle->dev), driver_name); code = (os_error_code) handle->cs; handle->cs = NULL; } else { handle->reg_complete = TRUE; } } #else handle->cs = class_create(THIS_MODULE, driver_name); if (IS_ERR(handle->cs)) { code = (os_error_code) handle->cs; handle->cs = NULL; } else { handle->cd = class_device_create(handle->cs, NULL, handle->dev, NULL, driver_name); if (IS_ERR(handle->cd)) { class_device_destroy(handle->cs, handle-> dev); class_destroy(handle->cs); unregister_chrdev(MAJOR (handle->dev), driver_name); code = (os_error_code) handle->cs; handle->cs = NULL; } else { handle->reg_complete = TRUE; } } #endif } } /* ... fops routine registered */ /* Handle power management fns through separate interface */ if ((code == OS_ERROR_OK_S) && (handle->dd.suspend || handle->dd.resume)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) handle->dd.name = name; handle->dd.bus = &platform_bus_type; code = driver_register(&handle->dd); #else handle->dd.driver.name = name; handle->dd.driver.bus = &platform_bus_type; code = driver_register(&handle->dd.driver); #endif if (code == OS_ERROR_OK_S) { handle->pd.name = name; handle->pd.id = 0; code = platform_device_register(&handle->pd); if (code != OS_ERROR_OK_S) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) driver_unregister(&handle->dd); #else driver_unregister(&handle->dd.driver); #endif } else { handle->power_complete = TRUE; } } } /* ... suspend or resume */ } /* name != NULL */ return code; } /*! * Finalize the driver registration with the kernel. * * Upon return from this call, the driver may begin receiving calls at the * defined entry points. * * @param handle The handle used with #os_driver_init_registration(). * @param major The major device number to be associated with the driver. * If this value is zero, a major number may be assigned. * See #os_driver_get_major() to determine final value. * #os_driver_remove_registration(). * @param driver_name The driver name. Can be used as part of 'device node' * name on platforms which support such a feature. * * @return An error code */ #define os_driver_complete_registration(handle, major, driver_name) \ os_drv_do_reg(&handle, major, driver_name) /*! * Get driver Major Number from handle after a successful registration. * * @param handle A handle which has completed registration. * * @return The major number (if any) associated with the handle. */ #define os_driver_get_major(handle) \ (handle.reg_complete ? MAJOR(handle.dev) : -1) /*! * INTERNAL implemention of os_driver_remove_registration. * * @param handle A handle initialized by #os_driver_init_registration(). * * @return An error code. */ inline static int os_drv_rmv_reg(os_driver_reg_t * handle) { if (handle->reg_complete) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) if (handle->cd != NULL) { class_simple_device_remove(handle->dev); handle->cd = NULL; } if (handle->cs != NULL) { class_simple_destroy(handle->cs); handle->cs = NULL; } unregister_chrdev(MAJOR(handle->dev), handle->dd.name); #else if (handle->cd != NULL) { class_device_destroy(handle->cs, handle->dev); handle->cd = NULL; } if (handle->cs != NULL) { class_destroy(handle->cs); handle->cs = NULL; } unregister_chrdev(MAJOR(handle->dev), handle->dd.driver.name); #endif handle->reg_complete = FALSE; } if (handle->power_complete) { platform_device_unregister(&handle->pd); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) driver_unregister(&handle->dd); #else driver_unregister(&handle->dd.driver); #endif handle->power_complete = FALSE; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) if (handle->dd.name != NULL) { kfree(handle->dd.name); handle->dd.name = NULL; } #else if (handle->dd.driver.name != NULL) { kfree(handle->dd.driver.name); handle->dd.driver.name = NULL; } #endif return OS_ERROR_OK_S; } /*! * Remove the driver's registration with the kernel. * * Upon return from this call, the driver not receive any more calls at the * defined entry points (other than ISR and shutdown). * * @param handle A handle initialized by #os_driver_init_registration(). * * @return An error code. */ #define os_driver_remove_registration(handle) \ os_drv_rmv_reg(&handle) /*! * Register a driver with the Linux Device Model. * * @param driver_information The device_driver structure information * * @return An error code. * * Status: denigrated in favor of #os_driver_complete_registration() */ #define os_register_to_driver(driver_information) \ driver_register(driver_information) /*! * Unregister a driver from the Linux Device Model * * this routine unregisters from the Linux Device Model * * @param driver_information The device_driver structure information * * @return An error code. * * Status: Denigrated. See #os_register_to_driver(). */ #define os_unregister_from_driver(driver_information) \ driver_unregister(driver_information) /*! * register a device to a driver * * this routine registers a drivers devices to the Linux Device Model * * @param device_information The platform_device structure information * * @return An error code. * * Status: denigrated in favor of #os_driver_complete_registration() */ #define os_register_a_device(device_information) \ platform_device_register(device_information) /*! * unregister a device from a driver * * this routine unregisters a drivers devices from the Linux Device Model * * @param device_information The platform_device structure information * * @return An error code. * * Status: Denigrated. See #os_register_a_device(). */ #define os_unregister_a_device(device_information) \ platform_device_unregister(device_information) /*! * Print a message to console / into log file. After the @c msg argument a * number of printf-style arguments may be added. Types should be limited to * printf string, char, octal, decimal, and hexadecimal types. (This excludes * pointers, and floating point). * * @param msg The main text of the message to be logged * @param s The printf-style arguments which go with msg, if any * * @return (void) */ /* This may be a GCC-ism which needs to be ported to ANSI */ #define os_printk(msg, s...) \ (void) printk(msg, ## s) /*! * Prepare a task to execute the given function. This should only be done once * per function,, during the driver's initialization routine. * * @param task_fn Name of the OS_DEV_TASK() function to be created. * * @return an OS ERROR code. */ #define os_create_task(function_name) \ OS_ERROR_OK_S /*! * Schedule execution of a task. * * @param function_name The function associated with the task. * * @return (void) */ #define os_dev_schedule_task(function_name) \ tasklet_schedule(&(function_name ## let)) /*! * Make sure that task is no longer running and will no longer run. * * This function will not return until both are true. This is useful when * shutting down a driver. */ #define os_dev_stop_task(function_name) \ do { \ tasklet_disable(&(function_name ## let)); \ tasklet_kill(&(function_name ## let)); \ } while (0) /*! * Allocate some kernel memory * * @param amount Number of 8-bit bytes to allocate * @param flags Some indication of purpose of memory (needs definition) * * @return Pointer to allocated memory, or NULL if failed. */ #define os_alloc_memory(amount, flags) \ (void*)kmalloc(amount, flags) /*! * Free some kernel memory * * @param location The beginning of the region to be freed. * * Do some OSes have separate free() functions which should be * distinguished by passing in @c flags here, too? Don't some also require the * size of the buffer being freed? */ #define os_free_memory(location) \ kfree(location) /*! * Allocate cache-coherent memory * * @param amount Number of bytes to allocate * @param[out] dma_addrp Location to store physical address of allocated * memory. * @param flags Some indication of purpose of memory (needs * definition). * * @return (virtual space) pointer to allocated memory, or NULL if failed. * */ #define os_alloc_coherent(amount, dma_addrp, flags) \ (void*)dma_alloc_coherent(NULL, amount, dma_addrp, flags) /*! * Free cache-coherent memory * * @param size Number of bytes which were allocated. * @param virt_addr Virtual(kernel) address of memory.to be freed, as * returned by #os_alloc_coherent(). * @param dma_addr Physical address of memory.to be freed, as returned * by #os_alloc_coherent(). * * @return void * */ #define os_free_coherent(size, virt_addr, dma_addr) \ dma_free_coherent(NULL, size, virt_addr, dma_addr /*! * Map an I/O space into kernel memory space * * @param start The starting address of the (physical / io space) region * @param range_bytes The number of bytes to map * * @return A pointer to the mapped area, or NULL on failure */ #define os_map_device(start, range_bytes) \ (void*)ioremap_nocache((start), range_bytes) /*! * Unmap an I/O space from kernel memory space * * @param start The starting address of the (virtual) region * @param range_bytes The number of bytes to unmap * * @return None */ #define os_unmap_device(start, range_bytes) \ iounmap((void*)(start)) /*! * Copy data from Kernel space to User space * * @param to The target location in user memory * @param from The source location in kernel memory * @param size The number of bytes to be copied * * @return #os_error_code */ #define os_copy_to_user(to, from, size) \ ((copy_to_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS) /*! * Copy data from User space to Kernel space * * @param to The target location in kernel memory * @param from The source location in user memory * @param size The number of bytes to be copied * * @return #os_error_code */ #define os_copy_from_user(to, from, size) \ ((copy_from_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS) /*! * Read a 8-bit device register * * @param register_address The (bus) address of the register to write to * @return The value in the register */ #define os_read8(register_address) \ __raw_readb(register_address) /*! * Write a 8-bit device register * * @param register_address The (bus) address of the register to write to * @param value The value to write into the register */ #define os_write8(register_address, value) \ __raw_writeb(value, register_address) /*! * Read a 16-bit device register * * @param register_address The (bus) address of the register to write to * @return The value in the register */ #define os_read16(register_address) \ __raw_readw(register_address) /*! * Write a 16-bit device register * * @param register_address The (bus) address of the register to write to * @param value The value to write into the register */ #define os_write16(register_address, value) \ __raw_writew(value, (uint32_t*)(register_address)) /*! * Read a 32-bit device register * * @param register_address The (bus) address of the register to write to * @return The value in the register */ #define os_read32(register_address) \ __raw_readl((uint32_t*)(register_address)) /*! * Write a 32-bit device register * * @param register_address The (bus) address of the register to write to * @param value The value to write into the register */ #define os_write32(register_address, value) \ __raw_writel(value, register_address) /*! * Read a 64-bit device register * * @param register_address The (bus) address of the register to write to * @return The value in the register */ #define os_read64(register_address) \ ERROR_UNIMPLEMENTED /*! * Write a 64-bit device register * * @param register_address The (bus) address of the register to write to * @param value The value to write into the register */ #define os_write64(register_address, value) \ ERROR_UNIMPLEMENTED /*! * Delay some number of microseconds * * Note that this is a busy-loop, not a suspension of the task/process. * * @param msecs The number of microseconds to delay * * @return void */ #define os_mdelay mdelay /*! * Calculate virtual address from physical address * * @param pa Physical address * * @return virtual address * * @note this assumes that addresses are 32 bits wide */ #define os_va __va /*! * Calculate physical address from virtual address * * * @param va Virtual address * * @return physical address * * @note this assumes that addresses are 32 bits wide */ #define os_pa __pa #ifdef CONFIG_PREEMPT_RT /*! * Allocate and initialize a lock, returning a lock handle. * * The lock state will be initialized to 'unlocked'. * * @return A lock handle, or NULL if an error occurred. */ inline static os_lock_t os_lock_alloc_init(void) { raw_spinlock_t *lockp; lockp = (raw_spinlock_t *) kmalloc(sizeof(raw_spinlock_t), 0); if (lockp) { _raw_spin_lock_init(lockp); } else { printk("OS: lock init failed\n"); } return lockp; } #else /*! * Allocate and initialize a lock, returning a lock handle. * * The lock state will be initialized to 'unlocked'. * * @return A lock handle, or NULL if an error occurred. */ inline static os_lock_t os_lock_alloc_init(void) { spinlock_t *lockp; lockp = (spinlock_t *) kmalloc(sizeof(spinlock_t), 0); if (lockp) { spin_lock_init(lockp); } else { printk("OS: lock init failed\n"); } return lockp; } #endif /* CONFIG_PREEMPT_RT */ /*! * Acquire a lock. * * This function should only be called from an interrupt service routine. * * @param lock_handle A handle to the lock to acquire. * * @return void */ #define os_lock(lock_handle) \ spin_lock(lock_handle) /*! * Unlock a lock. Lock must have been acquired by #os_lock(). * * @param lock_handle A handle to the lock to unlock. * * @return void */ #define os_unlock(lock_handle) \ spin_unlock(lock_handle) /*! * Acquire a lock in non-ISR context * * This function will spin until the lock is available. * * @param lock_handle A handle of the lock to acquire. * @param context Place to save the before-lock context * * @return void */ #define os_lock_save_context(lock_handle, context) \ spin_lock_irqsave(lock_handle, context) /*! * Release a lock in non-ISR context * * @param lock_handle A handle of the lock to release. * @param context Place where before-lock context was saved. * * @return void */ #define os_unlock_restore_context(lock_handle, context) \ spin_unlock_irqrestore(lock_handle, context) /*! * Deallocate a lock handle. * * @param lock_handle An #os_lock_t that has been allocated. * * @return void */ #define os_lock_deallocate(lock_handle) \ kfree(lock_handle) /*! * Determine process handle * * The process handle of the current user is returned. * * @return A handle on the current process. */ #define os_get_process_handle() \ current /*! * Send a signal to a process * * @param proc A handle to the target process. * @param sig The POSIX signal to send to that process. */ #define os_send_signal(proc, sig) \ send_sig(sig, proc, 0); /*! * Get some random bytes * * @param buf The location to store the random data. * @param count The number of bytes to store. * * @return void */ #define os_get_random_bytes(buf, count) \ get_random_bytes(buf, count) /*! * Go to sleep on an object. * * @param object The object on which to sleep * @param condition An expression to check for sleep completion. Must be * coded so that it can be referenced more than once inside * macro, i.e., no ++ or other modifying expressions. * @param atomic Non-zero if sleep must not return until condition. * * @return error code -- OK or sleep interrupted?? */ #define os_sleep(object, condition, atomic) \ ({ \ DEFINE_WAIT(_waitentry_); \ os_error_code code = OS_ERROR_OK_S; \ \ while (!(condition)) { \ prepare_to_wait(&(object##_qh), &_waitentry_, \ atomic ? 0 : TASK_INTERRUPTIBLE); \ if (!(condition)) { \ schedule(); \ } \ \ finish_wait(&(object##_qh), &_waitentry_); \ \ if (!atomic && signal_pending(current)) { \ code = OS_ERROR_FAIL_S; /* NEED SOMETHING BETTER */ \ break; \ } \ }; \ \ code; \ }) /*! * Wake up whatever is sleeping on sleep object * * @param object The object on which things might be sleeping * * @return none */ #define os_wake_sleepers(object) \ wake_up_interruptible(&(object##_qh)); /*! @} *//* dkops */ /****************************************************************************** * Function signature-generating macros *****************************************************************************/ /*! * @defgroup drsigs Driver Signatures * * These macros will define the entry point signatures for interrupt handlers; * driver initialization and shutdown; device open/close; etc. * * There are two versions of each macro for a given Driver Entry Point. The * first version is used to define a function and its implementation in the * driver.c file, e.g. #OS_DEV_INIT(). * * The second form is used whenever a forward declaration (prototype) is * needed. It has the letters @c _DCL appended to the name of the defintion * function, and takes only the first two arguments (driver_name and * function_name). These are not otherwise mentioned in this documenation. * * There is a third form used when a reference to a function is required, for * instance when passing the routine as a pointer to a function. It has the * letters @c _REF appended to it, and takes only the first two arguments * (driver_name and function_name). These functions are not otherwise * mentioned in this documentation. * * (Note that these two extra forms are required because of the * possibility/likelihood of having a 'wrapper function' which invokes the * generic function with expected arguments. An alternative would be to have a * generic function which isn't able to get at any arguments directly, but * would be equipped with macros which could get at information passed in. * * Example: * * (in a header file) * @code * OS_DEV_INIT_DCL(widget, widget_init); * @endcode * * (in an implementation file) * @code * OS_DEV_INIT(widget, widget_init) * { * os_dev_init_return(TRUE); * } * @endcode * */ /*! @addtogroup drsigs */ /*! @{ */ /*! * Define a function which will handle device initialization * * This is tne driver initialization routine. This is normally where the * part would be initialized; queues, locks, interrupts handlers defined; * long-term dynamic memory allocated for driver use; etc. * * @param function_name The name of the portable initialization function. * * @return A call to #os_dev_init_return() * */ #define OS_DEV_INIT(function_name) \ module_init(function_name); \ static int __init function_name (void) /*! Make declaration for driver init function. * @param function_name foo */ #define OS_DEV_INIT_DCL(function_name) \ static int __init function_name (void); /*! * Generate a function reference to the driver's init function. * @param function_name Name of the OS_DEV_INIT() function. * * @return A function pointer. */ #define OS_DEV_INIT_REF(function_name) \ function_name /*! * Define a function which will handle device shutdown * * This is the inverse of the #OS_DEV_INIT() routine. * * @param function_name The name of the portable driver shutdown routine. * * @return A call to #os_dev_shutdown_return() * */ #define OS_DEV_SHUTDOWN(function_name) \ module_exit(function_name); \ static void function_name(void) /*! * Generate a function reference to the driver's shutdown function. * @param function_name Name of the OS_DEV_HUSTDOWN() function. * * @return A function pointer. */ #define OS_DEV_SHUTDOWN_DCL(function_name) \ static void function_name(void); /*! * Generate a reference to driver's shutdown function * @param function_name Name of the OS_DEV_HUSTDOWN() function. */ #define OS_DEV_SHUTDOWN_REF(function_name) \ function_name /*! * Define a function which will open the device for a user. * * @param function_name The name of the driver open() function * * @return A call to #os_dev_open_return() */ #define OS_DEV_OPEN(function_name) \ static int function_name(struct inode* inode_p_, struct file* file_p_) /*! * Declare prototype for an open() function. * * @param function_name The name of the OS_DEV_OPEN() function. */ #define OS_DEV_OPEN_DCL(function_name) \ OS_DEV_OPEN(function_name); /*! * Generate a function reference to the driver's open() function. * @param function_name Name of the OS_DEV_OPEN() function. * * @return A function pointer. */ #define OS_DEV_OPEN_REF(function_name) \ function_name /*! * Define a function which will handle a user's ioctl() request * * @param function_name The name of the driver ioctl() function * * @return A call to #os_dev_ioctl_return() */ #define OS_DEV_IOCTL(function_name) \ static int function_name(struct inode* inode_p_, struct file* file_p_, \ unsigned int cmd_, unsigned long data_) /*! Boo. */ #define OS_DEV_IOCTL_DCL(function_name) \ OS_DEV_IOCTL(function_name); /*! * Generate a function reference to the driver's ioctl() function. * @param function_name Name of the OS_DEV_IOCTL() function. * * @return A function pointer. */ #define OS_DEV_IOCTL_REF(function_name) \ function_name /*! * Define a function which will handle a user's read() request * * @param function_name The name of the driver read() function * * @return A call to #os_dev_read_return() */ #define OS_DEV_READ(function_name) \ static ssize_t function_name(struct file* file_p_, char* user_buffer_, \ size_t count_bytes_, loff_t* file_position_) /*! * Declare prototype for an read() function. * * @param function_name The name of the driver read function. */ #define OS_DEV_READ_DCL(function_name) \ OS_DEV_READ(function_name); /*! * Generate a function reference to the driver's read() routine * @param function_name Name of the OS_DEV_READ() function. * * @return A function pointer. */ #define OS_DEV_READ_REF(function_name) \ function_name /*! * Define a function which will handle a user's write() request * * @param function_name The name of the driver write() function * * @return A call to #os_dev_write_return() */ #define OS_DEV_WRITE(function_name) \ static ssize_t function_name(struct file* file_p_, char* user_buffer_, \ size_t count_bytes_, loff_t* file_position_) /*! * Declare prototype for an write() function. * * @param function_name The name of the driver write function. */ #define OS_DEV_WRITE_DCL(function_name) \ OS_DEV_WRITE(function_name); /*! * Generate a function reference to the driver's write() routine * @param function_name Name of the OS_DEV_WRITE() function. * * @return A function pointer. */ #define OS_DEV_WRITE_REF(function_name) \ function_name /*! * Define a function which will close the device - opposite of OS_DEV_OPEN() * * @param function_name The name of the driver close() function * * @return A call to #os_dev_close_return() */ #define OS_DEV_CLOSE(function_name) \ static int function_name(struct inode* inode_p_, struct file* file_p_) /*! * Declare prototype for an close() function * * @param function_name The name of the driver close() function. */ #define OS_DEV_CLOSE_DCL(function_name) \ OS_DEV_CLOSE(function_name); /*! * Generate a function reference to the driver's close function. * @param function_name Name of the OS_DEV_CLOSE() function. * * @return A function pointer. */ #define OS_DEV_CLOSE_REF(function_name) \ function_name /*! * Define a function which will handle an interrupt * * No arguments are available to the generic function. It must not invoke any * OS functions which are illegal in a ISR. It gets no parameters, and must * have a call to #os_dev_isr_return() instead of any/all return statements. * * Example: * @code * OS_DEV_ISR(widget, widget_isr, WIDGET_IRQ_NUMBER) * { * os_dev_isr_return(1); * } * @endcode * * @param function_name The name of the driver ISR function * * @return A call to #os_dev_isr_return() */ #define OS_DEV_ISR(function_name) \ static irqreturn_t function_name(int N1_, void* N2_) /*! * Declare prototype for an ISR function. * * @param function_name The name of the driver ISR function. */ #define OS_DEV_ISR_DCL(function_name) \ OS_DEV_ISR(function_name); /*! * Generate a function reference to the driver's interrupt service routine * @param function_name Name of the OS_DEV_ISR() function. * * @return A function pointer. */ #define OS_DEV_ISR_REF(function_name) \ function_name /*! * Define a function which will operate as a background task / bottom half. * * Tasklet stuff isn't strictly limited to 'Device drivers', but leave it * this namespace anyway. * * @param function_name The name of this background task function * * @return A call to #os_dev_task_return() */ #define OS_DEV_TASK(function_name) \ static void function_name(unsigned long data_) /*! * Declare prototype for a background task / bottom half function * * @param function_name The name of this background task function */ #define OS_DEV_TASK_DCL(function_name) \ OS_DEV_TASK(function_name); \ DECLARE_TASKLET(function_name ## let, function_name, 0); /*! * Generate a reference to an #OS_DEV_TASK() function * * @param function_name The name of the task being referenced. */ #define OS_DEV_TASK_REF(function_name) \ (function_name ## let) /*! @} *//* drsigs */ /***************************************************************************** * Functions/Macros for returning values from Driver Signature routines *****************************************************************************/ /*! * Return from the #OS_DEV_INIT() function * * @param code An error code to report success or failure. * */ #define os_dev_init_return(code) \ return code /*! * Return from the #OS_DEV_SHUTDOWN() function * * @param code An error code to report success or failure. * */ #define os_dev_shutdown_return(code) \ return /*! * Return from the #OS_DEV_ISR() function * * The function should verify that it really was supposed to be called, * and that its device needed attention, in order to properly set the * return code. * * @param code non-zero if interrupt handled, zero otherwise. * */ #define os_dev_isr_return(code) \ do { \ /* Unused warnings */ \ (void)N1_; \ (void)N2_; \ \ return IRQ_RETVAL(code); \ } while (0) /*! * Return from the #OS_DEV_OPEN() function * * @param code An error code to report success or failure. * */ #define os_dev_open_return(code) \ do { \ int retcode = code; \ \ /* get rid of 'unused parameter' warnings */ \ (void)inode_p_; \ (void)file_p_; \ \ return retcode; \ } while (0) /*! * Return from the #OS_DEV_IOCTL() function * * @param code An error code to report success or failure. * */ #define os_dev_ioctl_return(code) \ do { \ int retcode = code; \ \ /* get rid of 'unused parameter' warnings */ \ (void)inode_p_; \ (void)file_p_; \ (void)cmd_; \ (void)data_; \ \ return retcode; \ } while (0) /*! * Return from the #OS_DEV_READ() function * * @param code Number of bytes read, or an error code to report failure. * */ #define os_dev_read_return(code) \ do { \ ssize_t retcode = code; \ \ /* get rid of 'unused parameter' warnings */ \ (void)file_p_; \ (void)user_buffer_; \ (void)count_bytes_; \ (void)file_position_; \ \ return retcode; \ } while (0) /*! * Return from the #OS_DEV_WRITE() function * * @param code Number of bytes written, or an error code to report failure. * */ #define os_dev_write_return(code) \ do { \ ssize_t retcode = code; \ \ /* get rid of 'unused parameter' warnings */ \ (void)file_p_; \ (void)user_buffer_; \ (void)count_bytes_; \ (void)file_position_; \ \ return retcode; \ } while (0) /*! * Return from the #OS_DEV_CLOSE() function * * @param code An error code to report success or failure. * */ #define os_dev_close_return(code) \ do { \ ssize_t retcode = code; \ \ /* get rid of 'unused parameter' warnings */ \ (void)inode_p_; \ (void)file_p_; \ \ return retcode; \ } while (0) /*! * Start the #OS_DEV_TASK() function * * In some implementations, this could be turned into a label for * the os_dev_task_return() call. * * @return none */ #define os_dev_task_begin() /*! * Return from the #OS_DEV_TASK() function * * In some implementations, this could be turned into a sleep followed * by a jump back to the os_dev_task_begin() call. * * @param code An error code to report success or failure. * */ #define os_dev_task_return(code) \ do { \ /* Unused warnings */ \ (void)data_; \ \ return; \ } while (0) /***************************************************************************** * Functions/Macros for accessing arguments from Driver Signature routines *****************************************************************************/ /*! @defgroup drsigargs Functions for Getting Arguments in Signature functions * */ /* @addtogroup @drsigargs */ /*! @{ */ /*! * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and * #OS_DEV_WRITE() routines to check whether user is requesting read * (permission) */ #define os_dev_is_flag_read() \ (file_p_->f_mode & FMODE_READ) /*! * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and * #OS_DEV_WRITE() routines to check whether user is requesting write * (permission) */ #define os_dev_is_flag_write() \ (file_p_->f_mode & FMODE_WRITE) /*! * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and * #OS_DEV_WRITE() routines to check whether user is requesting non-blocking * I/O. */ #define os_dev_is_flag_nonblock() \ (file_p_->f_flags & (O_NONBLOCK | O_NDELAY)) /*! * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine major device being * accessed. */ #define os_dev_get_major() \ (imajor(inode_p_)) /*! * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine minor device being * accessed. */ #define os_dev_get_minor() \ (iminor(inode_p_)) /*! * Used in #OS_DEV_IOCTL() to determine which operation the user wants * performed. * * @return Value of the operation. */ #define os_dev_get_ioctl_op() \ (cmd_) /*! * Used in #OS_DEV_IOCTL() to return the associated argument for the desired * operation. * * @return A value which can be cast to a struct pointer or used as * int/long. */ #define os_dev_get_ioctl_arg() \ (data_) /*! * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to access the requested * byte count. * * @return (unsigned) a count of bytes */ #define os_dev_get_count() \ ((unsigned)count_bytes_) /*! * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to return the pointer * byte count. * * @return char* pointer to user buffer */ #define os_dev_get_user_buffer() \ ((void*)user_buffer_) /*! * Used in OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines to * get the POSIX flags field for the associated open file). * * @return The flags associated with the file. */ #define os_dev_get_file_flags() \ (file_p_->f_flags) /*! * Set the driver's private structure associated with this file/open. * * Generally used during #OS_DEV_OPEN(). See #os_dev_get_user_private(). * * @param struct_p The driver data structure to associate with this user. */ #define os_dev_set_user_private(struct_p) \ file_p_->private_data = (void*)(struct_p) /*! * Get the driver's private structure associated with this file. * * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(), * #OS_DEV_IOCTL(), and #OS_DEV_CLOSE(). See #os_dev_set_user_private(). * * @return The driver data structure to associate with this user. */ #define os_dev_get_user_private() \ ((void*)file_p_->private_data) /*! @} *//* drsigargs */ /*! * @defgroup cacheops Cache Operations * * These functions are for synchronizing processor cache with RAM. */ /*! @addtogroup cacheops */ /*! @{ */ /*! * Flush and invalidate all cache lines. */ #if 0 #define os_flush_cache_all() \ flush_cache_all() #else /* Call ARM fn directly, in case L2cache=on3 not set */ #define os_flush_cache_all() \ v6_flush_kern_cache_all_L2() /*! * ARM-routine to flush all cache. Defined here, because it exists in no * easy-access header file. ARM-11 with L210 cache only! */ extern void v6_flush_kern_cache_all_L2(void); #endif /* * These macros are using part of the Linux DMA API. They rely on the * map function to do nothing more than the equivalent clean/inv/flush * operation at the time of the mapping, and do nothing at an unmapping * call, which the Sahara driver code will never invoke. */ /*! * Clean a range of addresses from the cache. That is, write updates back * to (RAM, next layer). * * @param start Starting virtual address * @param len Number of bytes to flush * * @return void */ #define os_cache_clean_range(start,len) \ { \ dmac_clean_range(start, (void *)((unsigned long)(start) + len)); \ outer_clean_range(__pa(start), __pa((unsigned long)(start) + len)); \ } /*! * Invalidate a range of addresses in the cache * * @param start Starting virtual address * @param len Number of bytes to flush * * @return void */ #define os_cache_inv_range(start,len) \ { \ dmac_inv_range(start, (void *)((unsigned long)(start) + len)); \ outer_inv_range(__pa(start), __pa((unsigned long)(start) + len)); \ } /*! * Flush a range of addresses from the cache. That is, perform clean * and invalidate * * @param start Starting virtual address * @param len Number of bytes to flush * * @return void */ #define os_cache_flush_range(start,len) \ { \ dmac_flush_range(start, (void *)((unsigned long)(start) + len)); \ outer_flush_range(__pa(start), __pa((unsigned long)(start) + len)); \ } /*! @} *//* cacheops */ #endif /* LINUX_PORT_H */