patch-2.3.99-pre6 linux/drivers/sbus/char/sunkbd.c

Next file: linux/drivers/sbus/char/sunmouse.c
Previous file: linux/drivers/sbus/char/su.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre5/linux/drivers/sbus/char/sunkbd.c linux/drivers/sbus/char/sunkbd.c
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
+#include <linux/spinlock.h>
 #include <linux/devfs_fs_kernel.h>
 
 #include <asm/kbio.h>
@@ -90,6 +91,8 @@
 	return 0;
 }
 
+static spinlock_t sunkbd_lock = SPIN_LOCK_UNLOCKED;
+
 /*
  * global state includes the following, and various static variables
  * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
@@ -255,6 +258,7 @@
 static void nop_kbd_put_char(unsigned char c) { }
 static void (*kbd_put_char)(unsigned char) = nop_kbd_put_char;
 
+/* Must be invoked under sunkbd_lock. */
 static inline void send_cmd(unsigned char c)
 {
 	kbd_put_char(c);
@@ -429,6 +433,7 @@
 	    e0_keys[scancode - 128];
 }
 
+static void __sunkbd_inchar(unsigned char ch, struct pt_regs *regs);
 void sunkbd_inchar(unsigned char ch, struct pt_regs *regs);
 static void keyboard_timer (unsigned long ignored);
 
@@ -443,16 +448,17 @@
 {
 	unsigned long flags;
 
-	save_flags(flags); cli();
+	spin_lock_irqsave(&sunkbd_lock, flags);
 
 	/* Auto repeat: send regs = 0 to indicate autorepeat */
-	sunkbd_inchar (last_keycode, 0);
+	__sunkbd_inchar (last_keycode, 0);
 	del_timer (&auto_repeat_timer);
 	if (kbd_rate_ticks) {
 		auto_repeat_timer.expires = jiffies + kbd_rate_ticks;
 		add_timer (&auto_repeat_timer);
 	}
-	restore_flags(flags);
+
+	spin_unlock_irqrestore(&sunkbd_lock, flags);
 }
 
 #ifndef CONFIG_PCI
@@ -460,8 +466,10 @@
 #endif
 
 /* #define SKBD_DEBUG */
-/* This is our keyboard 'interrupt' routine. */
-void sunkbd_inchar(unsigned char ch, struct pt_regs *regs)
+/* This is our keyboard 'interrupt' routine.
+ * Must run under sunkbd_lock.
+ */
+static void __sunkbd_inchar(unsigned char ch, struct pt_regs *regs)
 {
 	unsigned char keycode;
 	char up_flag;                          /* 0 or SUNKBD_UBIT */
@@ -612,6 +620,15 @@
 	tasklet_schedule(&keyboard_tasklet);
 }
 
+void sunkbd_inchar(unsigned char ch, struct pt_regs *regs)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sunkbd_lock, flags);
+	__sunkbd_inchar(ch, regs);
+	spin_unlock_irqrestore(&sunkbd_lock, flags);
+}
+
 static void put_queue(int ch)
 {
 	wake_up(&keypress_wait);
@@ -1164,15 +1181,21 @@
 static unsigned char sunkbd_ledstate = 0xff; /* undefined */
 void sun_kbd_bh(unsigned long dummy)
 {
-	unsigned char leds = getleds();
-	unsigned char kbd_leds = vcleds_to_sunkbd(leds);
+	unsigned long flags;
+	unsigned char leds, kbd_leds;
+
+	spin_lock_irqsave(&sunkbd_lock, flags);
 
+	leds = getleds();
+	kbd_leds = vcleds_to_sunkbd(leds);
 	if (kbd_leds != sunkbd_ledstate) {
 		ledstate = leds;
 		sunkbd_ledstate = kbd_leds;
 		send_cmd(SKBDCMD_SETLED);
 		send_cmd(kbd_leds);
 	}
+
+	spin_unlock_irqrestore(&sunkbd_lock, flags);
 }
 
 /* Support for keyboard "beeps". */ 
@@ -1180,7 +1203,11 @@
 /* Timer routine to turn off the beep after the interval expires. */
 static void sunkbd_kd_nosound(unsigned long __unused)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&sunkbd_lock, flags);
 	send_cmd(SKBDCMD_BELLOFF);
+	spin_unlock_irqrestore(&sunkbd_lock, flags);
 }
 
 /*
@@ -1195,8 +1222,7 @@
 	static struct timer_list sound_timer = { NULL, NULL, 0, 0,
 						 sunkbd_kd_nosound };
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&sunkbd_lock, flags);
 
 	del_timer(&sound_timer);
 
@@ -1209,7 +1235,7 @@
 	} else
 		send_cmd(SKBDCMD_BELLOFF);
 
-	restore_flags(flags);
+	spin_unlock_irqrestore(&sunkbd_lock, flags);
 }
 
 extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
@@ -1260,6 +1286,7 @@
 #define KBD_QSIZE 32
 static Firm_event kbd_queue [KBD_QSIZE];
 static int kbd_head, kbd_tail;
+static spinlock_t kbd_queue_lock = SPIN_LOCK_UNLOCKED;
 char kbd_opened;
 static int kbd_active = 0;
 static DECLARE_WAIT_QUEUE_HEAD(kbd_wait);
@@ -1268,16 +1295,22 @@
 void
 push_kbd (int scan)
 {
-	int next = (kbd_head + 1) % KBD_QSIZE;
+	unsigned long flags;
+	int next;
 
 	if (scan == KBD_IDLE)
 		return;
+
+	spin_lock_irqsave(&kbd_queue_lock, flags);
+	next = (kbd_head + 1) % KBD_QSIZE;
 	if (next != kbd_tail){
 		kbd_queue [kbd_head].id = scan & KBD_KEYMASK;
 		kbd_queue [kbd_head].value=scan & KBD_UP ? VKEY_UP : VKEY_DOWN;
 		kbd_queue [kbd_head].time = xtime;
 		kbd_head = next;
 	}
+	spin_unlock_irqrestore(&kbd_queue_lock, flags);
+
 	if (kb_fasync)
 		kill_fasync (kb_fasync, SIGIO, POLL_IN);
 	wake_up_interruptible (&kbd_wait);
@@ -1287,6 +1320,7 @@
 kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos)
 {
 	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
 	char *end, *p;
 
 	/* Return EWOULDBLOCK, because this is what the X server expects */
@@ -1294,9 +1328,11 @@
 		if (f->f_flags & O_NONBLOCK)
 			return -EWOULDBLOCK;
 		add_wait_queue (&kbd_wait, &wait);
-		while (kbd_head == kbd_tail && !signal_pending(current)) {
-			current->state = TASK_INTERRUPTIBLE;
-			schedule ();
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kbd_head == kbd_tail && !signal_pending(current)) {
+			schedule();
+			goto repeat;
 		}
 		current->state = TASK_RUNNING;
 		remove_wait_queue (&kbd_wait, &wait);
@@ -1304,29 +1340,40 @@
 	/* There is data in the keyboard, fill the user buffer */
 	end = buffer+count;
 	p = buffer;
+	spin_lock_irqsave(&kbd_queue_lock, flags);
 	for (; p < end && kbd_head != kbd_tail;){
+		Firm_event this_event = kbd_queue[kbd_tail];
+
+		kbd_tail = (kbd_tail + 1) % KBD_QSIZE;
+
+		spin_unlock_irqrestore(&kbd_queue_lock, flags);
+
 #ifdef CONFIG_SPARC32_COMPAT
 		if (current->thread.flags & SPARC_FLAG_32BIT) {
-			copy_to_user_ret((Firm_event *)p, &kbd_queue [kbd_tail], 
-					 sizeof(Firm_event)-sizeof(struct timeval), -EFAULT);
+			copy_to_user_ret((Firm_event *)p, &this_event,
+					 sizeof(Firm_event)-sizeof(struct timeval),
+					 -EFAULT);
 			p += sizeof(Firm_event)-sizeof(struct timeval);
-			__put_user_ret(kbd_queue[kbd_tail].time.tv_sec, (u32 *)p, -EFAULT);
+			__put_user_ret(this_event.time.tv_sec, (u32 *)p, -EFAULT);
 			p += sizeof(u32);
-			__put_user_ret(kbd_queue[kbd_tail].time.tv_usec, (u32 *)p, -EFAULT);
+			__put_user_ret(this_event.time.tv_usec, (u32 *)p, -EFAULT);
 			p += sizeof(u32);
 		} else
 #endif
 		{
-			copy_to_user_ret((Firm_event *)p, &kbd_queue [kbd_tail], 
+			copy_to_user_ret((Firm_event *)p, &this_event, 
 					 sizeof(Firm_event), -EFAULT);
 			p += sizeof (Firm_event);
 		}
 #ifdef KBD_DEBUG
-		printk ("[%s]", kbd_queue [kbd_tail].value == VKEY_UP ? "UP" : "DOWN");
+		printk ("[%s]", this_event.value == VKEY_UP ? "UP" : "DOWN");
 #endif
-		kbd_tail++;
-		kbd_tail %= KBD_QSIZE;
+
+		spin_lock_irqsave(&kbd_queue_lock, flags);
 	}
+
+	spin_unlock_irqrestore(&kbd_queue_lock, flags);
+
 	return p-buffer;
 }
 
@@ -1387,7 +1434,9 @@
 		switch (c) {
 			case SKBDCMD_CLICK:
 			case SKBDCMD_NOCLICK:
+				spin_lock_irq(&sunkbd_lock);
 				send_cmd(c);
+				spin_unlock_irq(&sunkbd_lock);
 				return 0;
 			case SKBDCMD_BELLON:
 				kd_mksound(1,0);
@@ -1469,7 +1518,11 @@
 		return 0;
 
 	kbd_opened = fg_console + 1;
+
+	spin_lock_irq(&kbd_queue_lock);
 	kbd_head = kbd_tail = 0;
+	spin_unlock_irq(&kbd_queue_lock);
+
 	return 0;
 }
 
@@ -1537,6 +1590,8 @@
 	if(sunkbd_type == SUNKBD_TYPE2)
 		sunkbd_clickp = 0;
 
+	spin_lock_irq(&sunkbd_lock);
+
 	if(sunkbd_clickp) {
 		send_cmd(SKBDCMD_CLICK);
 		printk("with keyclick\n");
@@ -1548,6 +1603,8 @@
 	/* Dork with led lights, then turn them all off */
 	send_cmd(SKBDCMD_SETLED); send_cmd(0xf); /* All on */
 	send_cmd(SKBDCMD_SETLED); send_cmd(0x0); /* All off */
+
+	spin_unlock_irq(&sunkbd_lock);
 
 	/* Register the /dev/kbd interface */
 	devfs_register (NULL, "kbd", 0, DEVFS_FL_NONE,

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)