file: driver.c

/* * This file contains the main slh device driver routines. It has * been tested with the Linux 2.2.5 kernel on the x86 only. */ # define __KERNEL__ #endif # define MODULE #endif #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> /* printk() */ #include <linux/malloc.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/proc_fs.h> #include <linux/fcntl.h> /* O_ACCMODE */ #include <asm/uaccess.h> #include "slh.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,5) #error "Driver not tested with this version of the kernel" #endif /* prevent symbols from this file from * being accessed by any part of the * kernel outside of this driver. */ EXPORT_NO_SYMBOLS; /* the slh device driver number */ int slh_major = SLH_MAJOR; /* the slh device driver owner (pid) */ int slh_owner = 0; /* size used in read() and ioctl() calls */ unsigned long size; int slh_open( struct inode *inode, struct file *filp ) { /* the open() system call */ /* check if the slh board is already owned */ if ( slh_owner == 0 ) { /* not owned: save pid of new owner */ slh_owner = current->pid; } else { /* owned: only read operations are allowed */ switch ( filp->f_flags & O_ACCMODE ) { case O_RDWR: case O_WRONLY: return ( -EBUSY ); } } /* increment kernel module use count */ MOD_INC_USE_COUNT; return ( 0 ); } int slh_release( struct inode *inode, struct file *filp ) { /* the close() system call */ /* check if the owner is closing */ if ( slh_owner == current->pid ) { /* free ownership */ slh_owner = 0; } /* increment kernel module use count */ MOD_DEC_USE_COUNT; return ( 0 ); } ssize_t slh_read( struct file *file, char *filp, size_t count, loff_t *buf ) { /* the read() system call */ int retval; char *chan_buf; if ( count < NUM_DMX_CHANS ) { size = count; } else { size = NUM_DMX_CHANS; } /* allocate space to hold firmware file */ chan_buf = kmalloc( NUM_DMX_CHANS, GFP_KERNEL ); if ( chan_buf == NULL ) { return ( -ENOMEM ); } /* copy the channel values to kernel buffer */ slh_channel_read( chan_buf ); /* copy channel values to user space */ retval = copy_to_user( filp, chan_buf, size ); if ( retval != 0 ) { return ( -EFAULT ); } /* free kernel buffer */ kfree( chan_buf ); /* return the buffer size */ return ( size ); } ssize_t slh_write( struct file *file, const char *filp, size_t count, loff_t *buf ) { /* the write() system call */ /* write() is not used */ return ( -EINVAL ); } int slh_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { /* the ioctl() system call */ int i; int retval; char *pump_buf; DMX_CHANNELS dmx_channels; /* check if the caller is the owner */ if ( slh_owner != current->pid ) { /* ioctl() not allowed */ return ( -EBUSY ); } switch ( cmd ) { case SLH_IOCRESET: /* reset board */ break; case SLH_IOCPUMP: /* pump board firmware */ /* get firmware file size from user */ retval = copy_from_user( (char *)&size, (char *)arg, sizeof(size) ); if ( retval != 0 ) { return ( -EFAULT ); } /* allocate space to hold firmware file */ pump_buf = kmalloc( size, GFP_KERNEL ); if ( pump_buf == NULL ) { return ( -ENOMEM ); } /* copy firmware file to kernel memory */ retval = copy_from_user( pump_buf, (char *)arg+sizeof(size), size ); if ( retval != 0 ) { return ( -EFAULT ); } /* pump board firmware and parameters */ retval = slh_board_pump( pump_buf, size ); if ( retval < 0 ) { return ( retval ); } /* load channel data to SoundLight board */ retval = slh_channel_load(); if ( retval < 0 ) { return ( retval ); } /* free kernel buffer */ kfree( pump_buf ); break; case SLH_IOCPARAMS: /* set board parameters */ break; case SLH_IOCREAD: /* read board DMX-512 channel values */ break; case SLH_IOCSET: /* set board DMX-512 channel values */ /* get the number of channels to be changed */ retval = copy_from_user( &dmx_channels.num_chan, (char *)arg, sizeof(dmx_channels.num_chan) ); if ( retval != 0 ) { return ( -EFAULT ); } /* validate number of channels */ if ( dmx_channels.num_chan > NUM_DMX_CHANS ) { dmx_channels.num_chan = NUM_DMX_CHANS; } /* copy channel data to kernel memory */ retval = copy_from_user( &dmx_channels, (char *)arg, CHAN_STRUCT_SZ(dmx_channels.num_chan) ); if ( retval != 0 ) { return ( -EFAULT ); } /* process each channel changed by the user */ for ( i = 0; i < dmx_channels.num_chan; i++ ) { slh_channel_dim( dmx_channels.chan_value[i].channel, dmx_channels.chan_value[i].new_value, dmx_channels.chan_value[i].transition ); } break; default: break; } return 0; } /* end slh_ioctl() */ struct file_operations slh_fops = { NULL, /* lseek */ slh_read, /* read */ slh_write, /* write */ NULL, /* readdir */ NULL, /* poll */ slh_ioctl, /* ioctl */ NULL, /* mmap */ slh_open, /* open */ NULL, /* flush */ slh_release, /* close */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */ NULL, /* lock */ }; int slh_read_procmem( char *buf, char **start, off_t offset, int len, int unused ) { /* generate output when reading /proc/slh */ register int i; register int length; unsigned char buffer[NUM_DMX_CHANS]; length = sprintf( buf, "SoundLight Device Driver:\n\n" ); length += sprintf( buf+length, "\tOwner pid = %d\n\n", slh_owner ); slh_channel_read( buffer ); length += sprintf( buf+length, "\tNon-zero DMX-512 channels:\n" ); /* print non-zero channels */ for ( i = 0; i < 512; i++ ) { if ( buffer[i] == 0 ) { continue; } length += sprintf( buf+length, "\t\tchan(%d)=%d\n", i+1, (int)buffer[i] ); } return length; } /* data structure used to create the /proc/slh special file */ struct proc_dir_entry slh_proc_entry = { 0, /* low_ino: the inode -- dynamic */ 3, "slh", /* len of name and name */ S_IFREG | S_IRUGO, /* mode */ 1, 0, 0, /* nlinks, owner, group */ 0, NULL, /* size - unused; operations -- use default */ &slh_read_procmem, /* function used to read data */ }; int init_module( void ) { /* initialize the slh device driver kernel module */ int retval; int proc_register(); /* register major, and accept a dynamic number */ retval = register_chrdev( slh_major, "slh", &slh_fops ); if ( retval < 0 ) { return ( retval ); } /* save the major number dynamically assigned this driver */ if ( slh_major == 0 ) { slh_major = retval; } /* initialize "board" software module */ retval = slh_board_mod_init(); if ( retval < 0 ) { return ( retval ); } /* initialize "channel" software module */ retval = slh_channel_mod_init(); if ( retval < 0 ) { return ( retval ); } /* probe hardware to find board */ retval = slh_board_find(); if ( retval < 0 ) { return ( retval ); } /* this is the last line in init_module */ proc_register( &proc_root, &slh_proc_entry ); /* return success */ return ( 0 ); } void cleanup_module( void ) { /* cleanup the slh device driver kernel module */ /* cleanup "channel" software module */ slh_channel_mod_cleanup(); /* cleanup "board" software module */ slh_board_mod_cleanup(); /* release major number */ unregister_chrdev( slh_major, "slh" ); /* release /proc/slh directory entry */ proc_unregister( &proc_root, slh_proc_entry.low_ino ); }


Back to Source File Index


C++ to HTML Conversion by ctoohtml