adb.c
来自「linux 内核源代码」· C语言 代码 · 共 880 行 · 第 1/2 页
C
880 行
/* * 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: * * - /sys/bus/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/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/fs.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/spinlock.h>#include <linux/completion.h>#include <linux/device.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#ifdef CONFIG_PPC#include <asm/prom.h>#include <asm/machdep.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#if defined(CONFIG_ADB_PMU) || defined(CONFIG_ADB_PMU68K) &via_pmu_driver,#endif#ifdef CONFIG_ADB_MACIO &macio_adb_driver,#endif NULL};static struct class *adb_dev_class;struct adb_driver *adb_controller;BLOCKING_NOTIFIER_HEAD(adb_client_list);static int adb_got_sleep;static int adb_inited;static pid_t adb_probe_task_pid;static DECLARE_MUTEX(adb_probe_mutex);static struct completion adb_probe_task_comp;static int sleepy_trackpad;static int autopoll_devs;int __adb_probe_sync;#ifdef CONFIG_PM_SLEEPstatic void 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 int try_handler_change(int, int);static struct adb_handler { void (*handler)(unsigned char *, int, int); int original_address; int handler_id; int busy;} adb_handler[16];/* * The adb_handler_sem mutex protects all accesses to the original_address * and handler_id fields of adb_handler[i] for all i, and changes to the * handler field. * Accesses to the handler field are protected by the adb_handler_lock * rwlock. It is held across all calls to any handler, so that by the * time adb_unregister returns, we know that the old handler isn't being * called. */static DECLARE_MUTEX(adb_handler_sem);static DEFINE_RWLOCK(adb_handler_lock);#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) msleep(ms); 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"); 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; up(&adb_probe_mutex); return 0;}static void__adb_probe_task(struct work_struct *bullshit){ adb_probe_task_pid = kernel_thread(adb_probe_task, NULL, SIGCHLD | CLONE_KERNEL);}static DECLARE_WORK(adb_reset_work, __adb_probe_task);intadb_reset_bus(void){ if (__adb_probe_sync) { do_adb_reset_bus(); return 0; } down(&adb_probe_mutex); schedule_work(&adb_reset_work); return 0;}int __init adb_init(void){ struct adb_driver *driver; int i;#ifdef CONFIG_PPC32 if (!machine_is(chrp) && !machine_is(powermac)) 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_PM_SLEEP pmu_register_sleep_notifier(&adb_sleep_notifier);#endif /* CONFIG_PM */#ifdef CONFIG_PPC if (machine_is_compatible("AAPL,PowerBook1998") || machine_is_compatible("PowerBook1,1")) sleepy_trackpad = 1;#endif /* CONFIG_PPC */ init_completion(&adb_probe_task_comp); adbdev_init(); adb_reset_bus(); } return 0;}__initcall(adb_init);#ifdef CONFIG_PM/* * notify clients before sleep and reset bus afterwards */voidadb_notify_sleep(struct pmu_sleep_notifier *self, int when){ switch (when) { case PBOOK_SLEEP_REQUEST: adb_got_sleep = 1; /* We need to get a lock on the probe thread */ down(&adb_probe_mutex); /* Stop autopoll */ if (adb_controller->autopoll) adb_controller->autopoll(0); blocking_notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); break; case PBOOK_WAKE: adb_got_sleep = 0; up(&adb_probe_mutex); adb_reset_bus(); break; }}#endif /* CONFIG_PM */static intdo_adb_reset_bus(void){ int ret; if (adb_controller == NULL) return -ENXIO; if (adb_controller->autopoll) adb_controller->autopoll(0); blocking_notifier_call_chain(&adb_client_list, ADB_MSG_PRE_RESET, NULL); if (sleepy_trackpad) { /* Let the trackpad settle down */ adb_wait_ms(500); } down(&adb_handler_sem); write_lock_irq(&adb_handler_lock); memset(adb_handler, 0, sizeof(adb_handler)); write_unlock_irq(&adb_handler_lock); /* 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) { autopoll_devs = adb_scan_bus(); if (adb_controller->autopoll) adb_controller->autopoll(autopoll_devs); } up(&adb_handler_sem); blocking_notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL); return ret;}voidadb_poll(void){ if ((adb_controller == NULL)||(adb_controller->poll == NULL)) return; adb_controller->poll();}static voidadb_probe_wakeup(struct adb_request *req){ complete(&adb_probe_task_comp);}/* Static request used during probe */static struct adb_request adb_sreq;static unsigned long adb_sreq_lock; // Use semaphore ! */ intadb_request(struct adb_request *req, void (*done)(struct adb_request *), int flags, int nbytes, ...){ va_list list; 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?