/****************************************************************************** *.Copyright (c) 1999-2006 DST Technologies Inc. All rights reserved. * * MODULE: * dst_module.c * * DESCRIPTION: * Linux kernel loadable module for low level DST hardware access ******************************************************************************/ #ifndef __KERNEL__ #define __KERNEL__ #endif #define CONFIG_DEVFS_FS 1 #ifndef MODULE #define MODULE #endif /*======================== * Includes *=======================*/ #include "lld_os.h" #include "lld_local.h" #include "dst_kernel.h" #include "dstmodule.h" #if KERNEL_2_4_0 //#include #endif /*======================== * Local defines *=======================*/ #define DEVICE_NAME DST_DEV_NAME #define DST_DEV_PERM 0666 #define KSEG0_START 0x80000000 #define GET_DS_U8_0(x) (unsigned int)((x>>24)&0xFF) #define GET_DS_U8_1(x) (unsigned int)((x>>16)&0xFF) #define GET_DS_U8_2(x) (unsigned int)((x>>8)&0xFF) #define GET_DS_U8_3(x) (unsigned int)((x)&0xFF) /*======================== * Local Prototypes *=======================*/ static DS_U32 lld_dlevel (DS_U32 level); static int device_open (struct inode *inode, struct file *file); static int device_close (struct inode *inode, struct file *file); static int device_ioctl (struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param); static __s64 dst_do_gettimeofday(void); /*======================== * Global variables *=======================*/ /*======================== * Local variables *=======================*/ static int lld_debug = 0; MODULE_AUTHOR ("DS Technology Inc."); MODULE_DESCRIPTION ("DS Kernel Module for Linux Platform"); //MODULE_PARM (debug, "i"); DST_DEVICE_INFO dst_device[MAX_DEVICES]; DS_U32 current_filp=KERNEL_PID; struct file_operations dst_fops = { #if KERNEL_2_4_0 owner: THIS_MODULE, #endif ioctl: device_ioctl, mmap: NULL, open: device_open, release: device_close, }; static int dsthwacc_major_num = 0; /****************************************************************************** * Interrupt setup for kernel * * * *****************************************************************************^^*/ static int DevOpenForKernel (DST_DEVICE_INFO *pdevice) { struct file KFileH; KFileH.private_data = pdevice; return (0); } /****************************************************************************** * Interrupt cleanup for kernel * * * *****************************************************************************^^*/ static int DevCloseForKernel (DST_DEVICE_INFO *pdevice) { struct file KFileH; KFileH.private_data = pdevice; return (0); } /****************************************************************************** * DS_U32 lld_GetVersion (void); * * Purpose: Return the version for this module * * Inputs: None * * Outputs: Version number * *****************************************************************************^^*/ DS_U32 lld_GetVersion (void) { return (DSTMOD_VERSION_NUMBER); } static int lld_get_info (char *page, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; int limit = count - 80; /* don't print more than this */ static int ref_count=0; unsigned int i; DST_DEVICE_INFO *pdevice = (DST_DEVICE_INFO *) data; ref_count++; for (i = 0; i < MAX_DEVICES; i++) { if ( limit > len ) len += sprintf(page+len, "dstmod[%d]: refcount = %i\n", i, ref_count); } *eof = 1; pdevice = pdevice; return (len); } /*^^*************************************************************************** * static DS_U32 lld_dlevel (DS_U32 level) * * Description: * Set debug level in LLD and HKD * * Entry : level = new debug level * * Return: Old debug level * **************************************************************************^^*/ static DS_U32 lld_dlevel (DS_U32 level) { DS_U32 old_level; old_level = lld_debug; lld_debug = level; return old_level; } /****************************************************************************** * init_module (void) * * Purpose: Initializes the module, registers the device * Inputs: NONE * Outputs: int - 0 on success, non zero on failure ******************************************************************************/ int init_module (void) { printk( DEVICE_NAME ": " DSTMOD_VERSION_STRING ", Copyright (c) 2005-2006, Digital Stream Technology.\n"); printk( DEVICE_NAME ": generated on " __DATE__ " at " __TIME__ " \n"); memset (dst_device, 0, sizeof (dst_device)); #if (KERNEL_2_4_0) && defined (CONFIG_DEVFS_FS) dsthwacc_major_num = devfs_register_chrdev (DST_MAJOR_DEV_NUM, DEVICE_NAME, &dst_fops); #else dsthwacc_major_num = register_chrdev (DST_MAJOR_DEV_NUM, DEVICE_NAME, &dst_fops); #endif if (dsthwacc_major_num < 0) { printk(DEVICE_NAME ": Error registering device: %d\n", dsthwacc_major_num); return (dsthwacc_major_num); } printk( KERN_DEBUG DEVICE_NAME ": Major device number is %d.\n", dsthwacc_major_num); #if (KERNEL_2_4_0) && defined (CONFIG_DEVFS_FS) devfs_register_series (NULL, DEVICE_NAME"%u", MAX_DEVICES, DEVFS_FL_DEFAULT, dsthwacc_major_num, 0, S_IFCHR | S_IRUSR | S_IWUSR | DST_DEV_PERM, &dst_fops, NULL); #endif /*============================ * Init sem and event libs *===========================*/ SemInit(); EventInit(); DevOpenForKernel(&dst_device[0]); create_proc_read_entry (DST_DEV_NAME, 0, NULL, lld_get_info, (void *) dst_device); return (0); } /****************************************************************************** * cleanup_module (void) * * Purpose: unregisters the device, frees memory and I/O ranges * used by the device. * Inputs: NONE * Outputs: NONE ******************************************************************************/ void cleanup_module (void) { int retval; #if (KERNEL_2_4_0) && defined (CONFIG_DEVFS_FS) int i; char dst_dev[16]; devfs_handle_t de; #endif printk( DEVICE_NAME ": Shutting down module.\n"); DevCloseForKernel(&dst_device[0]); #if (KERNEL_2_4_0) && defined (CONFIG_DEVFS_FS) retval = devfs_unregister_chrdev (dsthwacc_major_num, DEVICE_NAME); #else retval = unregister_chrdev (dsthwacc_major_num, DEVICE_NAME); #endif if (retval < 0) { printk( DEVICE_NAME ": Error in unregister_chrdev: %d\n", retval); } #if (KERNEL_2_4_0) && defined (CONFIG_DEVFS_FS) for (i = 0; i <= MAX_DEVICES; i++) { sprintf(dst_dev, DEVICE_NAME"%u", i); de = devfs_find_handle (NULL, dst_dev, 0, 0, DEVFS_SPECIAL_CHR, 0); devfs_unregister(de); } #endif SemCleanup(); EventCleanup(); remove_proc_entry (DST_DEV_NAME, NULL); memset (dst_device, 0, sizeof (dst_device)); } /****************************************************************************** *.open_device (struct inode *inode, struct file *filp) * Function: Opens the device, increments the module count * Inputs: struct inode *inode * struct file *filp * Outputs: int - returns 0 on success, the defined error otherwise ******************************************************************************/ static int open_device (struct inode *inode, struct file *filp) { unsigned int device_minor; DST_DEVICE_INFO *pdevice; /* Ensure that the requested minor device number exists and is valid */ device_minor = MINOR (inode->i_rdev); pdevice = &dst_device[device_minor]; printk( KERN_DEBUG DEVICE_NAME ": %s open for pid %d\n", pdevice->name, mCurrentTid); filp->private_data = pdevice; pdevice->refcount += 1; /* Increment counter */ //MOD_INC_USE_COUNT; return (0); } /****************************************************************************** *.close_device (struct inode *inode, struct file *filp) * Function: Closes the device, decrements the module count * Inputs: struct inode *inode * struct file *filp * Outputs: NONE ******************************************************************************/ static int close_device (struct inode *inode, struct file *filp) { DST_DEVICE_INFO *pdevice = filp->private_data; printk( KERN_DEBUG DEVICE_NAME ": %s close for pid %d - refcount = %d\n", pdevice->name, mCurrentTid, (int)(pdevice->refcount)); SemProcessExit(); EventProcessExit(); /*=============================== * decrement reference counter *==============================*/ if (pdevice->refcount > 0) { pdevice->refcount -= 1; } if (pdevice->refcount == 0) { filp->private_data = NULL; } //MOD_DEC_USE_COUNT; return (0); } /****************************************************************************** *.device_open (struct inode *inode, struct file *filp) * Function: Opens the device, increments the module count * Inputs: struct inode *inode * struct file *filp * Outputs: int - returns 0 on success, the defined error otherwise ******************************************************************************/ static int device_open (struct inode *inode, struct file *filp) { int i; DS_U32 org_filp; org_filp = current_filp; current_filp = (DS_U32)filp; i = open_device (inode, filp); current_filp = org_filp; return (i); } /****************************************************************************** *.device_close (struct inode *inode, struct file *filp); * Function: Closes the device, decrements the module count * Inputs: struct inode *inode * struct file *filp * Outputs: NONE ******************************************************************************/ static int device_close (struct inode *inode, struct file *filp) { int i; DS_U32 org_filp; org_filp = current_filp; current_filp = (DS_U32)filp; i = close_device (inode, filp); current_filp = org_filp; return (i); } /******************************************************************************* *.ioctl_device (struct inode *inode, struct file *filp, * unsigned int ioctl_num, unsigned long ioctl_param) * Function: Entry point for the programs calling the specific * IOCTLs. * Inputs: struct inode *inode * struct file *filp * unsigned int ioctl_num - the number of the IOCTL being called * unsigned int ioctl_param - the 'extra' data being sent * Outputs: int ******************************************************************************/ //extern char LoaderVer[]; static int ioctl_device (struct inode *inode, struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) { DS_U32 i; DST_DEVICE_INFO *pdevice = filp->private_data; __s64 time_stamp; OBJ_OPER ObjOper; DS_U32 flags; DS_U8 *pbAddr; DS_U16 *pwAddr; DS_U32 *pdwAddr; DS_U8 bTemp = 0; switch (ioctl_num) { case DSTHWIOC_SEM_OP: copy_from_user (&ObjOper, (char *) ioctl_param, sizeof(OBJ_OPER)); i = SemOperation (&ObjOper); copy_to_user ((char *)ioctl_param, &ObjOper, sizeof(OBJ_OPER)); return (i); break; case DSTHWIOC_EVENT_OP: copy_from_user (&ObjOper, (char *) ioctl_param, sizeof(OBJ_OPER)); i = EventOperation (&ObjOper); copy_to_user ((char *)ioctl_param, &ObjOper, sizeof(OBJ_OPER)); return (i); break; case DSTHWIOC_GET_VERSION: copy_from_user (&i, (char *)ioctl_param, sizeof(DS_U32)); i = lld_GetVersion(); copy_to_user ((char *)ioctl_param, &i, sizeof (DS_U32)); break; case DSTHWIOC_GET_LOADER_VER: //printk("\nLoader Version %s\n",LoaderVer); //i = *(int *)LoaderVer; i=0; i = ((GET_DS_U8_0(i) - 48) << 24 ) | ((GET_DS_U8_1(i) - 48) << 16 ) | ((GET_DS_U8_2(i) - 48) << 8 ) | ((GET_DS_U8_3(i) - 48) ); return i; break; case DSTHWIOC_TIME_STAMP: time_stamp = dst_do_gettimeofday(); copy_to_user((char *)ioctl_param, &time_stamp, sizeof(__s64)); return(-1); case DSTHWIOC_SET_DBG_LEVEL: { DST_SET_DBG_LEVEL_MESSAGE_PARAMS params; copy_from_user ((char *) ¶ms, (char *) ioctl_param, sizeof (params)); lld_dlevel (params.level); break; } case DSTHWIOC_IRQ_DISABLE: flags = lld_os_disable_ints(); // disable interrupts copy_to_user ((char*)ioctl_param, (char*)&flags, sizeof(DS_U32)); break; case DSTHWIOC_IRQ_ENABLE: lld_os_enable_ints(); // enable interrupts break; case DSTHWIOC_IRQ_RESTORE: copy_from_user ((char*)&flags, (char*)ioctl_param, sizeof(DS_U32)); lld_os_restore_ints(flags); // restore interrupt status break; case DSTHWIOC_ATOMIC_REG_ACCESS: copy_from_user (&ObjOper, (char *) ioctl_param, sizeof(OBJ_OPER)); if ( ObjOper.Opcode == OBJ_OPCODE_READ ) { switch( ObjOper.Prm2 ) { case 1: if ( ObjOper.Prm1 >= 0xB5021300 && ObjOper.Prm1 <= 0xB50213FF) { bTemp = *((DS_U8 *)0xB50213DF); *((DS_U8 *)0xB50213DF) = 0; } pbAddr = (DS_U8 *)ObjOper.Prm1; ObjOper.Prm2 = *pbAddr; if ( ObjOper.Prm1 >= 0xB5021300 && ObjOper.Prm1 <= 0xB50213FF) { *((DS_U8 *)0xB50213DF) |= bTemp & 1; } break; case 2: pwAddr = (DS_U16 *)ObjOper.Prm1; ObjOper.Prm2 = *pwAddr; break; case 4: default: pdwAddr = (DS_U32 *)ObjOper.Prm1; ObjOper.Prm2 = *pdwAddr; } } else { switch( ObjOper.RetCode ) { case 1: if ( ObjOper.Prm1 >= 0xB5021300 && ObjOper.Prm1 <= 0xB50213FF) { bTemp = *((DS_U8 *)0xB50213DF); *((DS_U8 *)0xB50213DF) = 0; } pbAddr = (DS_U8 *)ObjOper.Prm1; *pbAddr = ObjOper.Prm2; if ( ObjOper.Prm1 >= 0xB5021300 && ObjOper.Prm1 <= 0xB50213FF) { *((DS_U8 *)0xB50213DF) |= bTemp & 1; } break; case 2: pwAddr = (DS_U16 *)ObjOper.Prm1; *pwAddr = ObjOper.Prm2; break; case 4: default: pdwAddr = (DS_U32 *)ObjOper.Prm1; *pdwAddr = ObjOper.Prm2; } } ObjOper.RetCode = OBJ_OK; copy_to_user ((char *)ioctl_param, &ObjOper, sizeof(OBJ_OPER)); return (0); break; default: break; } pdevice = pdevice; return (0); } /****************************************************************************** *.device_ioctl (struct inode *inode, struct file *filp, * unsigned int ioctl_num, unsigned long ioctl_param) * Function: Entry point for the programs calling the specific * IOCTLs. * Inputs: struct inode *inode * struct file *filp * unsigned int ioctl_num - the number of the IOCTL being called * unsigned int ioctl_param - the 'extra' data being sent * Outputs: int ******************************************************************************/ static int device_ioctl (struct inode *inode, struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param) { int i; DS_U32 org_filp; org_filp = current_filp; current_filp = (DS_U32)filp; i = ioctl_device (inode, filp, ioctl_num, ioctl_param); current_filp = org_filp; return (i); } /****************************************************************************** *.dst_do_gettimeofday() * Function: Get the time of day * Inputs: NONE * Outputs: __s64 ******************************************************************************/ static __s64 dst_do_gettimeofday(void) { struct timeval t; __s64 stamp; do_gettimeofday(&t); stamp = (__s64)t.tv_sec * 1000000 + t.tv_usec; stamp *= 1000; return stamp; }