patch-2.4.10 linux/drivers/macintosh/adb.c
Next file: linux/drivers/macintosh/adbhid.c
Previous file: linux/drivers/macintosh/Makefile
Back to the patch index
Back to the overall index
- Lines: 331
- Date:
Tue Sep 18 14:23:14 2001
- Orig file:
v2.4.9/linux/drivers/macintosh/adb.c
- Orig date:
Mon Aug 27 12:41:42 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
@@ -13,6 +13,7 @@
* - /proc/adb to list the devices and infos
* - more /dev/adb to allow userland to receive the
* flow of auto-polling datas from a given device.
+ * - move bus probe to a kernel thread
*/
#include <linux/config.h>
@@ -32,6 +33,7 @@
#include <linux/notifier.h>
#include <linux/wait.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
#ifdef CONFIG_PPC
#include <asm/prom.h>
@@ -74,6 +76,11 @@
struct notifier_block *adb_client_list = NULL;
static int adb_got_sleep = 0;
static int adb_inited = 0;
+static pid_t adb_probe_task_pid;
+static int adb_probe_task_flag;
+static wait_queue_head_t adb_probe_task_wq;
+static int sleepy_trackpad;
+int __adb_probe_sync;
#ifdef CONFIG_PMAC_PBOOK
static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when);
@@ -84,6 +91,9 @@
#endif
static int adb_scan_bus(void);
+static int do_adb_reset_bus(void);
+static void adbdev_init(void);
+
static struct adb_handler {
void (*handler)(unsigned char *, int, struct pt_regs *, int);
@@ -104,6 +114,17 @@
}
#endif
+
+static __inline__ void adb_wait_ms(unsigned int ms)
+{
+ if (current->pid && adb_probe_task_pid &&
+ adb_probe_task_pid == current->pid) {
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms * HZ / 1000);
+ } else
+ mdelay(ms);
+}
+
static int adb_scan_bus(void)
{
int i, highFree=0, noMovement;
@@ -201,6 +222,64 @@
return devmask;
}
+/*
+ * This kernel task handles ADB probing. It dies once probing is
+ * completed.
+ */
+static int
+adb_probe_task(void *x)
+{
+ strcpy(current->comm, "kadbprobe");
+
+ spin_lock_irq(¤t->sigmask_lock);
+ sigfillset(¤t->blocked);
+ flush_signals(current);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ printk(KERN_INFO "adb: starting probe task...\n");
+ do_adb_reset_bus();
+ printk(KERN_INFO "adb: finished probe task...\n");
+
+ adb_probe_task_pid = 0;
+ clear_bit(0, &adb_probe_task_flag);
+ return 0;
+}
+
+static void
+__adb_probe_task(void *data)
+{
+ adb_probe_task_pid = kernel_thread(adb_probe_task, NULL,
+ SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+}
+
+int
+adb_reset_bus(void)
+{
+ static struct tq_struct tqs = {
+ routine: __adb_probe_task,
+ };
+
+ if (__adb_probe_sync) {
+ do_adb_reset_bus();
+ return 0;
+ }
+
+ /* We need to get a lock on the probe thread */
+ while (test_and_set_bit(0, &adb_probe_task_flag))
+ schedule();
+
+ /* Just wait for PID to be 0 just in case (possible race) */
+ while (adb_probe_task_pid != 0)
+ schedule();
+
+ /* Create probe thread as a child of keventd */
+ if (current_is_keventd())
+ __adb_probe_task(NULL);
+ else
+ schedule_task(&tqs);
+ return 0;
+}
+
int __init adb_init(void)
{
struct adb_driver *driver;
@@ -231,11 +310,16 @@
}
if ((adb_controller == NULL) || adb_controller->init()) {
printk(KERN_WARNING "Warning: no ADB interface detected\n");
+ adb_controller = NULL;
} else {
#ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&adb_sleep_notifier);
#endif /* CONFIG_PMAC_PBOOK */
-
+ if (machine_is_compatible("AAPL,PowerBook1998") ||
+ machine_is_compatible("PowerBook1,1"))
+ sleepy_trackpad = 1;
+ init_waitqueue_head(&adb_probe_task_wq);
+ adbdev_init();
adb_reset_bus();
}
return 0;
@@ -255,6 +339,12 @@
switch (when) {
case PBOOK_SLEEP_REQUEST:
adb_got_sleep = 1;
+ /* We need to get a lock on the probe thread */
+ while (test_and_set_bit(0, &adb_probe_task_flag))
+ schedule();
+ /* Just wait for PID to be 0 just in case (possible race) */
+ while (adb_probe_task_pid != 0)
+ schedule();
if (adb_controller->autopoll)
adb_controller->autopoll(0);
ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL);
@@ -264,6 +354,7 @@
case PBOOK_SLEEP_REJECT:
if (adb_got_sleep) {
adb_got_sleep = 0;
+ clear_bit(0, &adb_probe_task_flag);
adb_reset_bus();
}
break;
@@ -271,16 +362,17 @@
case PBOOK_SLEEP_NOW:
break;
case PBOOK_WAKE:
- adb_reset_bus();
adb_got_sleep = 0;
+ clear_bit(0, &adb_probe_task_flag);
+ adb_reset_bus();
break;
}
return PBOOK_SLEEP_OK;
}
#endif /* CONFIG_PMAC_PBOOK */
-int
-adb_reset_bus(void)
+static int
+do_adb_reset_bus(void)
{
int ret, nret, devs;
unsigned long flags;
@@ -298,16 +390,27 @@
return -EBUSY;
}
+ if (sleepy_trackpad) {
+ /* Let the trackpad settle down */
+ adb_wait_ms(500);
+ }
+
save_flags(flags);
cli();
memset(adb_handler, 0, sizeof(adb_handler));
restore_flags(flags);
-
+
+ /* That one is still a bit synchronous, oh well... */
if (adb_controller->reset_bus)
ret = adb_controller->reset_bus();
else
ret = 0;
+ if (sleepy_trackpad) {
+ /* Let the trackpad settle down */
+ adb_wait_ms(1500);
+ }
+
if (!ret) {
devs = adb_scan_bus();
if (adb_controller->autopoll)
@@ -329,24 +432,40 @@
adb_controller->poll();
}
+static void
+adb_probe_wakeup(struct adb_request *req)
+{
+ wake_up(&adb_probe_task_wq);
+}
+
+static struct adb_request adb_sreq;
+static int adb_sreq_lock; // Use semaphore ! */
int
adb_request(struct adb_request *req, void (*done)(struct adb_request *),
int flags, int nbytes, ...)
{
va_list list;
- int i;
- struct adb_request sreq;
+ int i, use_sreq;
+ int rc;
if ((adb_controller == NULL) || (adb_controller->send_request == NULL))
return -ENXIO;
if (nbytes < 1)
return -EINVAL;
+ if (req == NULL && (flags & ADBREQ_NOSEND))
+ return -EINVAL;
if (req == NULL) {
- req = &sreq;
+ if (test_and_set_bit(0,&adb_sreq_lock)) {
+ printk("adb.c: Warning: contention on static request !\n");
+ return -EPERM;
+ }
+ req = &adb_sreq;
flags |= ADBREQ_SYNC;
- }
+ use_sreq = 1;
+ } else
+ use_sreq = 0;
req->nbytes = nbytes+1;
req->done = done;
req->reply_expected = flags & ADBREQ_REPLY;
@@ -359,7 +478,36 @@
if (flags & ADBREQ_NOSEND)
return 0;
- return adb_controller->send_request(req, flags & ADBREQ_SYNC);
+ /* Synchronous requests send from the probe thread cause it to
+ * block. Beware that the "done" callback will be overriden !
+ */
+ if ((flags & ADBREQ_SYNC) &&
+ (current->pid && adb_probe_task_pid &&
+ adb_probe_task_pid == current->pid)) {
+ DECLARE_WAITQUEUE(wait, current);
+ req->done = adb_probe_wakeup;
+ add_wait_queue(&adb_probe_task_wq, &wait);
+ rc = adb_controller->send_request(req, 0);
+ if (rc || req->complete)
+ goto bail;
+ for (;;) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (req->complete)
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&adb_probe_task_wq, &wait);
+ rc = 0;
+ goto bail;
+ }
+
+ rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);
+bail:
+ if (use_sreq)
+ clear_bit(0, &adb_sreq_lock);
+
+ return rc;
}
/* Ultimately this should return the number of devices with
@@ -463,8 +611,6 @@
#define ADB_MAJOR 56 /* major number for /dev/adb */
-extern void adbdev_init(void);
-
struct adbdev_state {
spinlock_t lock;
atomic_t n_pending;
@@ -636,7 +782,7 @@
the controller */
if ((req->data[0] == ADB_PACKET)&&(count > 1)
&&(req->data[1] == ADB_BUSRESET)) {
- ret = adb_reset_bus();
+ ret = do_adb_reset_bus();
atomic_dec(&state->n_pending);
goto out;
} else {
@@ -667,17 +813,9 @@
release: adb_release,
};
-void adbdev_init()
+static void
+adbdev_init(void)
{
-#ifdef CONFIG_PPC
- if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
- return;
-#endif
-#ifdef CONFIG_MAC
- if (!MACH_IS_MAC)
- return;
-#endif
-
if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops))
printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
else
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)