/* * $Id: lights.c,v 1.4 2001/12/18 12:50:38 cse466_t Exp $ * * lights.c * Christmas light control module for arm-linux on a Cerfboard * Authors: Robin Battey and Lindsey Irwin * */ #ifdef MODULE #include #endif #include #include #include #include #include #include "Sequence.h" #define LIGHTS_DEV_MAJOR 23 #define LIGHTS_IDENT "Lights driver, v1.0" #define LIGHTS "lights" #define GPIO_SET(x) (GPSR|=(x)) #define GPIO_CLR(x) (GPCR|=(x)) #define GPIO_GET(x) (GPLR&(x)) typedef struct lmode_st { unsigned short *states; loff_t length; } lmode_t; /* * global vars are initialized to zero automajically, this is redundant for readability */ static lmode_t modes[16] = {{0}}; /* saved internal modes, default to always on */ static sequence_t *program = NULL; /* saved program to run */ static loff_t length = 0; static loff_t strobe = 0; /* mode index */ static loff_t index = 0; /* program index */ static loff_t label = 0; /* where we loop to in the program */ static int running = 0; /* are we running or not? */ static struct timer_list mode_timer; static struct timer_list program_timer; static loff_t lights_lseek (struct file * file, loff_t offset, int type) { return 0; } static ssize_t lights_read (struct file * file, char * buffer, size_t count, loff_t *ppos) { return 0; } static ssize_t lights_write (struct file* file, const char* buffer, size_t count, loff_t *ppos) { sequence_t *sequence; int mode = 0; int result = 0; int was_running = running; #ifdef DEBUG printk (KERN_INFO "%s: write, count: %i\n", LIGHTS_IDENT, count); #endif sequence = (sequence_t *) buffer; if (IS_MODE_BEGIN(sequence[0])) { /* * save the mode */ #ifdef DEBUG printk (KERN_INFO "%s: mode command\n", LIGHTS_IDENT); #endif /* * pause the program, so the timer handlers don't touch anything, and just re-register themselves */ running = 0; mode = sequence[1].mode; /* the rest of this tuple is ignored */ if (modes[mode].states) { kfree(modes[mode].states); } modes[mode].states = (unsigned short *) kmalloc (count - 2 * sizeof(sequence_t), GFP_KERNEL); copy_from_user (modes[mode].states, &sequence[1], count - 2 * sizeof(sequence_t)); modes[mode].length = (count - 2 * sizeof(sequence_t)) / sizeof (unsigned short); /* * reset strobe if necessary (if we're running that particular mode right now) */ if (mode == program[index].mode) { strobe = 0; } /* * restore program to previous state */ running = was_running; } else if (IS_PROGRAM_BEGIN(sequence[0])) { /* * stop the program -- we'll be replacing it */ del_timer (&program_timer); del_timer (&mode_timer); #ifdef DEBUG printk (KERN_INFO "%s: program command\n", LIGHTS_IDENT); #endif /* * save the program */ if (program) { kfree(program); } program = (sequence_t *) kmalloc (count - sizeof (sequence_t), GFP_KERNEL); copy_from_user (program, &sequence[1], count - sizeof (sequence_t)); /* * reset the program to the beginning */ length = (count - sizeof(sequence_t)) / sizeof(sequence_t); strobe = 0; label = 0; index = -1; /* * make sure it's running */ running = 1; program_timer.expires = jiffies; add_timer (&program_timer); } else { #ifdef DEBUG printk (KERN_WARNING "%s: invalid command\n", LIGHTS_IDENT); #endif result = -EINVAL; } /* count == "SUCCESS" */ if (result == 0) { result = count; } return result; } int lights_open (struct inode* inode, struct file* file) { int result = 0; #ifdef DEBUG printk (KERN_INFO "%s: open\n", LIGHTS_IDENT); #endif /* * only allow one device (device 0) */ if (MINOR(file->f_dentry->d_inode->i_dev) != 0) { result = -ENODEV; } return result; } int lights_release(struct inode* inode, struct file* file) { #ifdef DEBUG printk (KERN_INFO "%s: release\n", LIGHTS_IDENT); #endif return 0; } static struct file_operations lights_file_ops = { NULL, /* owner */ lights_lseek, /* lseek */ lights_read, /* read */ lights_write, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ lights_open, /* open */ NULL, /* flush */ lights_release, /* release */ NULL, /* fsync */ NULL, /* fasync */ NULL, /* lock */ NULL, /* readv */ NULL /* writev */ }; void mode_timer_handler (unsigned long unused) { /* * the default (uninitialized) mode is "always on" */ unsigned short state = 0xffff; #ifdef DEBUG printk (KERN_INFO "%s: mode timer, %li, running: %i\n", LIGHTS_IDENT, jiffies, running); #endif if (running) { if (modes[program[index].mode].states) { /* * fiddle with state here */ mode_timer.expires = mode_timer.expires + 10 * program[index].period; state = modes[program[index].mode].states[strobe]; strobe++; strobe %= modes[program[index].mode].length; add_timer (&mode_timer); } /* * lights are active low */ GPIO_CLR (state); GPIO_SET (~state); } else { /* we're paused */ mode_timer.expires = jiffies + 1; add_timer (&mode_timer); } return; } void program_timer_handler (unsigned long unused) { #ifdef DEBUG printk (KERN_INFO "%s: program timer\n", LIGHTS_IDENT); #endif if (running) { /* * increment the index */ index++; #ifdef DEBUG printk (KERN_DEBUG "%s: mode change\n", LIGHTS_IDENT); #endif /* * remember (and ignore) the loop marker */ while (index < length && IS_INF_LOOP_BEGIN (program[index])) { index++; label = index; } /* * loop when instructed */ if (index < length && IS_GOTO_LOOP (program[index])) { index = label; } /* * re-register the timers, unless we're at the end */ if (index < length) { /* * set up program timer */ program_timer.expires += 10 * program[index].length; add_timer (&program_timer); /* * reset mode timer */ strobe = 0; del_timer_sync (&mode_timer); /* make sure it's gone first... */ mode_timer.expires = jiffies; /* expires == now */ add_timer (&mode_timer); } else { /* * no race condition with write() because if write() reverses this, we'll * just do it again next time. Stop the mode timer, and halt the program. */ program_timer.expires = jiffies + 1; /* soon */ del_timer_sync (&mode_timer); running = 0; } } else { /* * We're paused for some reason */ program_timer.expires = jiffies + 1; /* the proverbial "soon" */ add_timer (&program_timer); } return; } int init_module(void) { int i; int result = register_chrdev(LIGHTS_DEV_MAJOR, LIGHTS, &lights_file_ops); #ifdef DEBUG printk (KERN_INFO "%s: loaded\n", LIGHTS_IDENT); #endif /* * set all GPIO pins to output */ GPDR|=0xFFFF; /* * initialize timers */ init_timer (&program_timer); program_timer.function = program_timer_handler; init_timer (&mode_timer); mode_timer.function = mode_timer_handler; /* * initialize modes */ for (i = 0; i < 16; i++) { modes[i] = (lmode_t) {NULL, 0}; } /* * XXX test of basic mode */ modes[1].states = (unsigned short *) kmalloc (2 * sizeof (unsigned short), GFP_KERNEL); modes[1].length = 2; modes[1].states[0] = 0x0000; modes[1].states[1] = 0xFFFF; modes[3].states = (unsigned short *) kmalloc (8 * sizeof (unsigned short), GFP_KERNEL); modes[3].length = 8; modes[3].states[0] = 0x0100; modes[3].states[1] = 0x0200; modes[3].states[2] = 0x0400; modes[3].states[3] = 0x0800; modes[3].states[4] = 0x1000; modes[3].states[5] = 0x0800; modes[3].states[6] = 0x0400; modes[3].states[7] = 0x0200; return result; } void cleanup_module(void) { int i; unregister_chrdev(LIGHTS_DEV_MAJOR, LIGHTS); #ifdef DEBUG printk (KERN_INFO "%s: unloaded\n", LIGHTS_IDENT); #endif /* * unregister timers */ del_timer_sync(&mode_timer); del_timer_sync(&program_timer); /* * free allocated memory */ kfree (program); for (i = 0; i < 16; i++) { if (modes[i].states) { kfree (modes[i].states); } } return; }