📄 adb.c
字号:
/* * Device driver for the Apple Desktop Bus * and the /dev/adb device on macintoshes. * * Copyright (C) 1996 Paul Mackerras. * * Modified to declare controllers as structures, added * client notification of bus reset and handles PowerBook * sleep, by Benjamin Herrenschmidt. * * To do: * * - /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>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/devfs_fs_kernel.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <linux/adb.h>#include <linux/cuda.h>#include <linux/pmu.h>#include <linux/notifier.h>#include <linux/wait.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/completion.h>#include <asm/uaccess.h>#ifdef CONFIG_PPC#include <asm/prom.h>#include <asm/hydra.h>#endifEXPORT_SYMBOL(adb_controller);EXPORT_SYMBOL(adb_client_list);extern struct adb_driver via_macii_driver;extern struct adb_driver via_maciisi_driver;extern struct adb_driver via_cuda_driver;extern struct adb_driver adb_iop_driver;extern struct adb_driver via_pmu_driver;extern struct adb_driver macio_adb_driver;static struct adb_driver *adb_driver_list[] = {#ifdef CONFIG_ADB_MACII &via_macii_driver,#endif#ifdef CONFIG_ADB_MACIISI &via_maciisi_driver,#endif#ifdef CONFIG_ADB_CUDA &via_cuda_driver,#endif#ifdef CONFIG_ADB_IOP &adb_iop_driver,#endif#ifdef CONFIG_ADB_PMU &via_pmu_driver,#endif#ifdef CONFIG_ADB_MACIO &macio_adb_driver,#endif NULL};struct adb_driver *adb_controller;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 struct completion adb_probe_task_comp;static int sleepy_trackpad;int __adb_probe_sync;#ifdef CONFIG_PMAC_PBOOKstatic int adb_notify_sleep(struct pmu_sleep_notifier *self, int when);static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, SLEEP_LEVEL_ADB,};#endifstatic 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); int original_address; int handler_id;} adb_handler[16];#if 0static 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");}#endifstatic __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; int devmask = 0; struct adb_request req; /* 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); /* * See if anybody actually moved. This is suggested * by HW TechNote 01: * * http://developer.apple.com/technotes/hw/hw_01.html */ adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, (highFree << 4) | 0xf); if (req.reply_len <= 1) continue; /* * 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); devmask |= 1 << i; } printk("\n"); return devmask;}/* * This kernel task handles ADB probing. It dies once probing is * completed. */static intadb_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);}intadb_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; int i;#ifdef CONFIG_PPC if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) return 0;#endif#ifdef CONFIG_MAC if (!MACH_IS_MAC) return 0;#endif /* xmon may do early-init */ if (adb_inited) return 0; adb_inited = 1; adb_controller = NULL; i = 0; while ((driver = adb_driver_list[i++]) != NULL) { if (!driver->probe()) { adb_controller = driver; break; } } 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_completion(&adb_probe_task_comp); adbdev_init(); adb_reset_bus(); } return 0;}__initcall(adb_init);#ifdef CONFIG_PMAC_PBOOK/* * notify clients before sleep and reset bus afterwards */intadb_notify_sleep(struct pmu_sleep_notifier *self, int when){ int ret; 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); if (ret & NOTIFY_STOP_MASK) return PBOOK_SLEEP_REFUSE; break; case PBOOK_SLEEP_REJECT: if (adb_got_sleep) { adb_got_sleep = 0; clear_bit(0, &adb_probe_task_flag); adb_reset_bus(); } break; case PBOOK_SLEEP_NOW: break; case PBOOK_WAKE: adb_got_sleep = 0; clear_bit(0, &adb_probe_task_flag); adb_reset_bus(); break; } return PBOOK_SLEEP_OK;}#endif /* CONFIG_PMAC_PBOOK */static intdo_adb_reset_bus(void){ int ret, nret, devs; unsigned long flags; if (adb_controller == NULL) return -ENXIO; if (adb_controller->autopoll) adb_controller->autopoll(0); nret = notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL); if (nret & NOTIFY_STOP_MASK) { if (adb_controller->autopoll) adb_controller->autopoll(devs); 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -