summaryrefslogtreecommitdiffstats
path: root/drivers/sbus
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-04-28 01:09:25 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-04-28 01:09:25 +0000
commitb9ba7aeb165cffecdffb60aec8c3fa8d590d9ca9 (patch)
tree42d07b0c7246ae2536a702e7c5de9e2732341116 /drivers/sbus
parent7406b0a326f2d70ade2671c37d1beef62249db97 (diff)
Merge with 2.3.99-pre6.
Diffstat (limited to 'drivers/sbus')
-rw-r--r--drivers/sbus/char/jsflash.c363
-rw-r--r--drivers/sbus/char/pcikbd.c215
-rw-r--r--drivers/sbus/char/sab82532.c83
-rw-r--r--drivers/sbus/char/su.c6
-rw-r--r--drivers/sbus/char/sunkbd.c101
-rw-r--r--drivers/sbus/char/sunmouse.c127
-rw-r--r--drivers/sbus/char/zs.c8
7 files changed, 656 insertions, 247 deletions
diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c
index af9f6c95f..44692c7e9 100644
--- a/drivers/sbus/char/jsflash.c
+++ b/drivers/sbus/char/jsflash.c
@@ -3,11 +3,15 @@
*
* Copyright (C) 1991, 1992 Linus Torvalds (drivers/char/mem.c)
* Copyright (C) 1997 Eddie C. Dost (drivers/sbus/char/flash.c)
- * Copyright (C) 1999 Pete Zaitcev
+ * Copyright (C) 1997-2000 Pavel Machek <pavel@ucw.cz> (drivers/block/nbd.c)
+ * Copyright (C) 1999-2000 Pete Zaitcev
*
* This driver is used to program OS into a Flash SIMM on
* Krups and Espresso platforms.
*
+ * TODO: do not allow erase/programming if file systems are mounted.
+ * TODO: Erase/program both banks of a 8MB SIMM.
+ *
* It is anticipated that programming an OS Flash will be a routine
* procedure. In the same time it is exeedingly dangerous because
* a user can program its OBP flash with OS image and effectively
@@ -31,23 +35,25 @@
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/string.h>
-#if 0 /* P3 from mem.c */
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/mman.h>
-#include <linux/random.h>
-#include <linux/raw.h>
-#include <linux/capability.h>
-#endif
+
+/*
+ * <linux/blk.h> is controlled from the outside with these definitions.
+ */
+#define MAJOR_NR JSFD_MAJOR
+
+#define DEVICE_NAME "jsfd"
+#define DEVICE_REQUEST jsfd_do_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+
+#include <linux/blk.h>
+
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#if 0 /* P3 from mem.c */
-#include <asm/system.h>
-#include <asm/sbus.h>
-#include <asm/ebus.h>
-#endif
#include <asm/pcic.h>
#include <asm/oplib.h>
@@ -58,8 +64,23 @@
/*
* Our device numbers have no business in system headers.
* The only thing a user knows is the device name /dev/jsflash.
+ *
+ * Block devices are laid out like this:
+ * minor+0 - Bootstrap, for 8MB SIMM 0x20400000[0x800000]
+ * minor+1 - Filesystem to mount, normally 0x20400400[0x7ffc00]
+ * minor+2 - Whole flash area for any case... 0x20000000[0x01000000]
+ * Total 3 minors per flash device.
+ *
+ * It is easier to have static size vectors, so we define
+ * a total minor range JSF_MAX, which must cover all minors.
*/
-#define JSF_MINOR 178
+/* character device */
+#define JSF_MINOR 178 /* 178 is registered with hpa */
+/* block device */
+#define JSF_MAX 3 /* 3 minors wasted total so far. */
+#define JSF_NPART 3 /* 3 minors per flash device */
+#define JSF_PART_BITS 2 /* 2 bits of minors to cover JSF_NPART */
+#define JSF_PART_MASK 0x3 /* 2 bits mask */
/*
* Access functions.
@@ -86,11 +107,20 @@ static void jsf_outl(unsigned long addr, __u32 data)
/*
* soft carrier
*/
+
+struct jsfd_part {
+ unsigned long dbase;
+ unsigned long dsize;
+ int refcnt;
+};
+
struct jsflash {
unsigned long base;
unsigned long size;
unsigned long busy; /* In use? */
struct jsflash_ident_arg id;
+ /* int mbase; */ /* Minor base, typically zero */
+ struct jsfd_part dv[JSF_NPART];
};
/*
@@ -103,6 +133,12 @@ struct jsflash {
#define JSF_BASE_JK 0x20400000
/*
+ */
+static int jsfd_blksizes[JSF_MAX];
+static int jsfd_sizes[JSF_MAX];
+static u64 jsfd_bytesizes[JSF_MAX];
+
+/*
* Let's pretend we may have several of these...
*/
static struct jsflash jsf0;
@@ -112,7 +148,7 @@ static struct jsflash jsf0;
* We use the Toggle bit DQ6 (0x40) because it does not
* depend on the data value as /DATA bit DQ7 does.
*
- * XXX Do we need any timeout here?
+ * XXX Do we need any timeout here? So far it never hanged, beware broken hw.
*/
static void jsf_wait(unsigned long p) {
unsigned int x1, x2;
@@ -146,6 +182,73 @@ static void jsf_write4(unsigned long fa, u32 data) {
}
/*
+ */
+static void jsfd_read(char *buf, unsigned long p, size_t togo) {
+ union byte4 {
+ char s[4];
+ unsigned int n;
+ } b;
+
+ while (togo >= 4) {
+ togo -= 4;
+ b.n = jsf_inl(p);
+ memcpy(buf, b.s, 4);
+ p += 4;
+ buf += 4;
+ }
+}
+
+static void jsfd_do_request(request_queue_t *q)
+{
+ struct request *req;
+ int dev;
+ struct jsfd_part *jdp;
+ unsigned long offset;
+ size_t len;
+
+ for (;;) {
+ INIT_REQUEST; /* if (QUEUE_EMPTY) return; */
+ req = CURRENT;
+
+ dev = MINOR(req->rq_dev);
+ if (dev >= JSF_MAX || (dev & JSF_PART_MASK) >= JSF_NPART) {
+ end_request(0);
+ continue;
+ }
+ jdp = &jsf0.dv[dev & JSF_PART_MASK];
+
+ offset = req->sector << 9;
+ len = req->current_nr_sectors << 9;
+ if ((offset + len) > jdp->dsize) {
+ end_request(0);
+ continue;
+ }
+
+ if (req->cmd == WRITE) {
+ printk(KERN_ERR "jsfd: write\n");
+ end_request(0);
+ continue;
+ }
+ if (req->cmd != READ) {
+ printk(KERN_ERR "jsfd: bad req->cmd %d\n", req->cmd);
+ end_request(0);
+ continue;
+ }
+
+ if ((jdp->dbase & 0xff000000) != 0x20000000) {
+ printk(KERN_ERR "jsfd: bad base %x\n", (int)jdp->dbase);
+ end_request(0);
+ continue;
+ }
+
+/* printk("jsfd%d: read buf %p off %x len %x\n", dev, req->buffer, (int)offset, (int)len); */ /* P3 */
+ jsfd_read(req->buffer, jdp->dbase + offset, len);
+
+ end_request(1);
+ }
+}
+
+/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
* though, in that case (0).
@@ -301,7 +404,7 @@ static int jsf_ioctl_program(unsigned long arg)
if (verify_area(VERIFY_READ, uptr, togo))
return -EFAULT;
while (togo != 0) {
- --togo;
+ togo -= 4;
copy_from_user(&b.s[0], uptr, 4);
jsf_write4(p, b.n);
p += 4;
@@ -316,6 +419,8 @@ static int jsf_ioctl(struct inode *inode, struct file *f, unsigned int cmd,
{
int error = -ENOTTY;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
switch (cmd) {
case JSFLASH_IDENT:
if (verify_area(VERIFY_WRITE, (void *)arg, JSFIDSZ))
@@ -334,6 +439,34 @@ static int jsf_ioctl(struct inode *inode, struct file *f, unsigned int cmd,
return error;
}
+static int jsfd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (!inode)
+ return -EINVAL;
+ if ((dev = MINOR(inode->i_rdev)) >= JSF_MAX) return -ENODEV;
+
+ switch (cmd) {
+ case BLKGETSIZE:
+ return put_user(jsfd_bytesizes[dev] >> 9, (long *) arg);
+
+#if 0
+ case BLKROSET:
+ case BLKROGET:
+ case BLKSSZGET:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+#endif
+
+ /* case BLKFLSBUF: */ /* Program, then read, what happens? Stale? */
+ default: ;
+ }
+ return -ENOTTY;
+}
+
static int jsf_mmap(struct file * file, struct vm_area_struct * vma)
{
return -ENXIO;
@@ -350,6 +483,26 @@ static int jsf_open(struct inode * inode, struct file * filp)
return 0; /* XXX What security? */
}
+static int jsfd_open(struct inode *inode, struct file *file)
+{
+ struct jsfd_part *jdp;
+ int dev;
+
+ if (!inode)
+ return -EINVAL;
+ dev = MINOR(inode->i_rdev);
+ if (dev >= JSF_MAX || (dev & JSF_PART_MASK) >= JSF_NPART) {
+ printk(KERN_ALERT "jsfd_open: illegal minor %d\n", dev);
+ return -ENODEV;
+ }
+
+ jdp = &jsf0.dv[dev];
+ jdp->refcnt++;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
static int jsf_release(struct inode *inode, struct file *file)
{
@@ -359,6 +512,30 @@ static int jsf_release(struct inode *inode, struct file *file)
return 0;
}
+static int jsfd_release(struct inode *inode, struct file *file)
+{
+ struct jsfd_part *jdp;
+ int dev;
+
+ if (!inode)
+ return -ENODEV;
+ dev = MINOR(inode->i_rdev);
+ if (dev >= JSF_MAX || (dev & JSF_PART_MASK) >= JSF_NPART) {
+ printk(KERN_ALERT "jsfd_release: illegal minor %d\n", dev);
+ return -ENODEV;
+ }
+
+ jdp = &jsf0.dv[dev];
+ if (jdp->refcnt <= 0) {
+ printk(KERN_ALERT "jsfd_release: bad ref on minor %d\n", dev);
+ } else {
+ --jdp->refcnt;
+ }
+ /* N.B. Doesn't lo->file need an fput?? */
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
static struct file_operations jsf_fops = {
llseek: jsf_lseek,
read: jsf_read,
@@ -371,37 +548,90 @@ static struct file_operations jsf_fops = {
static struct miscdevice jsf_dev = { JSF_MINOR, "jsflash", &jsf_fops };
+static struct block_device_operations jsfd_fops = {
+ open: jsfd_open,
+ release: jsfd_release,
+ ioctl: jsfd_ioctl,
+};
+
EXPORT_NO_SYMBOLS;
-#ifdef MODULE
-int init_module(void)
-#else
-int __init jsflash_init(void)
-#endif
+int jsflash_init(void)
{
int rc;
+ struct jsflash *jsf;
+ int node;
char banner[128];
+ struct linux_prom_registers reg0;
+
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(node, "flash-memory");
+ if (node != 0 && node != -1) {
+ if (prom_getproperty(node, "reg",
+ (char *)&reg0, sizeof(reg0)) == -1) {
+ printk("jsflash: no \"reg\" property\n");
+ return -ENXIO;
+ }
+ if (reg0.which_io != 0) {
+ printk("jsflash: bus number nonzero: 0x%x:%x\n",
+ reg0.which_io, reg0.phys_addr);
+ return -ENXIO;
+ }
+ /*
+ * Flash may be somewhere else, for instance on Ebus.
+ * So, don't do the following check for IIep flash space.
+ */
+#if 0
+ if ((reg0.phys_addr >> 24) != 0x20) {
+ printk("jsflash: suspicious address: 0x%x:%x\n",
+ reg0.which_io, reg0.phys_addr);
+ return -ENXIO;
+ }
+#endif
+ if ((int)reg0.reg_size <= 0) {
+ printk("jsflash: bad size 0x%x\n", (int)reg0.reg_size);
+ return -ENXIO;
+ }
+ } else {
+ /* XXX Remove this code once PROLL ID12 got widespread */
+ printk("jsflash: no /flash-memory node, use PROLL >= 12\n");
+ prom_getproperty(prom_root_node, "banner-name", banner, 128);
+ if (strcmp (banner, "JavaStation-NC") != 0 &&
+ strcmp (banner, "JavaStation-E") != 0) {
+ return -ENXIO;
+ }
+ reg0.which_io = 0;
+ reg0.phys_addr = 0x20400000;
+ reg0.reg_size = 0x00800000;
+ }
- /* FIXME: Really autodetect things */
- prom_getproperty(prom_root_node, "banner-name", banner, 128);
- if (strcmp (banner, "JavaStation-NC") && strcmp (banner, "JavaStation-E"))
+ /* Let us be really paranoid for modifications to probing code. */
+ /* extern enum sparc_cpu sparc_cpu_model; */ /* in <asm/system.h> */
+ if (sparc_cpu_model != sun4m) {
+ /* We must be on sun4m because we use MMU Bypass ASI. */
return -ENXIO;
+ }
- /* extern enum sparc_cpu sparc_cpu_model; */ /* in <asm/system.h> */
- if (sparc_cpu_model == sun4m && jsf0.base == 0) {
- /* XXX Autodetect */
- /*
- * We do not want to use PROM properties;
- * They are faked by PROLL anyways.
- */
- jsf0.base = JSF_BASE_JK;
- jsf0.size = 0x00800000; /* 8M */
+ if (jsf0.base == 0) {
+ jsf = &jsf0;
+
+ jsf->base = reg0.phys_addr;
+ jsf->size = reg0.reg_size;
+
+ /* XXX Redo the userland interface. */
+ jsf->id.off = JSF_BASE_ALL;
+ jsf->id.size = 0x01000000; /* 16M - all segments */
+ strcpy(jsf->id.name, "Krups_all");
- jsf0.id.off = JSF_BASE_ALL;
- jsf0.id.size = 0x01000000; /* 16M - all segments */
- strcpy(jsf0.id.name, "Krups_all");
+ jsf->dv[0].dbase = jsf->base;
+ jsf->dv[0].dsize = jsf->size;
+ jsf->dv[1].dbase = jsf->base + 1024;
+ jsf->dv[1].dsize = jsf->size - 1024;
+ jsf->dv[2].dbase = JSF_BASE_ALL;
+ jsf->dv[2].dsize = 0x01000000;
- printk("Espresso Flash @0x%lx\n", jsf0.base);
+ printk("Espresso Flash @0x%lx [%d MB]\n", jsf->base,
+ (int) (jsf->size / (1024*1024)));
}
if ((rc = misc_register(&jsf_dev)) != 0) {
@@ -410,12 +640,63 @@ int __init jsflash_init(void)
jsf0.base = 0;
return rc;
}
+
+ return 0;
+}
+
+int jsfd_init(void) {
+ struct jsflash *jsf;
+ struct jsfd_part *jdp;
+ int i;
+
+ if (jsf0.base == 0) {
+ printk("jsfd_init: no flash\n"); /* P3 */
+ return -EIO;
+ }
+
+ if (register_blkdev(JSFD_MAJOR, "jsfd", &jsfd_fops)) {
+ printk("jsfd_init: unable to get major number %d\n",
+ JSFD_MAJOR);
+ return -EIO;
+ }
+
+ printk("jsfd0: at major %d\n", MAJOR_NR); /* P3 */
+
+ blksize_size[JSFD_MAJOR] = jsfd_blksizes;
+ blk_size[JSFD_MAJOR] = jsfd_sizes;
+
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+ /* blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); */
+ for (i = 0; i < JSF_MAX; i++) {
+ if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
+ jsf = &jsf0; /* actually, &jsfv[i >> JSF_PART_BITS] */
+ jdp = &jsf->dv[i&JSF_PART_MASK];
+
+ jdp->refcnt = 0;
+
+ jsfd_blksizes[i] = 1024;
+ jsfd_bytesizes[i] = jdp->dsize;
+ jsfd_sizes[i] = jsfd_bytesizes[i] >> 10;
+ register_disk(NULL, MKDEV(JSFD_MAJOR, i), 1, &jsfd_fops,
+ jsfd_bytesizes[i] >> 9);
+ set_device_ro(MKDEV(JSFD_MAJOR, i), 1);
+ }
return 0;
}
#ifdef MODULE
-void cleanup_module(void)
-{
+
+int init_module(void) {
+ int rc;
+
+ if ((rc = jsflash_init()) == 0) {
+ jsfd_init();
+ return 0;
+ }
+ return rc;
+}
+
+void cleanup_module(void) {
/* for (all probed units) { } */
if (jsf0.busy)
@@ -424,5 +705,7 @@ void cleanup_module(void)
jsf0.busy = 0;
misc_deregister(&jsf_dev);
+ if (unregister_blkdev(JSFD_MAJOR, "jsfd") != 0)
+ printk("jsfd: cleanup_module failed\n");
}
#endif
diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c
index 76b83eacb..d0e88d087 100644
--- a/drivers/sbus/char/pcikbd.c
+++ b/drivers/sbus/char/pcikbd.c
@@ -1,4 +1,4 @@
-/* $Id: pcikbd.c,v 1.44 2000/02/11 04:49:13 davem Exp $
+/* $Id: pcikbd.c,v 1.45 2000/04/24 06:10:19 davem Exp $
* pcikbd.c: Ultra/AX PC keyboard support.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -23,6 +23,7 @@
#include <linux/kbd_ll.h>
#include <linux/kbd_kern.h>
#include <linux/delay.h>
+#include <linux/spinlock.h>
#include <linux/init.h>
#include <asm/ebus.h>
@@ -59,6 +60,8 @@ static volatile unsigned char reply_expected = 0;
static volatile unsigned char acknowledge = 0;
static volatile unsigned char resend = 0;
+static spinlock_t pcikbd_lock = SPIN_LOCK_UNLOCKED;
+
unsigned char pckbd_read_mask = KBD_STAT_OBF;
extern int pcikbd_init(void);
@@ -71,26 +74,18 @@ extern unsigned char pci_getledstate(void);
#define pcikbd_inb(x) inb(x)
#define pcikbd_outb(v,x) outb(v,x)
-#if 0 /* deadwood */
-static __inline__ unsigned char pcikbd_inb(unsigned long port)
-{
- return inb(port);
-}
-
-static __inline__ void pcikbd_outb(unsigned char val, unsigned long port)
-{
- outb(val, port);
-}
-#endif
-
-static inline void kb_wait(void)
+/* Wait for keyboard controller input buffer to drain.
+ * Must be invoked under the pcikbd_lock.
+ */
+static void kb_wait(void)
{
- unsigned long start = jiffies;
+ unsigned long timeout = 250;
do {
if(!(pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF))
return;
- } while (jiffies - start < KBC_TIMEOUT);
+ mdelay(1);
+ } while (--timeout);
}
/*
@@ -312,8 +307,11 @@ char pcikbd_unexpected_up(unsigned char keycode)
static void
pcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
+ unsigned long flags;
unsigned char status;
+ spin_lock_irqsave(&pcikbd_lock, flags);
+
kbd_pt_regs = regs;
status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
do {
@@ -327,27 +325,45 @@ pcikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
} while(status & KBD_STAT_OBF);
tasklet_schedule(&keyboard_tasklet);
+
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
}
static int send_data(unsigned char data)
{
int retries = 3;
- unsigned long start;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcikbd_lock, flags);
do {
+ unsigned long timeout = 1000;
+
kb_wait();
- acknowledge = resend = 0;
+
+ acknowledge = 0;
+ resend = 0;
reply_expected = 1;
+
pcikbd_outb(data, pcikbd_iobase + KBD_DATA_REG);
- start = jiffies;
do {
- if(acknowledge)
- return 1;
- if(jiffies - start >= KBD_TIMEOUT)
- return 0;
- } while(!resend);
- } while(retries-- > 0);
+ if (acknowledge)
+ goto out_ack;
+ if (resend)
+ break;
+ mdelay(1);
+ } while (--timeout);
+ if (timeout == 0)
+ goto out_timeout;
+ } while (retries-- > 0);
+
+out_timeout:
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
return 0;
+
+out_ack:
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+ return 1;
}
void pcikbd_leds(unsigned char leds)
@@ -360,17 +376,23 @@ void pcikbd_leds(unsigned char leds)
static int __init pcikbd_wait_for_input(void)
{
int status, data;
- unsigned long start = jiffies;
+ unsigned long timeout = 1000;
do {
+ mdelay(1);
+
status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG);
- if(!(status & KBD_STAT_OBF))
+ if (!(status & KBD_STAT_OBF))
continue;
+
data = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG);
- if(status & (KBD_STAT_GTO | KBD_STAT_PERR))
+ if (status & (KBD_STAT_GTO | KBD_STAT_PERR))
continue;
+
return (data & 0xff);
- } while(jiffies - start < KBD_INIT_TIMEOUT);
+
+ } while (--timeout > 0);
+
return -1;
}
@@ -573,27 +595,12 @@ struct aux_queue {
};
static struct aux_queue *queue;
-static int aux_ready = 0;
static int aux_count = 0;
static int aux_present = 0;
#define pcimouse_inb(x) inb(x)
#define pcimouse_outb(v,x) outb(v,x)
-#if 0
-
-static __inline__ unsigned char pcimouse_inb(unsigned long port)
-{
- return inb(port);
-}
-
-static __inline__ void pcimouse_outb(unsigned char val, unsigned long port)
-{
- outb(val, port);
-}
-
-#endif
-
/*
* Shared subroutines
*/
@@ -603,11 +610,11 @@ static unsigned int get_from_queue(void)
unsigned int result;
unsigned long flags;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&pcikbd_lock, flags);
result = queue->buf[queue->tail];
queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
- restore_flags(flags);
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+
return result;
}
@@ -645,17 +652,17 @@ static int aux_fasync(int fd, struct file *filp, int on)
static int poll_aux_status(void)
{
- int retries=0;
+ int retries = 0;
while ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) &
(KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) {
if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF)
== AUX_STAT_OBF)
pcimouse_inb(pcimouse_iobase + KBD_DATA_REG);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((5*HZ + 99) / 100);
+ mdelay(5);
retries++;
}
+
return (retries < MAX_RETRIES);
}
@@ -699,51 +706,38 @@ static void aux_write_cmd(int val)
}
/*
- * AUX handler critical section start and end.
- *
- * Only one process can be in the critical section and all keyboard sends are
- * deferred as long as we're inside. This is necessary as we may sleep when
- * waiting for the keyboard controller and other processes / BH's can
- * preempt us. Please note that the input buffer must be flushed when
- * aux_end_atomic() is called and the interrupt is no longer enabled as not
- * doing so might cause the keyboard driver to ignore all incoming keystrokes.
- */
-
-static DECLARE_MUTEX(aux_sema4);
-
-static inline void aux_start_atomic(void)
-{
- down(&aux_sema4);
- tasklet_disable_nosync(&keyboard_tasklet);
- tasklet_unlock_wait(&keyboard_tasklet);
-}
-
-static inline void aux_end_atomic(void)
-{
- tasklet_enable(&keyboard_tasklet);
- up(&aux_sema4);
-}
-
-/*
* Interrupt from the auxiliary device: a character
* is waiting in the keyboard/aux controller.
*/
void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- int head = queue->head;
- int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
+ unsigned long flags;
+ int head, maxhead;
+ unsigned char val;
+
+ spin_lock_irqsave(&pcikbd_lock, flags);
- if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF)
+ head = queue->head;
+ maxhead = (queue->tail - 1) & (AUX_BUF_SIZE - 1);
+
+ if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) !=
+ AUX_STAT_OBF) {
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
return;
+ }
- add_mouse_randomness(queue->buf[head] = pcimouse_inb(pcimouse_iobase + KBD_DATA_REG));
+ val = pcimouse_inb(pcimouse_iobase + KBD_DATA_REG);
+ queue->buf[head] = val;
+ add_mouse_randomness(val);
if (head != maxhead) {
head++;
- head &= AUX_BUF_SIZE-1;
+ head &= AUX_BUF_SIZE - 1;
}
queue->head = head;
- aux_ready = 1;
+
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+
if (queue->fasync)
kill_fasync(queue->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&queue->proc_list);
@@ -751,10 +745,13 @@ void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static int aux_release(struct inode * inode, struct file * file)
{
+ unsigned long flags;
+
aux_fasync(-1, file, 0);
if (--aux_count)
return 0;
- aux_start_atomic();
+
+ spin_lock_irqsave(&pcikbd_lock, flags);
/* Disable controller ints */
aux_write_cmd(AUX_INTS_OFF);
@@ -763,7 +760,8 @@ static int aux_release(struct inode * inode, struct file * file)
/* Disable Aux device */
pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG);
poll_aux_status();
- aux_end_atomic();
+
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
MOD_DEC_USE_COUNT;
return 0;
@@ -776,17 +774,19 @@ static int aux_release(struct inode * inode, struct file * file)
static int aux_open(struct inode * inode, struct file * file)
{
+ unsigned long flags;
+
if (!aux_present)
return -ENODEV;
- aux_start_atomic();
- if (aux_count++) {
- aux_end_atomic();
+ if (aux_count++)
return 0;
- }
- if (!poll_aux_status()) { /* FIXME: Race condition */
+
+ spin_lock_irqsave(&pcikbd_lock, flags);
+
+ if (!poll_aux_status()) {
aux_count--;
- aux_end_atomic();
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
return -EBUSY;
}
queue->head = queue->tail = 0; /* Flush input queue */
@@ -798,9 +798,10 @@ static int aux_open(struct inode * inode, struct file * file)
aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */
aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */
poll_aux_status();
- aux_end_atomic();
- aux_ready = 0;
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+
+
return 0;
}
@@ -812,23 +813,35 @@ static ssize_t aux_write(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
ssize_t retval = 0;
+ unsigned long flags;
if (count) {
ssize_t written = 0;
- aux_start_atomic();
+ spin_lock_irqsave(&pcikbd_lock, flags);
+
do {
char c;
+
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+
+ get_user(c, buffer++);
+
+ spin_lock_irqsave(&pcikbd_lock, flags);
+
if (!poll_aux_status())
break;
- pcimouse_outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);
+ pcimouse_outb(KBD_CCMD_WRITE_MOUSE,
+ pcimouse_iobase + KBD_CNTL_REG);
if (!poll_aux_status())
break;
- get_user(c, buffer++);
+
pcimouse_outb(c, pcimouse_iobase + KBD_DATA_REG);
written++;
} while (--count);
- aux_end_atomic();
+
+ spin_unlock_irqrestore(&pcikbd_lock, flags);
+
retval = -EIO;
if (written) {
retval = written;
@@ -872,7 +885,6 @@ repeat:
put_user(c, buffer++);
i--;
}
- aux_ready = !queue_empty();
if (count-i) {
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return count-i;
@@ -885,7 +897,7 @@ repeat:
static unsigned int aux_poll(struct file *file, poll_table * wait)
{
poll_wait(file, &queue->proc_list, wait);
- if (aux_ready)
+ if (!queue_empty())
return POLLIN | POLLRDNORM;
return 0;
}
@@ -972,7 +984,9 @@ found:
pckbd_read_mask = AUX_STAT_OBF;
misc_register(&psaux_mouse);
- aux_start_atomic();
+
+ spin_lock_irq(&pcikbd_lock);
+
pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG);
aux_write_ack(AUX_RESET);
aux_write_ack(AUX_SET_SAMPLE);
@@ -987,7 +1001,8 @@ found:
poll_aux_status();
pcimouse_outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG);
poll_aux_status();
- aux_end_atomic();
+
+ spin_unlock_irq(&pcikbd_lock);
return 0;
diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c
index b2fc1ffc3..28bcd6919 100644
--- a/drivers/sbus/char/sab82532.c
+++ b/drivers/sbus/char/sab82532.c
@@ -1,4 +1,4 @@
-/* $Id: sab82532.c,v 1.41 2000/03/13 03:54:17 davem Exp $
+/* $Id: sab82532.c,v 1.44 2000/04/26 09:36:32 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -176,12 +176,22 @@ static struct ebrg_struct ebrg_table[] = {
#define NR_EBRG_VALUES (sizeof(ebrg_table)/sizeof(struct ebrg_struct))
-#define SAB82532_MAX_TEC_DELAY 2000 /* 2 ms */
+#define SAB82532_MAX_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */
+#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (at 50 baud) */
static __inline__ void sab82532_tec_wait(struct sab82532 *info)
{
- int count = SAB82532_MAX_TEC_DELAY;
- while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --count)
+ int timeout = info->tec_timeout;
+
+ while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
+ udelay(1);
+}
+
+static __inline__ void sab82532_cec_wait(struct sab82532 *info)
+{
+ int timeout = info->cec_timeout;
+
+ while ((readb(&info->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
udelay(1);
}
@@ -209,8 +219,7 @@ static __inline__ void sab82532_start_tx(struct sab82532 *info)
}
/* Issue a Transmit Frame command. */
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
out:
@@ -277,8 +286,7 @@ static void batten_down_hatches(struct sab82532 *info)
tmp = readb(&info->regs->rw.rfc);
tmp &= ~(SAB82532_RFC_RFDF);
writeb(tmp, &info->regs->rw.rfc);
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
#ifndef __sparc_v9__
@@ -293,8 +301,7 @@ static void batten_down_hatches(struct sab82532 *info)
* Reset FIFO to character + status mode.
*/
writeb(saved_rfc, &info->regs->w.rfc);
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
}
@@ -345,16 +352,19 @@ static inline void receive_chars(struct sab82532 *info,
free_fifo++;
}
- if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
- count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);
- free_fifo++;
- }
-
/* Issue a FIFO read command in case we where idle. */
if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr);
+ /* Wait for command execution, to catch the TCD below. */
+ sab82532_cec_wait(info);
+ }
+
+ if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
+ count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);
+ if (count == 0)
+ count = info->recv_fifo_size;
+ free_fifo++;
}
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
@@ -370,8 +380,7 @@ static inline void receive_chars(struct sab82532 *info,
/* Issue Receive Message Complete command. */
if (free_fifo) {
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr);
}
@@ -451,8 +460,7 @@ static inline void transmit_chars(struct sab82532 *info,
}
/* Issue a Transmit Frame command. */
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
if (info->xmit_cnt < WAKEUP_CHARS)
@@ -709,18 +717,14 @@ sab82532_init_line(struct sab82532 *info)
/*
* Wait for any commands or immediate characters
*/
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
sab82532_tec_wait(info);
/*
* Clear the FIFO buffers.
*/
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XRES, &info->regs->w.cmdr);
/*
@@ -997,10 +1001,15 @@ static void change_speed(struct sab82532 *info)
ebrg |= (ebrg_table[i].m << 6);
info->baud = ebrg_table[i].baud;
- if (info->baud)
+ if (info->baud) {
info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
- else
+ info->tec_timeout = (10 * 1000000) / info->baud;
+ info->cec_timeout = info->tec_timeout >> 2;
+ } else {
info->timeout = 0;
+ info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
+ info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
+ }
info->timeout += HZ / 50; /* Add .02 seconds of slop */
/* CTS flow control flags */
@@ -1037,8 +1046,7 @@ static void change_speed(struct sab82532 *info)
SAB82532_ISR0_TIME;
save_flags(flags); cli();
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
sab82532_tec_wait(info);
writeb(dafo, &info->regs->w.dafo);
writeb(ebrg & 0xff, &info->regs->w.bgr);
@@ -2092,7 +2100,7 @@ int sab82532_read_proc(char *page, char **start, off_t off, int count,
done:
if (off >= len+begin)
return 0;
- *start = page + (begin-off);
+ *start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
}
@@ -2163,7 +2171,7 @@ static void __init sab82532_kgdb_hook(int line)
static inline void __init show_serial_version(void)
{
- char *revision = "$Revision: 1.41 $";
+ char *revision = "$Revision: 1.44 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2196,7 +2204,11 @@ int __init sab82532_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
serial_driver.name = "tts/%d";
+#else
+ serial_driver.name = "ttyS";
+#endif
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + su_num_ports;
serial_driver.num = NR_PORTS;
@@ -2267,6 +2279,8 @@ int __init sab82532_init(void)
info->custom_divisor = 16;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
+ info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
+ info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
info->x_char = 0;
info->event = 0;
info->blocked_open = 0;
@@ -2550,8 +2564,7 @@ sab82532_console_setup(struct console *con, char *options)
info->flags |= ASYNC_CHECK_CD;
save_flags(flags); cli();
- if (readb(&info->regs->r.star) & SAB82532_STAR_CEC)
- udelay(1);
+ sab82532_cec_wait(info);
sab82532_tec_wait(info);
writeb(dafo, &info->regs->w.dafo);
writeb(ebrg & 0xff, &info->regs->w.bgr);
diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c
index a691db430..6df7d9911 100644
--- a/drivers/sbus/char/su.c
+++ b/drivers/sbus/char/su.c
@@ -1,4 +1,4 @@
-/* $Id: su.c,v 1.37 2000/03/13 03:54:15 davem Exp $
+/* $Id: su.c,v 1.38 2000/04/22 00:45:16 davem Exp $
* su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
@@ -2204,7 +2204,7 @@ int su_read_proc(char *page, char **start, off_t off, int count,
done:
if (off >= len+begin)
return 0;
- *start = page + (begin-off);
+ *start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
}
@@ -2223,7 +2223,7 @@ done:
*/
static __inline__ void __init show_su_version(void)
{
- char *revision = "$Revision: 1.37 $";
+ char *revision = "$Revision: 1.38 $";
char *version, *p;
version = strchr(revision, ' ');
diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c
index 91bf24a18..56bbe4035 100644
--- a/drivers/sbus/char/sunkbd.c
+++ b/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 @@ int keyboard_wait_for_keypress(struct console *co)
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 unsigned char sunkbd_clickp;
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 @@ int sun_getkeycode(unsigned int scancode)
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 @@ keyboard_timer (unsigned long ignored)
{
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 @@ DECLARE_TASKLET_DISABLED(keyboard_tasklet, sun_kbd_bh, 0);
#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 @@ out:
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 inline unsigned char getleds(void){
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 @@ void sun_kbd_bh(unsigned long dummy)
/* 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 void sunkbd_kd_mksound(unsigned int hz, unsigned int ticks)
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 @@ static void sunkbd_kd_mksound(unsigned int hz, unsigned int ticks)
} 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 @@ int __init sun_kbd_init(void)
#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 @@ static struct fasync_struct *kb_fasync;
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 @@ static ssize_t
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 @@ kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos)
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 @@ kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos)
/* 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 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg)
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 @@ kbd_open (struct inode *i, struct file *f)
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 @@ void __init keyboard_zsinit(void (*put_char)(unsigned char))
if(sunkbd_type == SUNKBD_TYPE2)
sunkbd_clickp = 0;
+ spin_lock_irq(&sunkbd_lock);
+
if(sunkbd_clickp) {
send_cmd(SKBDCMD_CLICK);
printk("with keyclick\n");
@@ -1549,6 +1604,8 @@ void __init keyboard_zsinit(void (*put_char)(unsigned char))
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,
KBD_MAJOR, 0,
diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c
index f1641a33a..03869c52a 100644
--- a/drivers/sbus/char/sunmouse.c
+++ b/drivers/sbus/char/sunmouse.c
@@ -48,6 +48,7 @@
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/poll.h>
+#include <linux/spinlock.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -69,13 +70,13 @@ struct sun_mouse {
unsigned char prev_state; /* Previous button state */
int delta_x; /* Current delta-x */
int delta_y; /* Current delta-y */
- int ready; /* set if there if data is available */
int active; /* set if device is open */
int vuid_mode; /* VUID_NATIVE or VUID_FIRM_EVENT */
wait_queue_head_t proc_list;
struct fasync_struct *fasync;
/* The event/stream queue */
+ spinlock_t lock;
unsigned int head;
unsigned int tail;
union {
@@ -96,14 +97,22 @@ extern void mouse_put_char(char ch);
static int
push_event (Firm_event *ev)
{
- int next = (sunmouse.head + 1) % EV_SIZE;
+ unsigned long flags;
+ int next, ret;
- if (next != sunmouse.tail){
+ spin_lock_irqsave(&sunmouse.lock, flags);
+
+ next = (sunmouse.head + 1) % EV_SIZE;
+ ret = 0;
+ if (next != sunmouse.tail) {
sunmouse.queue.ev [sunmouse.head] = *ev;
sunmouse.head = next;
- return 1;
+ ret = 1;
}
- return 0;
+
+ spin_unlock_irqrestore(&sunmouse.lock, flags);
+
+ return ret;
}
static int
@@ -112,29 +121,32 @@ queue_empty (void)
return sunmouse.head == sunmouse.tail;
}
-static Firm_event *
-get_from_queue (void)
+/* Must be invoked under the sunmouse.lock */
+static void get_from_queue (Firm_event *p)
{
- Firm_event *result;
-
- result = &sunmouse.queue.ev [sunmouse.tail];
+ *p = sunmouse.queue.ev [sunmouse.tail];
sunmouse.tail = (sunmouse.tail + 1) % EV_SIZE;
- return result;
}
static void
push_char (char c)
{
- int next = (sunmouse.head + 1) % STREAM_SIZE;
+ unsigned long flags;
+ int next;
+
+ spin_lock_irqsave(&sunmouse.lock, flags);
- if (next != sunmouse.tail){
+ next = (sunmouse.head + 1) % STREAM_SIZE;
+ if (next != sunmouse.tail) {
#ifdef SMOUSE_DEBUG
printk("P<%02x>\n", (unsigned char)c);
#endif
sunmouse.queue.stream [sunmouse.head] = c;
sunmouse.head = next;
}
- sunmouse.ready = 1;
+
+ spin_unlock_irqrestore(&sunmouse.lock, flags);
+
if (sunmouse.fasync)
kill_fasync (sunmouse.fasync, SIGIO, POLL_IN);
wake_up_interruptible (&sunmouse.proc_list);
@@ -325,24 +337,26 @@ sun_mouse_inbyte(unsigned char byte, int is_break)
sunmouse.byte = 69; /* What could cause this? */
return;
};
- if (!gen_events){
+
+ if (!gen_events) {
push_char (~sunmouse.button_state & 0x87);
push_char (sunmouse.delta_x);
push_char (sunmouse.delta_y);
return;
}
+
d = bstate ^ pstate;
pstate = bstate;
- if (d){
- if (d & BUTTON_LEFT){
+ if (d) {
+ if (d & BUTTON_LEFT) {
ev.id = MS_LEFT;
ev.value = bstate & BUTTON_LEFT;
}
- if (d & BUTTON_RIGHT){
+ if (d & BUTTON_RIGHT) {
ev.id = MS_RIGHT;
ev.value = bstate & BUTTON_RIGHT;
}
- if (d & BUTTON_MIDDLE){
+ if (d & BUTTON_MIDDLE) {
ev.id = MS_MIDDLE;
ev.value = bstate & BUTTON_MIDDLE;
}
@@ -350,25 +364,24 @@ sun_mouse_inbyte(unsigned char byte, int is_break)
ev.value = ev.value ? VKEY_DOWN : VKEY_UP;
pushed += push_event (&ev);
}
- if (sunmouse.delta_x){
+ if (sunmouse.delta_x) {
ev.id = LOC_X_DELTA;
ev.time = xtime;
ev.value = sunmouse.delta_x;
pushed += push_event (&ev);
sunmouse.delta_x = 0;
}
- if (sunmouse.delta_y){
+ if (sunmouse.delta_y) {
ev.id = LOC_Y_DELTA;
ev.time = xtime;
ev.value = sunmouse.delta_y;
pushed += push_event (&ev);
}
- if(pushed != 0) {
+ if (pushed != 0) {
/* We just completed a transaction, wake up whoever is awaiting
* this event.
*/
- sunmouse.ready = 1;
if (sunmouse.fasync)
kill_fasync (sunmouse.fasync, SIGIO, POLL_IN);
wake_up_interruptible(&sunmouse.proc_list);
@@ -379,9 +392,9 @@ sun_mouse_inbyte(unsigned char byte, int is_break)
static int
sun_mouse_open(struct inode * inode, struct file * file)
{
- if(sunmouse.active++)
+ if (sunmouse.active++)
return 0;
- sunmouse.ready = sunmouse.delta_x = sunmouse.delta_y = 0;
+ sunmouse.delta_x = sunmouse.delta_y = 0;
sunmouse.button_state = 0x80;
sunmouse.vuid_mode = VUID_NATIVE;
return 0;
@@ -401,9 +414,7 @@ static int
sun_mouse_close(struct inode *inode, struct file *file)
{
sun_mouse_fasync (-1, file, 0);
- if (--sunmouse.active)
- return 0;
- sunmouse.ready = 0;
+ sunmouse.active--;
return 0;
}
@@ -419,49 +430,57 @@ sun_mouse_read(struct file *file, char *buffer,
size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
- if (queue_empty ()){
+ if (queue_empty ()) {
if (file->f_flags & O_NONBLOCK)
return -EWOULDBLOCK;
add_wait_queue (&sunmouse.proc_list, &wait);
- while (queue_empty () && !signal_pending(current)) {
- current->state = TASK_INTERRUPTIBLE;
- schedule ();
+repeat:
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (queue_empty() && !signal_pending(current)) {
+ schedule();
+ goto repeat;
}
current->state = TASK_RUNNING;
remove_wait_queue (&sunmouse.proc_list, &wait);
}
- if (gen_events){
+ if (gen_events) {
char *p = buffer, *end = buffer+count;
+ spin_lock_irqsave(&sunmouse.lock, flags);
while (p < end && !queue_empty ()){
+ Firm_event this_event;
+
+ get_from_queue(&this_event);
+ spin_unlock_irqrestore(&sunmouse.lock, flags);
+
#ifdef CONFIG_SPARC32_COMPAT
if (current->thread.flags & SPARC_FLAG_32BIT) {
- Firm_event *q = get_from_queue();
-
if ((end - p) <
((sizeof(Firm_event) - sizeof(struct timeval) +
(sizeof(u32) * 2))))
break;
- copy_to_user_ret((Firm_event *)p, q,
+ 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(q->time.tv_sec, (u32 *)p, -EFAULT);
+ __put_user_ret(this_event.time.tv_sec, (u32 *)p, -EFAULT);
p += sizeof(u32);
- __put_user_ret(q->time.tv_usec, (u32 *)p, -EFAULT);
+ __put_user_ret(this_event.time.tv_usec, (u32 *)p, -EFAULT);
p += sizeof(u32);
} else
#endif
{
if ((end - p) < sizeof(Firm_event))
break;
- copy_to_user_ret((Firm_event *)p, get_from_queue(),
+ copy_to_user_ret((Firm_event *)p, &this_event,
sizeof(Firm_event), -EFAULT);
p += sizeof (Firm_event);
}
+ spin_lock_irqsave(&sunmouse.lock, flags);
}
- sunmouse.ready = !queue_empty ();
+ spin_unlock_irqrestore(&sunmouse.lock, flags);
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return p-buffer;
} else {
@@ -470,9 +489,24 @@ sun_mouse_read(struct file *file, char *buffer,
if (count < limit)
limit = count;
for (c = 0; c < limit; c++) {
- put_user(sunmouse.queue.stream[sunmouse.tail], buffer);
+ unsigned char val;
+ int empty = 0;
+
+ spin_lock_irqsave(&sunmouse.lock, flags);
+ if (queue_empty()) {
+ empty = 1;
+ val = 0;
+ } else {
+ val = sunmouse.queue.stream[sunmouse.tail];
+ sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
+ }
+ spin_unlock_irqrestore(&sunmouse.lock, flags);
+
+ if (empty)
+ break;
+
+ put_user(val, buffer);
buffer++;
- sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
}
while (c < count) {
if (c >= 5)
@@ -481,7 +515,6 @@ sun_mouse_read(struct file *file, char *buffer,
buffer++;
c++;
}
- sunmouse.ready = !queue_empty();
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return c;
}
@@ -494,7 +527,7 @@ sun_mouse_read(struct file *file, char *buffer,
static unsigned int sun_mouse_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &sunmouse.proc_list, wait);
- if(sunmouse.ready)
+ if(!queue_empty())
return POLLIN | POLLRDNORM;
return 0;
}
@@ -516,8 +549,11 @@ sun_mouse_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsig
int value;
get_user_ret(value, (int *)arg, -EFAULT);
+
+ spin_lock_irq(&sunmouse.lock);
sunmouse.vuid_mode = value;
sunmouse.head = sunmouse.tail = 0;
+ spin_unlock_irq(&sunmouse.lock);
} else
return -EINVAL;
break;
@@ -556,10 +592,11 @@ void sun_mouse_zsinit(void)
{
printk("Sun Mouse-Systems mouse driver version 1.00\n");
- sunmouse.ready = sunmouse.active = 0;
+ sunmouse.active = 0;
misc_register (&sun_mouse_mouse);
sunmouse.delta_x = sunmouse.delta_y = 0;
sunmouse.button_state = 0x80;
init_waitqueue_head(&sunmouse.proc_list);
+ spin_lock_init(&sunmouse.lock);
sunmouse.byte = 69;
}
diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c
index 868193747..fb687102c 100644
--- a/drivers/sbus/char/zs.c
+++ b/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.56 2000/03/12 04:02:11 davem Exp $
+/* $Id: zs.c,v 1.57 2000/04/26 09:36:32 davem Exp $
* zs.c: Zilog serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -1928,7 +1928,7 @@ int zs_open(struct tty_struct *tty, struct file * filp)
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.56 $";
+ char *revision = "$Revision: 1.57 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2415,7 +2415,11 @@ int __init zs_init(void)
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
serial_driver.name = "tts/%d";
+#else
+ serial_driver.name = "ttyS";
+#endif
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NUM_CHANNELS;