07070100031043000081a4000000000000000000000001388dad1d00000429000000030000000600000000000000000000000900000000Makefile# This makefile is used for building the Linux device driver for the # SoundLight 1512B DMX-512 ISA card. It also makes a driver demo # program (slh_demo). # Comment/uncomment the following line to enable/disable debugging DEBUG = y # Change it here or specify it on the "make" commandline INCLUDEDIR = /usr/include ifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSLH_DEBUG # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif CFLAGS = -D__KERNEL__ -DMODULE -Wall $(DEBFLAGS) CFLAGS += -I$(INCLUDEDIR) # Extract version number from headers. VER = $(shell awk -F\" '/REL/ {print $$2}' $(INCLUDEDIR)/linux/version.h) TARGET = slh OBJS = driver.o channel.o board.o all: .depend $(TARGET).o slh_demo $(TARGET).o: $(OBJS) $(LD) -r $^ -o $@ slh_demo: slh_demo.o install: mkdir -p /lib/modules/$(VER)/misc /lib/modules/misc install -c $(TARGET).o /lib/modules/$(VER)/misc install -c $(TARGET).o /lib/modules/misc clean: rm -f *.o *~ core .depend depend .depend dep: $(CC) $(CFLAGS) -M *.c > $@ ifeq (.depend,$(wildcard .depend)) include .depend endif 07070100031044000081a4000000000000000000000001388dad1d00000cb3000000030000000600000000000000000000000700000000READMEThe SoundLight Linux device driver supports the following operations: open()/close(): These operations allow an application to initiate/terminate the use of a SoundLight DMX-512 board. Any process can open the /dev/slh device read only (O_RDONLY) and read the device to obtain the current DMX-512 channel values. The first process to open the device read/write (O_RDWR) is considered the device owner and can set and retrieve DMX-512 channel values. Only the device owner can set DMX-512 channel values. read(): The read() system call will return 512 bytes of binary data corresponding to the current 512 DMX-512 channel values. write(): The write() system call has no meaning for the /dev/slh device and will return EINVAL if called. ioctl( SLH_IOCPUMP ): The SLH_IOCPUMP ioctl() operation is used to pump the SoundLight board with firmware. The application must read the firmware from a file, place it in a buffer, and pass the buffer address to ioctl(). When the board is pumped, the board parameters are loaded, the DMX-512 channels initialized to zero, and the board started. At this point the driver will instruct the board to transmit DMX-512 channel data every 50ms. ioctl( SLH_IOCSET ): The SLH_IOCSET ioctl() operation is used to change the DMX-512 channel values. Any number of channels (1 - 512) can be changed in one operation. For each channel to be changed, both the new channel value and transition time are specified. The transition time is the number of milliseconds to transition (or dim) a channel from the current value to the new value. ************************************************************************ A description of each file associated with the SoundLight Linux device driver is given below: Makefile: This is the makefile that is used to make the kernel module "slh" and the "slh_demo" program. board.c: This file contains the "board" software module. It contains constant definitions and C functions that interface directly with the SoundLight boards. channel.c This file contains the "channel" software module. It contains the data structures and C functions that maintain and modify the DMX-512 channel values. This module uses a repetitive timer to update the DMX-512 channels every 50ms. This module also performs dimming operations. driver.c: This file contains the main device driver C functions. slh.h This file contains miscellaneous definitions used in the slh device driver. slh_demo.c This file contains a demo program that demonstrates how to use the device driver to initialize and use a SoundLight DMX-512 board in a Linux system. load: The "load" shell script is used to load the SoundLight device driver module into the Linux kernel and create the /dev/slh device file. The kernel dynamically assigns a major number to the driver and can be determined by looking at /proc/devices. The file /proc/slh is also created and can be used to determine which process owns the driver when it is in use and which DMX-512 channels are non-zero. unload: The "unload" shell script is used to unload the SoundLight device driver module from the Linux kernel and remove /dev/slh and /proc/slh. The major number dynamically assigned to the slh driver will also be freed. 070701000310560000816d000000000000000000000001388dae5300000227000000030000000600000000000000000000000d00000000SLHDMX16.BIN}2222  22˜222222222uuu`uuœuuuueuu:0u:123458;D:>ABCuuxyD‘uC0ґ’u1Ғ542™ ;`-;0™03Œ8`]0™03Œ8`uǪuUC"PC-1512B/16 (C) D. Hoffmann & SLH 1994/95 ҜA?B@ t22˜ 80  C xy2œ2 2?p@?2œ207070100031046000081a4000000000000000000000001388dad1d00001ba0000000030000000600000000000000000000000800000000board.c/* * This file contains the "board" software module. It contains constant * definitions and C functions that interface directly with the * SoundLight boards. */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #define __NO_VERSION__ #include #include #include #include #include #include #include /* local definitions */ #include "slh.h" /* prevent symbols from this file from * being accessed by any part of the * kernel outside of this driver. */ EXPORT_NO_SYMBOLS; /* macros to get high and low byte of address */ #define ADDR_HI( addr ) ( ((addr) >> 8) & 0xff ) #define ADDR_LO( addr ) ( (addr) & 0xff ) /* board RAM size */ #define SLH_RAM_SIZE 0xa00 /* first possible board port */ #define SLH_FIRST_PORT 0x100 /* board address for pumping */ #define SLH_PUMP_ADDR 0x000 /* board address for parameters */ #define SLH_PARMS_ADDR 0x3f0 /* board address for type */ #define SLH_BRD_TYPE_ADDR 0x3f9 /* board address of DMX send channels */ #define SLH_SND_CHAN_ADDR 0x400 /* board address of DMX receive channels */ #define SLH_RCV_CHAN_ADDR 0x800 /* test byte for board probing */ #define SLH_TEST_BYTE 0xaa /* board type for 1512C */ #define SLH_TYPE_1512C 0x04 /* board type for 1512C */ #define SLH_TYPE_1512CC 0x05 /* board type for 1512A or 1512B/LC */ #define SLH_TYPE_1512ABLC 0x80 /* board type for 1512B */ #define SLH_TYPE_1512B 0x01 /* board port number */ static unsigned long slh_board_port; /* board port number */ static unsigned char slh_board_parms[9]; int slh_board_mod_init() { /* initialize the "board" software module * (called when the slh driver is loaded). */ slh_board_port = 0; slh_board_parms[0] = 60; /* start sync duration */ slh_board_parms[1] = 0; /* start byte */ slh_board_parms[2] = 0; /* end idle time */ slh_board_parms[3] = ADDR_LO(512); /* number of chans (lo byte) */ slh_board_parms[4] = ADDR_HI(512); /* number of chans (hi byte) */ slh_board_parms[5] = 0; /* send repeat */ slh_board_parms[6] = 0; /* inter-digit time */ slh_board_parms[7] = 0; /* not used */ slh_board_parms[8] = 0; /* card mode (read/write) */ return ( 0 ); } void slh_board_mod_cleanup() { /* cleanup the "board" software module * (called when the slh driver is unloaded). */ /* stop the board (if running) */ slh_board_stop(); /* release the ISA ports */ release_region( slh_board_port, 4 ); slh_board_port = 0; } int slh_board_find() { /* find the Soundlight boards (if any) in the system */ register int i; unsigned char data; slh_board_port = 0; for ( i = 0; i < 4; i++ ) { unsigned port = SLH_FIRST_PORT + (0x20 * i); /* check if ports are registered to other apps */ if ( check_region(port, 4) < 0 ) { continue; } /* write the test byte to the port */ outb( (unsigned char)0, port ); outb( (unsigned char)0, port + 1 ); outb( (unsigned char)SLH_TEST_BYTE, port + 2 ); /* read the test byte back from the port */ outb( (unsigned char)0, port ); outb( (unsigned char)0, port + 1 ); data = inb( port + 2 ); /* check if the test byte was retrieved */ if ( data == SLH_TEST_BYTE ) { slh_board_port = port; SLHDEBUG( "Found Board! port=0x%lx\n", slh_board_port ); /* register the ports */ request_region( slh_board_port, 4, "slh" ); return ( port ); } } return ( -1 ); } int slh_board_pump( unsigned char *data, unsigned long length ) { /* pump the firmware and parameters to the SoundLight board */ register unsigned long i; register unsigned long addr; register unsigned char test; if ( slh_board_port == 0 ) { return ( -1 ); } /* stop repetitive channel timer (if running) */ slh_channel_timer_stop(); /* load the board firmware */ for ( addr = SLH_PUMP_ADDR, i = 0; i < length; i++, addr++, data++ ) { /* write a firmware byte to the board */ outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); outb( *data, slh_board_port + 2 ); /* read the firmware byte back from the board */ outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); test = inb( slh_board_port + 2 ); /* verify that the firmware byte was written successfully */ if ( *data != test ) { SLHDEBUG( "Board pump check failed! data=0x%x, actual=0x%x\n", (unsigned int)*data, (unsigned int)test ); return ( -1 ); } } /* zero the remaining board memory */ for ( ; i < SLH_RAM_SIZE; i++, addr++ ) { outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); outb( (unsigned char)0, slh_board_port + 2 ); } /* load the board parameters */ slh_board_pump_parms(); /* start processor on board */ slh_board_start(); /* Wait 100us for the board to initialize. Measurements * indicate that it takes the 1512B about 56us to init. */ udelay( 100 ); /* check the board type */ outb( ADDR_LO(SLH_BRD_TYPE_ADDR), slh_board_port ); outb( ADDR_HI(SLH_BRD_TYPE_ADDR), slh_board_port + 1 ); test = inb( slh_board_port + 2 ); if ( test != SLH_TYPE_1512B ) { SLHDEBUG( "Board type check failed! actual=0x%x\n", (unsigned int)test ); return ( -1 ); } SLHDEBUG( "Board type is 1512B!\n" ); /* start repetitive channel timer */ slh_channel_timer_start(); return ( 0 ); } int slh_board_pump_parms() { /* pump parameters to SoundLight board */ register unsigned long i; register unsigned long addr; if ( slh_board_port == 0 ) { return ( -1 ); } addr = SLH_PARMS_ADDR; for ( i = 0; i < sizeof(slh_board_parms); i++, addr++ ) { outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); outb( slh_board_parms[i], slh_board_port + 2 ); } return ( 0 ); } #if 0 int slh_board_init_chans() { register unsigned long i; register unsigned long addr; if ( slh_board_port == 0 ) { return ( -1 ); } addr = SLH_SND_CHAN_ADDR; for ( i = 0; i < NUM_DMX_CHANS; i++, addr++ ) { outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); outb( 0, slh_board_port + 2 ); } return ( 0 ); } #endif int slh_board_start() { /* start processor on Soundlight board */ if ( slh_board_port == 0 ) { return ( -1 ); } (void) inb( slh_board_port + 3 ); return ( 0 ); } int slh_board_stop() { /* stop processor on Soundlight board */ if ( slh_board_port == 0 ) { return ( -1 ); } outb( (unsigned char)0, slh_board_port ); return ( 0 ); } void slh_board_chan_set( unsigned long chan, unsigned long value ) { /* set a DMX-512 channel to a specified value */ unsigned long addr; if ( slh_board_port == 0 ) { return; } /* channel must be 0 to 511 */ if ( chan >= 512 ) { return; } /* calculate the board address of the DMX-512 channel */ addr = SLH_SND_CHAN_ADDR + chan; /* set the channel value */ outb( ADDR_LO(addr), slh_board_port ); outb( ADDR_HI(addr), slh_board_port + 1 ); outb( value, slh_board_port + 2 ); return; } 07070100031047000081a4000000000000000000000001388dad1d0000173d000000030000000600000000000000000000000a00000000channel.c/* * This file contains the "channel" software module. It contains the * data structures and C functions that maintain and modify the DMX-512 * channel values. This module uses a repetitive timer to update the * DMX-512 channels every 50ms. This module also performs dimming * operations. */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #define __NO_VERSION__ #include #include #include #include #include #include #include #include /* local definitions */ #include "slh.h" /* prevent symbols from this file from * being accessed by any part of the * kernel outside of this driver. */ EXPORT_NO_SYMBOLS; /* interval timer in milliseconds */ #define TMR_INTERVAL 50 /* interval timer in clock ticks */ #define TMR_TICKS (HZ*TMR_INTERVAL/1000) /* DMX-512 channel status */ typedef struct { /* total amount channel is to change with dim */ long delta; /* current number of timer expirations into dim */ unsigned long tick; /* total number of timer expirations in dim */ unsigned long total_ticks; /* channel value at start of dim */ unsigned long start_value; /* current channel value */ unsigned long current_value; } CHAN_STATUS; CHAN_STATUS chan_status[NUM_DMX_CHANS]; /* "is the timer running?" flag */ static unsigned long timer_running; /* kernel timer structure */ static struct timer_list slh_timer; int slh_channel_mod_init() { /* initialize the "channel" software module * (called when the slh driver is loaded). */ /* zero the channel status structure */ memset( (void *)chan_status, 0, sizeof(chan_status) ); /* zero the kernel timer structure */ memset( (void *)&slh_timer, 0, sizeof(slh_timer) ); timer_running = 0; return ( 0 ); } void slh_channel_mod_cleanup() { /* cleanup the "channel" software module * (called when the slh driver is unloaded). */ /* stop the channel timer (if running) */ slh_channel_timer_stop(); } void slh_channel_dim( unsigned long chan, unsigned long new_value, unsigned long transition ) { /* dim a DMX-512 channel */ register CHAN_STATUS *stat; unsigned long flags; /* validate the DMX-512 channel number (it must be 1 -> 512) */ if ( (chan == 0) || (chan > NUM_DMX_CHANS) ) { return; } /* block interrupts */ save_flags( flags ); cli(); /* update channel data */ stat = &chan_status[ chan - 1 ]; stat->delta = new_value - stat->current_value; stat->tick = 0; stat->total_ticks = transition / TMR_INTERVAL; stat->start_value = stat->current_value; #if 0 SLHDEBUG( "chan_dim: new=%ld, delta=%ld, curr=%ld, start=%ld\n", new_value, stat->delta, stat->current_value, stat->start_value); #endif /* allow interrupts */ restore_flags( flags ); } void slh_channel_timeout( unsigned long data ) { /* When the kernel timer expires, this function is called. * It will perform dimmer calculations, update channel data, * start the SoundLight board to send the DMX-512 channel data, * and restart the kernel channel timer. */ register unsigned long i; register CHAN_STATUS *stat; timer_running = 0; /* set the channel status pointer */ stat = &chan_status[0]; /* The 1512b board requires at least one channel be * set before it will transmit the DMX channel data... * so we will always set channel 0. */ slh_board_chan_set( 0, stat->current_value ); /* loop through the channel status structure looking for * channels currently being dimmed. */ for ( i = 0; i < NUM_DMX_CHANS; i++, stat++ ) { /* check if channel is being dimmed */ if ( stat->delta == 0L ) { continue; } /* increment the count of timer expirations * for this channel being dimmed. */ stat->tick += 1; /* check if this is an immediate or gradual * change to the new channel value */ if ( stat->total_ticks ) { /* gradual change */ stat->current_value = stat->start_value + (long) (stat->tick * stat->delta) / (long)stat->total_ticks; } else { /* immediate change */ stat->current_value = stat->start_value + stat->delta; } /* set channel value */ slh_board_chan_set( i, stat->current_value ); /* check if the dim is complete */ if ( stat->tick >= stat->total_ticks ) { stat->delta = 0; } } /* end for ( ... ) */ /* start board DMX-512 transmitter */ slh_board_start(); /* start timer */ slh_channel_timer_start(); } void slh_channel_timer_start() { /* start kernel channel timer */ if ( timer_running ) { return; } timer_running = 1; /* initialize timer */ init_timer( &slh_timer ); slh_timer.function = slh_channel_timeout; slh_timer.data = 0; slh_timer.expires = jiffies + TMR_TICKS; /* start timer */ add_timer( &slh_timer ); } void slh_channel_timer_stop() { /* stop kernel channel timer */ if ( !timer_running ) { return; } del_timer( &slh_timer ); timer_running = 0; } int slh_channel_load() { /* load all channel values to SoundLight board */ register unsigned long i; register CHAN_STATUS *stat; unsigned long flags; /* block interrupts */ save_flags( flags ); cli(); for ( i = 0, stat = chan_status; i < NUM_DMX_CHANS; i++, stat++ ) { /* set channel value */ slh_board_chan_set( i, stat->current_value ); } /* end for ( ... ) */ /* allow interrupts */ restore_flags( flags ); return ( 0 ); } void slh_channel_read( unsigned char *buffer ) { /* read into a buffer all current channel values */ register unsigned long i; register CHAN_STATUS *stat; unsigned long flags; /* block interrupts */ save_flags( flags ); cli(); for ( i = 0, stat = chan_status; i < NUM_DMX_CHANS; i++, stat++ ) { /* save channel value */ *buffer++ = stat->current_value; } /* end for ( ... ) */ /* allow interrupts */ restore_flags( flags ); } 07070100031048000081a4000000000000000000000001388dad1d00001fe8000000030000000600000000000000000000000900000000driver.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. */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include #include #include /* printk() */ #include /* kmalloc() */ #include /* everything... */ #include /* error codes */ #include /* size_t */ #include #include /* O_ACCMODE */ #include #include "slh.h" /* local definitions */ #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 */ SLHDEBUG( "OPEN() pid=%d\n", (int)current->pid ); /* 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 */ SLHDEBUG( "CLOSE()\n" ); /* 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; } SLHDEBUG( "READ()\n" ); /* 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; SLHDEBUG( "IOCTL()\n" ); /* 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 ); } SLHDEBUG( "IOCTL( PUMP ) Pump file size=%ld.\n", size ); /* 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 ); } 0707010003104b000081a4000000000000000000000001388dad1d0000017b000000030000000600000000000000000000000500000000load#!/bin/sh module="slh" device="slh" group="wheel" mode="664" # remove stale nodes rm -f /dev/${device}? # invoke insmod with all arguments we got /sbin/insmod -f $module $* || exit 1 major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` mknod /dev/${device} c $major 0 # give appropriate group/permissions chgrp $group /dev/${device} chmod $mode /dev/${device} 0707010003104c000081a4000000000000000000000001388dad1d00000853000000030000000600000000000000000000000600000000slh.h#include /* * Macros to help debugging */ #undef PDEBUG /* undef it, just in case */ #ifdef SLH_DEBUG # ifdef __KERNEL__ /* This one if debugging is on, and kernel space */ # define SLHDEBUG(fmt, args...) printk( KERN_CRIT "SLH: " fmt, ## args) # else /* This one for user space */ # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) # endif #else # define SLHDEBUG(fmt, args...) /* not debugging: nothing */ # define PDEBUG(fmt, args...) /* not debugging: nothing */ #endif #undef PDEBUGG #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ #define SLH_MAJOR 0 /* dynamic major by default */ /* * Ioctl definitions */ #define SLH_IOC_MAGIC 'K' #define SLH_IOCRESET _IO(SLH_IOC_MAGIC, 0) #define SLH_IOCPUMP _IO(SLH_IOC_MAGIC, 1) #define SLH_IOCPARAMS _IO(SLH_IOC_MAGIC, 2) #define SLH_IOCREAD _IO(SLH_IOC_MAGIC, 3) #define SLH_IOCSET _IO(SLH_IOC_MAGIC, 4) #define NUM_DMX_CHANS 512 typedef struct { unsigned long channel; /* DMX-512 channel */ unsigned long new_value; /* new channel value */ unsigned long transition; /* transition time to new value */ } CHAN_VALUE; typedef struct { unsigned long num_chan; CHAN_VALUE chan_value[NUM_DMX_CHANS]; } DMX_CHANNELS; #define CHAN_STRUCT_SZ( num ) \ ( sizeof(DMX_CHANNELS) - (NUM_DMX_CHANS-(num))*sizeof(CHAN_VALUE) ) #ifdef __KERNEL__ /* board.c */ extern int slh_board_mod_init(); extern void slh_board_mod_cleanup(); extern int slh_board_find(); extern int slh_board_pump( unsigned char *, unsigned long ); extern int slh_board_pump_parms(); extern int slh_board_start(); extern int slh_board_stop(); extern void slh_board_chan_set( unsigned long, unsigned long ); /* channel.c */ extern int slh_channel_mod_init(); extern void slh_channel_mod_cleanup(); extern void slh_channel_dim( unsigned long, unsigned long, unsigned long ); extern void slh_channel_timeout( unsigned long ); extern void slh_channel_timer_start(); extern void slh_channel_timer_stop(); extern int slh_channel_load(); extern void slh_channel_read( unsigned char * ); #endif 0707010003104d000081a4000000000000000000000001388dae7b00000df8000000030000000600000000000000000000000b00000000slh_demo.c#include #include #include #include #include #include #include #include #include #include #include "slh.h" #define PUMP_FILE "./SLHDMX16.BIN" unsigned char buffer[NUM_DMX_CHANS]; void print_dmx_channels(); int main( ) { int fd; int retval; struct stat stat; void *pump_buf; DMX_CHANNELS dmx_channels; /* open the pump file */ fd = open( PUMP_FILE, O_RDONLY ); if ( fd < 0 ) { fprintf( stderr, "cannot open pump file (%d)\n", errno ); exit ( -10 ); } /* get the pump file stats to determine the file size */ retval = fstat( fd, &stat ); if ( retval < 0 ) { fprintf( stderr, "cannot get pump file stats (%d)\n", errno ); exit ( -20 ); } fprintf( stderr, "Pumping Board; size = %d\n", (int)stat.st_size ); /* allocate buffer to hold pump file */ pump_buf = malloc( (size_t)stat.st_size + sizeof(long) ); if ( pump_buf == NULL ) { fprintf( stderr, "unable to malloc() pump buffer\n" ); exit ( -30 ); } /* read in pump file to buffer */ retval = read( fd, pump_buf+sizeof(long), (size_t)stat.st_size ); if ( retval < 0 ) { fprintf( stderr, "cannot read pump file (%d)\n", errno ); exit ( -40 ); } /* copy the pump file length into the first word of the buffer */ *((unsigned long *)pump_buf) = stat.st_size; /* open soundlight 1512b device */ fd = open( "/dev/slh", O_RDWR ); if ( fd < 0 ) { fprintf( stderr, "open( /dev/slh ) failed (%d)\n", errno ); exit ( -50 ); } /* pump the board */ retval = ioctl( fd, SLH_IOCPUMP, (char*)pump_buf ); if ( retval < 0 ) { fprintf( stderr, "ioctl( pump ) failed (%d)\n", errno ); exit ( -60 ); } /* free pump file buffer */ free( pump_buf ); /* print non-zero DMX-512 channels */ print_dmx_channels( fd ); /* set board parameters here ??? */ /* zero channel structure */ memset( (void *)&dmx_channels, 0, sizeof(dmx_channels) ); /* set channel values */ dmx_channels.num_chan = 2; /* set dimmer 4 to 55 immediately */ dmx_channels.chan_value[0].channel = 4; dmx_channels.chan_value[0].new_value = 55; dmx_channels.chan_value[0].transition = 0; /* set dimmer 8 to 128 using a one second fade */ dmx_channels.chan_value[1].channel = 8; dmx_channels.chan_value[1].new_value = 128; dmx_channels.chan_value[1].transition = 1000; retval = ioctl( fd, SLH_IOCSET, (char*)&dmx_channels ); if ( retval < 0 ) { fprintf( stderr, "ioctl( set ) failed (%d)\n", errno ); } print_dmx_channels( fd ); exit ( 0 ); } void print_dmx_channels( int fd ) { register int i; int retval; int found; fprintf( stderr, "printing non-zero channels:\n" ); /* This sleep is not necessary. But, if channel values are set * [using ioctl()] and print_dmx_channels() is called immediately * afterwards, the change might not be seen since it is not actually * made until the next channel timer expiration which ocurs every * 50ms. */ usleep( 50000 ); /* read in DMX-512 channel values */ retval = read( fd, buffer, NUM_DMX_CHANS ); if ( retval < 0 ) { fprintf( stderr, "read( chans ) failed (%d)\n", errno ); return; } if ( retval != NUM_DMX_CHANS ) { fprintf( stderr, "read( chans ): num_chans=%d\n", retval ); } /* print non-zero channels */ for ( found = 0, i = 0; i < 512; i++ ) { if ( buffer[i] == 0 ) { continue; } fprintf( stderr, "\tchan(%d)=%d\n", i+1, (int)buffer[i] ); found++; } if ( !found ) { fprintf( stderr, "\tNo non-zero channels\n" ); } } 0707010003104e000081a4000000000000000000000001388dad1d000000ab000000030000000600000000000000000000000700000000unload#!/bin/sh module="slh" device="slh" # invoke rmmod with all arguments we got /sbin/rmmod $module $* || exit 1 # remove nodes rm -f /dev/${device} /dev/${device} exit 0 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!