📄 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. */#include <linux/config.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/malloc.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 <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;#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 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 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;}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"); } else {#ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier(&adb_sleep_notifier);#endif /* CONFIG_PMAC_PBOOK */ 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; 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; adb_reset_bus(); } break; case PBOOK_SLEEP_NOW: break; case PBOOK_WAKE: adb_reset_bus(); adb_got_sleep = 0; break; } return PBOOK_SLEEP_OK;}#endif /* CONFIG_PMAC_PBOOK */intadb_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; } save_flags(flags); cli(); memset(adb_handler, 0, sizeof(adb_handler)); restore_flags(flags); if (adb_controller->reset_bus) ret = adb_controller->reset_bus(); else ret = 0; if (!ret) { devs = adb_scan_bus(); if (adb_controller->autopoll) adb_controller->autopoll(devs); } nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL); if (nret & NOTIFY_STOP_MASK) return -EBUSY; return ret;}voidadb_poll(void){ if ((adb_controller == NULL)||(adb_controller->poll == NULL)) return; adb_controller->poll();}intadb_request(struct adb_request *req, void (*done)(struct adb_request *), int flags, int nbytes, ...){ va_list list; int i; struct adb_request sreq; if ((adb_controller == NULL) || (adb_controller->send_request == NULL)) return -ENXIO; if (nbytes < 1) return -EINVAL; if (req == NULL) { req = &sreq;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -