From: <he...@us...> - 2004-06-22 23:08:46
|
Update of /cvsroot/gc-linux/linux/arch/ppc/platforms In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26925 Modified Files: gc-rsw.c Log Message: Let the reset driver do something useful. Pressing the reset button once schedules a reboot in 3 seconds. If the reset button is pressed again before the 3 seconds timeout expires then the reboot is cancelled. If the reboot is not cancelled, and the reset button is pressed again, a emergency mode is entered. In the emergency mode, pressing 10 times the reset button causes a machine restart (or a kexec if CONFIG_KEXEC is defined and a kexec'able image is loaded). The emergency mode will be mainly useful for developers. Index: gc-rsw.c =================================================================== RCS file: /cvsroot/gc-linux/linux/arch/ppc/platforms/gc-rsw.c,v retrieving revision 1.6 retrieving revision 1.7 diff -u -d -r1.6 -r1.7 --- gc-rsw.c 16 Jun 2004 15:17:16 -0000 1.6 +++ gc-rsw.c 22 Jun 2004 23:08:36 -0000 1.7 @@ -2,6 +2,7 @@ /* gc-rsw.c GameCube Reset Switch Driver */ /* ------------------------------------------------------------------------- */ /* Copyright (C) 2004 Stefan Esser + Copyright (C) 2004 Albert Herranz This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,39 +20,161 @@ /* ------------------------------------------------------------------------- */ #include <linux/kernel.h> -#include <linux/ioport.h> #include <linux/module.h> +#include <linux/spinlock.h> #include <linux/delay.h> -#include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/wait.h> +#include <linux/reboot.h> -#include <asm/irq.h> +#ifdef CONFIG_KEXEC +#include <linux/kexec.h> +#endif #define RSW_IRQ 1 -static irqreturn_t gc_rsw_handler(int this_irq, void *dev_id, struct pt_regs *regs) { +/* from kernel/sys.c */ +extern void ctrl_alt_del(void); + +#define GC_RSW_NORMAL_TIMEOUT 3 /* secs */ +#define GC_RSW_EMERGENCY_CLICKS 10 + +typedef enum { + IDLE = 0, /* nothing to do */ + NORMAL_RESET, /* reboot requested */ + EMERGENCY_RESET, /* try emergency reboot */ +} gc_rsw_state_t; + +struct gc_rsw_private { + gc_rsw_state_t state; + struct timer_list timer; + unsigned long jiffies; + int clicks; + int timeout; + spinlock_t lock; +}; + +static struct gc_rsw_private gc_rsw_private = { + .state = IDLE, + .timeout = GC_RSW_NORMAL_TIMEOUT, +}; + +static void gc_rsw_emergency_reset(void); +static void gc_rsw_normal_reset(unsigned long dummy); + +/** + * + */ +static irqreturn_t gc_rsw_handler(int this_irq, void *data, + struct pt_regs *regs) +{ + struct gc_rsw_private *priv = (struct gc_rsw_private *)data; + unsigned long flags; + + spin_lock_irqsave(priv->lock, flags); + + /* someone pressed the reset button */ + switch (priv->state) { + case IDLE: + priv->state = NORMAL_RESET; + printk(KERN_EMERG "Rebooting in %d seconds...\n", + priv->timeout); + printk(KERN_WARNING + "Press Reset button again to cancel reboot!\n"); + + /* schedule a reboot in a few seconds */ + init_timer(&priv->timer); + priv->timer.expires = jiffies + priv->timeout * HZ; + priv->timer.function = + (void (*)(unsigned long))gc_rsw_normal_reset; + add_timer(&priv->timer); + priv->jiffies = jiffies; + break; + case NORMAL_RESET: + if (time_before(jiffies, priv->jiffies + priv->timeout * HZ)) { + /* the reset button was hit again before deadline */ + del_timer(&priv->timer); + priv->state = IDLE; + printk(KERN_EMERG "Reboot cancelled!\n"); + } else { + /* + * Time expired. System should be now restarting. + * Go to emergency mode in case something goes bad. + */ + priv->state = EMERGENCY_RESET; + priv->clicks = 0; + printk(KERN_WARNING + "SWITCHED TO EMERGENCY RESET MODE!\n" + "Press %d times the Reset button to force" + " a hard reset!\n", GC_RSW_EMERGENCY_CLICKS); + } + break; + case EMERGENCY_RESET: + /* force a hard reset if the user insists ... */ + if (++priv->clicks >= GC_RSW_EMERGENCY_CLICKS) { + spin_unlock_irqrestore(priv->lock, flags); + gc_rsw_emergency_reset(); + return IRQ_HANDLED; + } else { + printk(KERN_INFO + "%d ...\n", + GC_RSW_EMERGENCY_CLICKS - priv->clicks); + } + break; + } + + spin_unlock_irqrestore(priv->lock, flags); - printk(KERN_ERR "gc_rsw: reset switch pressed\n"); return IRQ_HANDLED; } +/** + * + */ +static void gc_rsw_emergency_reset(void) +{ +#ifdef CONFIG_KEXEC + struct kimage *image; + image = xchg(&kexec_image, 0); + if (image) { + machine_kexec(image); + } +#endif + machine_restart(NULL); +} + +/** + * + */ +static void gc_rsw_normal_reset(unsigned long dummy) +{ + ctrl_alt_del(); +} +/** + * + */ static int gc_rsw_init(void) { int err; - err = request_irq(RSW_IRQ, gc_rsw_handler, 0, "GameCube Reset Switch", 0); + spin_lock_init(&gc_rsw_private.lock); + + err = + request_irq(RSW_IRQ, gc_rsw_handler, 0, "GameCube Reset Switch", + (void *)&gc_rsw_private); if (err) printk(KERN_ERR "gc_rsw: Request irq%d failed\n", RSW_IRQ); return err; } +/** + * + */ static void gc_rsw_exit(void) { - free_irq(RSW_IRQ, 0); + free_irq(RSW_IRQ, &gc_rsw_private); } MODULE_AUTHOR("Stefan Esser <se...@no...>"); |