patch-2.1.97 linux/drivers/macintosh/adb.c
Next file: linux/drivers/macintosh/ati-gx.h
Previous file: linux/drivers/macintosh/Makefile
Back to the patch index
Back to the overall index
- Lines: 451
- Date:
Tue Apr 14 17:33:59 1998
- Orig file:
v2.1.96/linux/drivers/macintosh/adb.c
- Orig date:
Mon Jan 12 15:18:13 1998
diff -u --recursive --new-file v2.1.96/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
@@ -10,18 +10,23 @@
#include <linux/malloc.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/sched.h>
#include <asm/prom.h>
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/uaccess.h>
#include <asm/hydra.h>
enum adb_hw adb_hardware;
int (*adb_send_request)(struct adb_request *req, int sync);
int (*adb_autopoll)(int on);
+static void adb_scan_bus(void);
static struct adb_handler {
void (*handler)(unsigned char *, int, struct pt_regs *, int);
+ int original_address;
+ int handler_id;
} adb_handler[16];
static int adb_nodev(void)
@@ -29,17 +34,123 @@
return -1;
}
+#if 0
+static void printADBreply(struct adb_request *req)
+{
+ int i;
+
+ printk("adb reply (%d)", req->reply_len);
+ for(i = 0; i < req->reply_len; i++)
+ printk(" %x", req->reply[i]);
+ printk("\n");
+
+}
+#endif
+
+static void adb_scan_bus(void)
+{
+ int i, highFree=0, noMovement;
+ struct adb_request req;
+
+ /* reset ADB bus */
+ /*adb_request(&req, NULL, ADBREQ_SYNC, 1, 0);*/
+
+ /* assumes adb_handler[] is all zeroes at this point */
+ for (i = 1; i < 16; i++) {
+ /* see if there is anything at address i */
+ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+ (i << 4) | 0xf);
+ if (req.reply_len > 1)
+ /* one or more devices at this address */
+ adb_handler[i].original_address = i;
+ else if (i > highFree)
+ highFree = i;
+ }
+
+ /* Note we reset noMovement to 0 each time we move a device */
+ for (noMovement = 1; noMovement < 2 && highFree > 0; noMovement++) {
+ for (i = 1; i < 16; i++) {
+ if (adb_handler[i].original_address == 0)
+ continue;
+ /*
+ * Send a "talk register 3" command to address i
+ * to provoke a collision if there is more than
+ * one device at this address.
+ */
+ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+ (i << 4) | 0xf);
+ /*
+ * Move the device(s) which didn't detect a
+ * collision to address `highFree'. Hopefully
+ * this only moves one device.
+ */
+ adb_request(&req, NULL, ADBREQ_SYNC, 3,
+ (i<< 4) | 0xb, (highFree | 0x60), 0xfe);
+ /*
+ * Test whether there are any device(s) left
+ * at address i.
+ */
+ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+ (i << 4) | 0xf);
+ if (req.reply_len > 1) {
+ /*
+ * There are still one or more devices
+ * left at address i. Register the one(s)
+ * we moved to `highFree', and find a new
+ * value for highFree.
+ */
+ adb_handler[highFree].original_address =
+ adb_handler[i].original_address;
+ while (highFree > 0 &&
+ adb_handler[highFree].original_address)
+ highFree--;
+ if (highFree <= 0)
+ break;
+
+ noMovement = 0;
+ }
+ else {
+ /*
+ * No devices left at address i; move the
+ * one(s) we moved to `highFree' back to i.
+ */
+ adb_request(&req, NULL, ADBREQ_SYNC, 3,
+ (highFree << 4) | 0xb,
+ (i | 0x60), 0xfe);
+ }
+ }
+ }
+
+ /* Now fill in the handler_id field of the adb_handler entries. */
+ printk(KERN_DEBUG "adb devices:");
+ for (i = 1; i < 16; i++) {
+ if (adb_handler[i].original_address == 0)
+ continue;
+ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
+ (i << 4) | 0xf);
+ adb_handler[i].handler_id = req.reply[2];
+ printk(" [%d]: %d %x", i, adb_handler[i].original_address,
+ adb_handler[i].handler_id);
+ }
+ printk("\n");
+}
+
void adb_init(void)
{
adb_hardware = ADB_NONE;
adb_send_request = (void *) adb_nodev;
adb_autopoll = (void *) adb_nodev;
+ if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+ return;
via_cuda_init();
+ via_pmu_init();
macio_adb_init();
if (adb_hardware == ADB_NONE)
printk(KERN_WARNING "Warning: no ADB interface detected\n");
- else
+ else {
+ adb_scan_bus();
adb_autopoll(1);
+ }
}
int
@@ -67,12 +178,25 @@
/* Ultimately this should return the number of devices with
the given default id. */
int
-adb_register(int default_id,
+adb_register(int default_id, int handler_id, struct adb_ids *ids,
void (*handler)(unsigned char *, int, struct pt_regs *, int))
{
- if (adb_handler[default_id].handler != 0)
- panic("Two handlers for ADB device %d\n", default_id);
- adb_handler[default_id].handler = handler;
+ int i;
+
+ ids->nids = 0;
+ for (i = 1; i < 16; i++) {
+ if ((adb_handler[i].original_address == default_id) ||
+ (adb_handler[i].handler_id == handler_id)) {
+ if (adb_handler[i].handler != 0) {
+ printk(KERN_ERR
+ "Two handlers for ADB device %d\n",
+ default_id);
+ return 0;
+ }
+ adb_handler[i].handler = handler;
+ ids->id[ids->nids++] = i;
+ }
+ }
return 1;
}
@@ -103,44 +227,40 @@
extern void adbdev_init(void);
struct adbdev_state {
- struct adb_request req;
+ spinlock_t lock;
+ atomic_t n_pending;
+ struct adb_request *completed;
+ struct wait_queue *wait_queue;
+ int inuse;
};
-static struct wait_queue *adb_wait;
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
- int ret = 0;
- struct wait_queue wait = { current, NULL };
-
- add_wait_queue(&adb_wait, &wait);
- current->state = TASK_INTERRUPTIBLE;
-
- while (!state->req.complete) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- }
-
- current->state = TASK_RUNNING;
- remove_wait_queue(&adb_wait, &wait);
-
- return ret;
-}
-
static void adb_write_done(struct adb_request *req)
{
+ struct adbdev_state *state = (struct adbdev_state *) req->arg;
+ unsigned long flags;
+
if (!req->complete) {
req->reply_len = 0;
req->complete = 1;
}
- wake_up_interruptible(&adb_wait);
+ spin_lock_irqsave(&state->lock, flags);
+ atomic_dec(&state->n_pending);
+ if (!state->inuse) {
+ kfree(req);
+ if (atomic_read(&state->n_pending) == 0) {
+ spin_unlock_irqrestore(&state->lock, flags);
+ kfree(state);
+ return;
+ }
+ } else {
+ struct adb_request **ap = &state->completed;
+ while (*ap != NULL)
+ ap = &(*ap)->next;
+ req->next = NULL;
+ *ap = req;
+ wake_up_interruptible(&state->wait_queue);
+ }
+ spin_unlock_irqrestore(&state->lock, flags);
}
static int adb_open(struct inode *inode, struct file *file)
@@ -153,20 +273,31 @@
if (state == 0)
return -ENOMEM;
file->private_data = state;
- state->req.reply_expected = 0;
+ spin_lock_init(&state->lock);
+ atomic_set(&state->n_pending, 0);
+ state->completed = NULL;
+ state->wait_queue = NULL;
+ state->inuse = 1;
+
return 0;
}
static int adb_release(struct inode *inode, struct file *file)
{
struct adbdev_state *state = file->private_data;
+ unsigned long flags;
if (state) {
file->private_data = NULL;
- if (state->req.reply_expected && !state->req.complete)
- if (adb_wait_reply(state, file))
- return 0;
- kfree(state);
+ spin_lock_irqsave(&state->lock, flags);
+ if (atomic_read(&state->n_pending) == 0
+ && state->completed == NULL) {
+ spin_unlock_irqrestore(&state->lock, flags);
+ kfree(state);
+ } else {
+ state->inuse = 0;
+ spin_unlock_irqrestore(&state->lock, flags);
+ }
}
return 0;
}
@@ -181,27 +312,57 @@
{
int ret;
struct adbdev_state *state = file->private_data;
+ struct adb_request *req;
+ struct wait_queue wait = { current, NULL };
+ unsigned long flags;
if (count < 2)
return -EINVAL;
- if (count > sizeof(state->req.reply))
- count = sizeof(state->req.reply);
+ if (count > sizeof(req->reply))
+ count = sizeof(req->reply);
ret = verify_area(VERIFY_WRITE, buf, count);
if (ret)
return ret;
- if (!state->req.reply_expected)
- return 0;
+ req = NULL;
+ add_wait_queue(&state->wait_queue, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ for (;;) {
+ spin_lock_irqsave(&state->lock, flags);
+ req = state->completed;
+ if (req != NULL)
+ state->completed = req->next;
+ else if (atomic_read(&state->n_pending) == 0)
+ ret = -EIO;
+ spin_unlock_irqrestore(&state->lock, flags);
+ if (req != NULL || ret != 0)
+ break;
+
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&state->wait_queue, &wait);
- ret = adb_wait_reply(state, file);
if (ret)
return ret;
- state->req.reply_expected = 0;
- ret = state->req.reply_len;
- if (copy_to_user(buf, state->req.reply, ret))
- return -EFAULT;
+ ret = req->reply_len;
+ if (ret > count)
+ ret = count;
+ if (ret > 0 && copy_to_user(buf, req->reply, ret))
+ ret = -EFAULT;
+ kfree(req);
return ret;
}
@@ -210,46 +371,64 @@
{
int ret, i;
struct adbdev_state *state = file->private_data;
+ struct adb_request *req;
- if (count < 2 || count > sizeof(state->req.data))
+ if (count < 2 || count > sizeof(req->data))
return -EINVAL;
ret = verify_area(VERIFY_READ, buf, count);
if (ret)
return ret;
- if (state->req.reply_expected && !state->req.complete) {
- /* A previous request is still being processed.
- Wait for it to finish. */
- ret = adb_wait_reply(state, file);
- if (ret)
- return ret;
- }
+ req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
+ GFP_KERNEL);
+ if (req == NULL)
+ return -ENOMEM;
- state->req.nbytes = count;
- state->req.done = adb_write_done;
- state->req.complete = 0;
- if (copy_from_user(state->req.data, buf, count))
- return -EFAULT;
+ req->nbytes = count;
+ req->done = adb_write_done;
+ req->arg = (void *) state;
+ req->complete = 0;
+
+ ret = -EFAULT;
+ if (copy_from_user(req->data, buf, count))
+ goto out;
+ atomic_inc(&state->n_pending);
switch (adb_hardware) {
case ADB_NONE:
- return -ENXIO;
+ ret = -ENXIO;
+ break;
case ADB_VIACUDA:
- state->req.reply_expected = 1;
- cuda_send_request(&state->req);
+ req->reply_expected = 1;
+ ret = cuda_send_request(req);
break;
+ case ADB_VIAPMU:
+ if (req->data[0] != ADB_PACKET) {
+ ret = pmu_send_request(req);
+ break;
+ }
+ /* else fall through */
default:
- if (state->req.data[0] != ADB_PACKET)
- return -EINVAL;
- for (i = 1; i < state->req.nbytes; ++i)
- state->req.data[i] = state->req.data[i+1];
- state->req.reply_expected =
- ((state->req.data[0] & 0xc) == 0xc);
- adb_send_request(&state->req, 0);
+ ret = -EINVAL;
+ if (req->data[0] != ADB_PACKET)
+ break;
+ for (i = 0; i < req->nbytes-1; ++i)
+ req->data[i] = req->data[i+1];
+ req->nbytes--;
+ req->reply_expected = ((req->data[0] & 0xc) == 0xc);
+ ret = adb_send_request(req, 0);
break;
}
+ if (ret != 0) {
+ atomic_dec(&state->n_pending);
+ goto out;
+ }
return count;
+
+out:
+ kfree(req);
+ return ret;
}
static struct file_operations adb_fops = {
@@ -266,6 +445,8 @@
void adbdev_init()
{
+ if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+ return;
if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov