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 );
}
C++ to HTML Conversion by ctoohtml