162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for the Apple Desktop Bus 462306a36Sopenharmony_ci * and the /dev/adb device on macintoshes. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Modified to declare controllers as structures, added 962306a36Sopenharmony_ci * client notification of bus reset and handles PowerBook 1062306a36Sopenharmony_ci * sleep, by Benjamin Herrenschmidt. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * To do: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - /sys/bus/adb to list the devices and infos 1562306a36Sopenharmony_ci * - more /dev/adb to allow userland to receive the 1662306a36Sopenharmony_ci * flow of auto-polling datas from a given device. 1762306a36Sopenharmony_ci * - move bus probe to a kernel thread 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/mm.h> 2762306a36Sopenharmony_ci#include <linux/sched/signal.h> 2862306a36Sopenharmony_ci#include <linux/adb.h> 2962306a36Sopenharmony_ci#include <linux/cuda.h> 3062306a36Sopenharmony_ci#include <linux/pmu.h> 3162306a36Sopenharmony_ci#include <linux/notifier.h> 3262306a36Sopenharmony_ci#include <linux/wait.h> 3362306a36Sopenharmony_ci#include <linux/init.h> 3462306a36Sopenharmony_ci#include <linux/delay.h> 3562306a36Sopenharmony_ci#include <linux/spinlock.h> 3662306a36Sopenharmony_ci#include <linux/completion.h> 3762306a36Sopenharmony_ci#include <linux/device.h> 3862306a36Sopenharmony_ci#include <linux/kthread.h> 3962306a36Sopenharmony_ci#include <linux/platform_device.h> 4062306a36Sopenharmony_ci#include <linux/mutex.h> 4162306a36Sopenharmony_ci#include <linux/of.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include <linux/uaccess.h> 4462306a36Sopenharmony_ci#ifdef CONFIG_PPC 4562306a36Sopenharmony_ci#include <asm/machdep.h> 4662306a36Sopenharmony_ci#endif 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciEXPORT_SYMBOL(adb_client_list); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciextern struct adb_driver via_macii_driver; 5262306a36Sopenharmony_ciextern struct adb_driver via_cuda_driver; 5362306a36Sopenharmony_ciextern struct adb_driver adb_iop_driver; 5462306a36Sopenharmony_ciextern struct adb_driver via_pmu_driver; 5562306a36Sopenharmony_ciextern struct adb_driver macio_adb_driver; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic DEFINE_MUTEX(adb_mutex); 5862306a36Sopenharmony_cistatic struct adb_driver *adb_driver_list[] = { 5962306a36Sopenharmony_ci#ifdef CONFIG_ADB_MACII 6062306a36Sopenharmony_ci &via_macii_driver, 6162306a36Sopenharmony_ci#endif 6262306a36Sopenharmony_ci#ifdef CONFIG_ADB_CUDA 6362306a36Sopenharmony_ci &via_cuda_driver, 6462306a36Sopenharmony_ci#endif 6562306a36Sopenharmony_ci#ifdef CONFIG_ADB_IOP 6662306a36Sopenharmony_ci &adb_iop_driver, 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 6962306a36Sopenharmony_ci &via_pmu_driver, 7062306a36Sopenharmony_ci#endif 7162306a36Sopenharmony_ci#ifdef CONFIG_ADB_MACIO 7262306a36Sopenharmony_ci &macio_adb_driver, 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci NULL 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct class *adb_dev_class; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct adb_driver *adb_controller; 8062306a36Sopenharmony_ciBLOCKING_NOTIFIER_HEAD(adb_client_list); 8162306a36Sopenharmony_cistatic int adb_got_sleep; 8262306a36Sopenharmony_cistatic int adb_inited; 8362306a36Sopenharmony_cistatic DEFINE_SEMAPHORE(adb_probe_mutex, 1); 8462306a36Sopenharmony_cistatic int sleepy_trackpad; 8562306a36Sopenharmony_cistatic int autopoll_devs; 8662306a36Sopenharmony_ciint __adb_probe_sync; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int adb_scan_bus(void); 8962306a36Sopenharmony_cistatic int do_adb_reset_bus(void); 9062306a36Sopenharmony_cistatic void adbdev_init(void); 9162306a36Sopenharmony_cistatic int try_handler_change(int, int); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct adb_handler { 9462306a36Sopenharmony_ci void (*handler)(unsigned char *, int, int); 9562306a36Sopenharmony_ci int original_address; 9662306a36Sopenharmony_ci int handler_id; 9762306a36Sopenharmony_ci int busy; 9862306a36Sopenharmony_ci} adb_handler[16]; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * The adb_handler_mutex mutex protects all accesses to the original_address 10262306a36Sopenharmony_ci * and handler_id fields of adb_handler[i] for all i, and changes to the 10362306a36Sopenharmony_ci * handler field. 10462306a36Sopenharmony_ci * Accesses to the handler field are protected by the adb_handler_lock 10562306a36Sopenharmony_ci * rwlock. It is held across all calls to any handler, so that by the 10662306a36Sopenharmony_ci * time adb_unregister returns, we know that the old handler isn't being 10762306a36Sopenharmony_ci * called. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic DEFINE_MUTEX(adb_handler_mutex); 11062306a36Sopenharmony_cistatic DEFINE_RWLOCK(adb_handler_lock); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#if 0 11362306a36Sopenharmony_cistatic void printADBreply(struct adb_request *req) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int i; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci printk("adb reply (%d)", req->reply_len); 11862306a36Sopenharmony_ci for(i = 0; i < req->reply_len; i++) 11962306a36Sopenharmony_ci printk(" %x", req->reply[i]); 12062306a36Sopenharmony_ci printk("\n"); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci#endif 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int adb_scan_bus(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int i, highFree=0, noMovement; 12862306a36Sopenharmony_ci int devmask = 0; 12962306a36Sopenharmony_ci struct adb_request req; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* assumes adb_handler[] is all zeroes at this point */ 13262306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 13362306a36Sopenharmony_ci /* see if there is anything at address i */ 13462306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 13562306a36Sopenharmony_ci (i << 4) | 0xf); 13662306a36Sopenharmony_ci if (req.reply_len > 1) 13762306a36Sopenharmony_ci /* one or more devices at this address */ 13862306a36Sopenharmony_ci adb_handler[i].original_address = i; 13962306a36Sopenharmony_ci else if (i > highFree) 14062306a36Sopenharmony_ci highFree = i; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Note we reset noMovement to 0 each time we move a device */ 14462306a36Sopenharmony_ci for (noMovement = 1; noMovement < 2 && highFree > 0; noMovement++) { 14562306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 14662306a36Sopenharmony_ci if (adb_handler[i].original_address == 0) 14762306a36Sopenharmony_ci continue; 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * Send a "talk register 3" command to address i 15062306a36Sopenharmony_ci * to provoke a collision if there is more than 15162306a36Sopenharmony_ci * one device at this address. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 15462306a36Sopenharmony_ci (i << 4) | 0xf); 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Move the device(s) which didn't detect a 15762306a36Sopenharmony_ci * collision to address `highFree'. Hopefully 15862306a36Sopenharmony_ci * this only moves one device. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC, 3, 16162306a36Sopenharmony_ci (i<< 4) | 0xb, (highFree | 0x60), 0xfe); 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * See if anybody actually moved. This is suggested 16462306a36Sopenharmony_ci * by HW TechNote 01: 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * https://developer.apple.com/technotes/hw/hw_01.html 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 16962306a36Sopenharmony_ci (highFree << 4) | 0xf); 17062306a36Sopenharmony_ci if (req.reply_len <= 1) continue; 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Test whether there are any device(s) left 17362306a36Sopenharmony_ci * at address i. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 17662306a36Sopenharmony_ci (i << 4) | 0xf); 17762306a36Sopenharmony_ci if (req.reply_len > 1) { 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * There are still one or more devices 18062306a36Sopenharmony_ci * left at address i. Register the one(s) 18162306a36Sopenharmony_ci * we moved to `highFree', and find a new 18262306a36Sopenharmony_ci * value for highFree. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci adb_handler[highFree].original_address = 18562306a36Sopenharmony_ci adb_handler[i].original_address; 18662306a36Sopenharmony_ci while (highFree > 0 && 18762306a36Sopenharmony_ci adb_handler[highFree].original_address) 18862306a36Sopenharmony_ci highFree--; 18962306a36Sopenharmony_ci if (highFree <= 0) 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci noMovement = 0; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * No devices left at address i; move the 19662306a36Sopenharmony_ci * one(s) we moved to `highFree' back to i. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC, 3, 19962306a36Sopenharmony_ci (highFree << 4) | 0xb, 20062306a36Sopenharmony_ci (i | 0x60), 0xfe); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Now fill in the handler_id field of the adb_handler entries. */ 20662306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 20762306a36Sopenharmony_ci if (adb_handler[i].original_address == 0) 20862306a36Sopenharmony_ci continue; 20962306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 21062306a36Sopenharmony_ci (i << 4) | 0xf); 21162306a36Sopenharmony_ci adb_handler[i].handler_id = req.reply[2]; 21262306a36Sopenharmony_ci printk(KERN_DEBUG "adb device [%d]: %d 0x%X\n", i, 21362306a36Sopenharmony_ci adb_handler[i].original_address, 21462306a36Sopenharmony_ci adb_handler[i].handler_id); 21562306a36Sopenharmony_ci devmask |= 1 << i; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return devmask; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci * This kernel task handles ADB probing. It dies once probing is 22262306a36Sopenharmony_ci * completed. 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic int 22562306a36Sopenharmony_ciadb_probe_task(void *x) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci pr_debug("adb: starting probe task...\n"); 22862306a36Sopenharmony_ci do_adb_reset_bus(); 22962306a36Sopenharmony_ci pr_debug("adb: finished probe task...\n"); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci up(&adb_probe_mutex); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void 23762306a36Sopenharmony_ci__adb_probe_task(struct work_struct *bullshit) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci kthread_run(adb_probe_task, NULL, "kadbprobe"); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic DECLARE_WORK(adb_reset_work, __adb_probe_task); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciint 24562306a36Sopenharmony_ciadb_reset_bus(void) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (__adb_probe_sync) { 24862306a36Sopenharmony_ci do_adb_reset_bus(); 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci down(&adb_probe_mutex); 25362306a36Sopenharmony_ci schedule_work(&adb_reset_work); 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#ifdef CONFIG_PM 25862306a36Sopenharmony_ci/* 25962306a36Sopenharmony_ci * notify clients before sleep 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_cistatic int __adb_suspend(struct platform_device *dev, pm_message_t state) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci adb_got_sleep = 1; 26462306a36Sopenharmony_ci /* We need to get a lock on the probe thread */ 26562306a36Sopenharmony_ci down(&adb_probe_mutex); 26662306a36Sopenharmony_ci /* Stop autopoll */ 26762306a36Sopenharmony_ci if (adb_controller->autopoll) 26862306a36Sopenharmony_ci adb_controller->autopoll(0); 26962306a36Sopenharmony_ci blocking_notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int adb_suspend(struct device *dev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return __adb_suspend(to_platform_device(dev), PMSG_SUSPEND); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int adb_freeze(struct device *dev) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci return __adb_suspend(to_platform_device(dev), PMSG_FREEZE); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int adb_poweroff(struct device *dev) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci return __adb_suspend(to_platform_device(dev), PMSG_HIBERNATE); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * reset bus after sleep 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic int __adb_resume(struct platform_device *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci adb_got_sleep = 0; 29562306a36Sopenharmony_ci up(&adb_probe_mutex); 29662306a36Sopenharmony_ci adb_reset_bus(); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int adb_resume(struct device *dev) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci return __adb_resume(to_platform_device(dev)); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci#endif /* CONFIG_PM */ 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int __init adb_init(void) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct adb_driver *driver; 31062306a36Sopenharmony_ci int i; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci#ifdef CONFIG_PPC32 31362306a36Sopenharmony_ci if (!machine_is(chrp) && !machine_is(powermac)) 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci#endif 31662306a36Sopenharmony_ci#ifdef CONFIG_MAC 31762306a36Sopenharmony_ci if (!MACH_IS_MAC) 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci#endif 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* xmon may do early-init */ 32262306a36Sopenharmony_ci if (adb_inited) 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci adb_inited = 1; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci adb_controller = NULL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci i = 0; 32962306a36Sopenharmony_ci while ((driver = adb_driver_list[i++]) != NULL) { 33062306a36Sopenharmony_ci if (!driver->probe()) { 33162306a36Sopenharmony_ci adb_controller = driver; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci if (adb_controller != NULL && adb_controller->init && 33662306a36Sopenharmony_ci adb_controller->init()) 33762306a36Sopenharmony_ci adb_controller = NULL; 33862306a36Sopenharmony_ci if (adb_controller == NULL) { 33962306a36Sopenharmony_ci pr_warn("Warning: no ADB interface detected\n"); 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci#ifdef CONFIG_PPC 34262306a36Sopenharmony_ci if (of_machine_is_compatible("AAPL,PowerBook1998") || 34362306a36Sopenharmony_ci of_machine_is_compatible("PowerBook1,1")) 34462306a36Sopenharmony_ci sleepy_trackpad = 1; 34562306a36Sopenharmony_ci#endif /* CONFIG_PPC */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci adbdev_init(); 34862306a36Sopenharmony_ci adb_reset_bus(); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cidevice_initcall(adb_init); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int 35662306a36Sopenharmony_cido_adb_reset_bus(void) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci int ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (adb_controller == NULL) 36162306a36Sopenharmony_ci return -ENXIO; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (adb_controller->autopoll) 36462306a36Sopenharmony_ci adb_controller->autopoll(0); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci blocking_notifier_call_chain(&adb_client_list, 36762306a36Sopenharmony_ci ADB_MSG_PRE_RESET, NULL); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (sleepy_trackpad) { 37062306a36Sopenharmony_ci /* Let the trackpad settle down */ 37162306a36Sopenharmony_ci msleep(500); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 37562306a36Sopenharmony_ci write_lock_irq(&adb_handler_lock); 37662306a36Sopenharmony_ci memset(adb_handler, 0, sizeof(adb_handler)); 37762306a36Sopenharmony_ci write_unlock_irq(&adb_handler_lock); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* That one is still a bit synchronous, oh well... */ 38062306a36Sopenharmony_ci if (adb_controller->reset_bus) 38162306a36Sopenharmony_ci ret = adb_controller->reset_bus(); 38262306a36Sopenharmony_ci else 38362306a36Sopenharmony_ci ret = 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (sleepy_trackpad) { 38662306a36Sopenharmony_ci /* Let the trackpad settle down */ 38762306a36Sopenharmony_ci msleep(1500); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (!ret) { 39162306a36Sopenharmony_ci autopoll_devs = adb_scan_bus(); 39262306a36Sopenharmony_ci if (adb_controller->autopoll) 39362306a36Sopenharmony_ci adb_controller->autopoll(autopoll_devs); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci blocking_notifier_call_chain(&adb_client_list, 39862306a36Sopenharmony_ci ADB_MSG_POST_RESET, NULL); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_civoid 40462306a36Sopenharmony_ciadb_poll(void) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci if ((adb_controller == NULL)||(adb_controller->poll == NULL)) 40762306a36Sopenharmony_ci return; 40862306a36Sopenharmony_ci adb_controller->poll(); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ciEXPORT_SYMBOL(adb_poll); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void adb_sync_req_done(struct adb_request *req) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct completion *comp = req->arg; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci complete(comp); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciint 42062306a36Sopenharmony_ciadb_request(struct adb_request *req, void (*done)(struct adb_request *), 42162306a36Sopenharmony_ci int flags, int nbytes, ...) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci va_list list; 42462306a36Sopenharmony_ci int i; 42562306a36Sopenharmony_ci int rc; 42662306a36Sopenharmony_ci struct completion comp; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if ((adb_controller == NULL) || (adb_controller->send_request == NULL)) 42962306a36Sopenharmony_ci return -ENXIO; 43062306a36Sopenharmony_ci if (nbytes < 1) 43162306a36Sopenharmony_ci return -EINVAL; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci req->nbytes = nbytes+1; 43462306a36Sopenharmony_ci req->done = done; 43562306a36Sopenharmony_ci req->reply_expected = flags & ADBREQ_REPLY; 43662306a36Sopenharmony_ci req->data[0] = ADB_PACKET; 43762306a36Sopenharmony_ci va_start(list, nbytes); 43862306a36Sopenharmony_ci for (i = 0; i < nbytes; ++i) 43962306a36Sopenharmony_ci req->data[i+1] = va_arg(list, int); 44062306a36Sopenharmony_ci va_end(list); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (flags & ADBREQ_NOSEND) 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Synchronous requests block using an on-stack completion */ 44662306a36Sopenharmony_ci if (flags & ADBREQ_SYNC) { 44762306a36Sopenharmony_ci WARN_ON(done); 44862306a36Sopenharmony_ci req->done = adb_sync_req_done; 44962306a36Sopenharmony_ci req->arg = ∁ 45062306a36Sopenharmony_ci init_completion(&comp); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rc = adb_controller->send_request(req, 0); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if ((flags & ADBREQ_SYNC) && !rc && !req->complete) 45662306a36Sopenharmony_ci wait_for_completion(&comp); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciEXPORT_SYMBOL(adb_request); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Ultimately this should return the number of devices with 46362306a36Sopenharmony_ci the given default id. 46462306a36Sopenharmony_ci And it does it now ! Note: changed behaviour: This function 46562306a36Sopenharmony_ci will now register if default_id _and_ handler_id both match 46662306a36Sopenharmony_ci but handler_id can be left to 0 to match with default_id only. 46762306a36Sopenharmony_ci When handler_id is set, this function will try to adjust 46862306a36Sopenharmony_ci the handler_id id it doesn't match. */ 46962306a36Sopenharmony_ciint 47062306a36Sopenharmony_ciadb_register(int default_id, int handler_id, struct adb_ids *ids, 47162306a36Sopenharmony_ci void (*handler)(unsigned char *, int, int)) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int i; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 47662306a36Sopenharmony_ci ids->nids = 0; 47762306a36Sopenharmony_ci for (i = 1; i < 16; i++) { 47862306a36Sopenharmony_ci if ((adb_handler[i].original_address == default_id) && 47962306a36Sopenharmony_ci (!handler_id || (handler_id == adb_handler[i].handler_id) || 48062306a36Sopenharmony_ci try_handler_change(i, handler_id))) { 48162306a36Sopenharmony_ci if (adb_handler[i].handler) { 48262306a36Sopenharmony_ci pr_err("Two handlers for ADB device %d\n", 48362306a36Sopenharmony_ci default_id); 48462306a36Sopenharmony_ci continue; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci write_lock_irq(&adb_handler_lock); 48762306a36Sopenharmony_ci adb_handler[i].handler = handler; 48862306a36Sopenharmony_ci write_unlock_irq(&adb_handler_lock); 48962306a36Sopenharmony_ci ids->id[ids->nids++] = i; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 49362306a36Sopenharmony_ci return ids->nids; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ciEXPORT_SYMBOL(adb_register); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciint 49862306a36Sopenharmony_ciadb_unregister(int index) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci int ret = -ENODEV; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 50362306a36Sopenharmony_ci write_lock_irq(&adb_handler_lock); 50462306a36Sopenharmony_ci if (adb_handler[index].handler) { 50562306a36Sopenharmony_ci while(adb_handler[index].busy) { 50662306a36Sopenharmony_ci write_unlock_irq(&adb_handler_lock); 50762306a36Sopenharmony_ci yield(); 50862306a36Sopenharmony_ci write_lock_irq(&adb_handler_lock); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci ret = 0; 51162306a36Sopenharmony_ci adb_handler[index].handler = NULL; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci write_unlock_irq(&adb_handler_lock); 51462306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 51562306a36Sopenharmony_ci return ret; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL(adb_unregister); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_civoid 52062306a36Sopenharmony_ciadb_input(unsigned char *buf, int nb, int autopoll) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int i, id; 52362306a36Sopenharmony_ci static int dump_adb_input; 52462306a36Sopenharmony_ci unsigned long flags; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci void (*handler)(unsigned char *, int, int); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* We skip keystrokes and mouse moves when the sleep process 52962306a36Sopenharmony_ci * has been started. We stop autopoll, but this is another security 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci if (adb_got_sleep) 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci id = buf[0] >> 4; 53562306a36Sopenharmony_ci if (dump_adb_input) { 53662306a36Sopenharmony_ci pr_info("adb packet: "); 53762306a36Sopenharmony_ci for (i = 0; i < nb; ++i) 53862306a36Sopenharmony_ci pr_cont(" %x", buf[i]); 53962306a36Sopenharmony_ci pr_cont(", id = %d\n", id); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci write_lock_irqsave(&adb_handler_lock, flags); 54262306a36Sopenharmony_ci handler = adb_handler[id].handler; 54362306a36Sopenharmony_ci if (handler != NULL) 54462306a36Sopenharmony_ci adb_handler[id].busy = 1; 54562306a36Sopenharmony_ci write_unlock_irqrestore(&adb_handler_lock, flags); 54662306a36Sopenharmony_ci if (handler != NULL) { 54762306a36Sopenharmony_ci (*handler)(buf, nb, autopoll); 54862306a36Sopenharmony_ci wmb(); 54962306a36Sopenharmony_ci adb_handler[id].busy = 0; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* Try to change handler to new_id. Will return 1 if successful. */ 55562306a36Sopenharmony_cistatic int try_handler_change(int address, int new_id) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct adb_request req; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (adb_handler[address].handler_id == new_id) 56062306a36Sopenharmony_ci return 1; 56162306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC, 3, 56262306a36Sopenharmony_ci ADB_WRITEREG(address, 3), address | 0x20, new_id); 56362306a36Sopenharmony_ci adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1, 56462306a36Sopenharmony_ci ADB_READREG(address, 3)); 56562306a36Sopenharmony_ci if (req.reply_len < 2) 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci if (req.reply[2] != new_id) 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci adb_handler[address].handler_id = req.reply[2]; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 1; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ciint 57562306a36Sopenharmony_ciadb_try_handler_change(int address, int new_id) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci int ret; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 58062306a36Sopenharmony_ci ret = try_handler_change(address, new_id); 58162306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci pr_debug("adb handler change: [%d] 0x%X\n", address, new_id); 58462306a36Sopenharmony_ci return ret; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ciEXPORT_SYMBOL(adb_try_handler_change); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciint 58962306a36Sopenharmony_ciadb_get_infos(int address, int *original_address, int *handler_id) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 59262306a36Sopenharmony_ci *original_address = adb_handler[address].original_address; 59362306a36Sopenharmony_ci *handler_id = adb_handler[address].handler_id; 59462306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return (*original_address != 0); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci * /dev/adb device driver. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci#define ADB_MAJOR 56 /* major number for /dev/adb */ 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistruct adbdev_state { 60762306a36Sopenharmony_ci spinlock_t lock; 60862306a36Sopenharmony_ci atomic_t n_pending; 60962306a36Sopenharmony_ci struct adb_request *completed; 61062306a36Sopenharmony_ci wait_queue_head_t wait_queue; 61162306a36Sopenharmony_ci int inuse; 61262306a36Sopenharmony_ci}; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void adb_write_done(struct adb_request *req) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct adbdev_state *state = (struct adbdev_state *) req->arg; 61762306a36Sopenharmony_ci unsigned long flags; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!req->complete) { 62062306a36Sopenharmony_ci req->reply_len = 0; 62162306a36Sopenharmony_ci req->complete = 1; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci spin_lock_irqsave(&state->lock, flags); 62462306a36Sopenharmony_ci atomic_dec(&state->n_pending); 62562306a36Sopenharmony_ci if (!state->inuse) { 62662306a36Sopenharmony_ci kfree(req); 62762306a36Sopenharmony_ci if (atomic_read(&state->n_pending) == 0) { 62862306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 62962306a36Sopenharmony_ci kfree(state); 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci struct adb_request **ap = &state->completed; 63462306a36Sopenharmony_ci while (*ap != NULL) 63562306a36Sopenharmony_ci ap = &(*ap)->next; 63662306a36Sopenharmony_ci req->next = NULL; 63762306a36Sopenharmony_ci *ap = req; 63862306a36Sopenharmony_ci wake_up_interruptible(&state->wait_queue); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int 64462306a36Sopenharmony_cido_adb_query(struct adb_request *req) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci int ret = -EINVAL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci switch(req->data[1]) { 64962306a36Sopenharmony_ci case ADB_QUERY_GETDEVINFO: 65062306a36Sopenharmony_ci if (req->nbytes < 3 || req->data[2] >= 16) 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci mutex_lock(&adb_handler_mutex); 65362306a36Sopenharmony_ci req->reply[0] = adb_handler[req->data[2]].original_address; 65462306a36Sopenharmony_ci req->reply[1] = adb_handler[req->data[2]].handler_id; 65562306a36Sopenharmony_ci mutex_unlock(&adb_handler_mutex); 65662306a36Sopenharmony_ci req->complete = 1; 65762306a36Sopenharmony_ci req->reply_len = 2; 65862306a36Sopenharmony_ci adb_write_done(req); 65962306a36Sopenharmony_ci ret = 0; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci return ret; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int adb_open(struct inode *inode, struct file *file) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct adbdev_state *state; 66862306a36Sopenharmony_ci int ret = 0; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci mutex_lock(&adb_mutex); 67162306a36Sopenharmony_ci if (iminor(inode) > 0 || adb_controller == NULL) { 67262306a36Sopenharmony_ci ret = -ENXIO; 67362306a36Sopenharmony_ci goto out; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL); 67662306a36Sopenharmony_ci if (!state) { 67762306a36Sopenharmony_ci ret = -ENOMEM; 67862306a36Sopenharmony_ci goto out; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci file->private_data = state; 68162306a36Sopenharmony_ci spin_lock_init(&state->lock); 68262306a36Sopenharmony_ci atomic_set(&state->n_pending, 0); 68362306a36Sopenharmony_ci state->completed = NULL; 68462306a36Sopenharmony_ci init_waitqueue_head(&state->wait_queue); 68562306a36Sopenharmony_ci state->inuse = 1; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ciout: 68862306a36Sopenharmony_ci mutex_unlock(&adb_mutex); 68962306a36Sopenharmony_ci return ret; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int adb_release(struct inode *inode, struct file *file) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct adbdev_state *state = file->private_data; 69562306a36Sopenharmony_ci unsigned long flags; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci mutex_lock(&adb_mutex); 69862306a36Sopenharmony_ci if (state) { 69962306a36Sopenharmony_ci file->private_data = NULL; 70062306a36Sopenharmony_ci spin_lock_irqsave(&state->lock, flags); 70162306a36Sopenharmony_ci if (atomic_read(&state->n_pending) == 0 70262306a36Sopenharmony_ci && state->completed == NULL) { 70362306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 70462306a36Sopenharmony_ci kfree(state); 70562306a36Sopenharmony_ci } else { 70662306a36Sopenharmony_ci state->inuse = 0; 70762306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci mutex_unlock(&adb_mutex); 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic ssize_t adb_read(struct file *file, char __user *buf, 71562306a36Sopenharmony_ci size_t count, loff_t *ppos) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci int ret = 0; 71862306a36Sopenharmony_ci struct adbdev_state *state = file->private_data; 71962306a36Sopenharmony_ci struct adb_request *req; 72062306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 72162306a36Sopenharmony_ci unsigned long flags; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (count < 2) 72462306a36Sopenharmony_ci return -EINVAL; 72562306a36Sopenharmony_ci if (count > sizeof(req->reply)) 72662306a36Sopenharmony_ci count = sizeof(req->reply); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci req = NULL; 72962306a36Sopenharmony_ci spin_lock_irqsave(&state->lock, flags); 73062306a36Sopenharmony_ci add_wait_queue(&state->wait_queue, &wait); 73162306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci for (;;) { 73462306a36Sopenharmony_ci req = state->completed; 73562306a36Sopenharmony_ci if (req != NULL) 73662306a36Sopenharmony_ci state->completed = req->next; 73762306a36Sopenharmony_ci else if (atomic_read(&state->n_pending) == 0) 73862306a36Sopenharmony_ci ret = -EIO; 73962306a36Sopenharmony_ci if (req != NULL || ret != 0) 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 74362306a36Sopenharmony_ci ret = -EAGAIN; 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci if (signal_pending(current)) { 74762306a36Sopenharmony_ci ret = -ERESTARTSYS; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 75162306a36Sopenharmony_ci schedule(); 75262306a36Sopenharmony_ci spin_lock_irqsave(&state->lock, flags); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 75662306a36Sopenharmony_ci remove_wait_queue(&state->wait_queue, &wait); 75762306a36Sopenharmony_ci spin_unlock_irqrestore(&state->lock, flags); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci return ret; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = req->reply_len; 76362306a36Sopenharmony_ci if (ret > count) 76462306a36Sopenharmony_ci ret = count; 76562306a36Sopenharmony_ci if (ret > 0 && copy_to_user(buf, req->reply, ret)) 76662306a36Sopenharmony_ci ret = -EFAULT; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci kfree(req); 76962306a36Sopenharmony_ci return ret; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic ssize_t adb_write(struct file *file, const char __user *buf, 77362306a36Sopenharmony_ci size_t count, loff_t *ppos) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci int ret/*, i*/; 77662306a36Sopenharmony_ci struct adbdev_state *state = file->private_data; 77762306a36Sopenharmony_ci struct adb_request *req; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (count < 2 || count > sizeof(req->data)) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci if (adb_controller == NULL) 78262306a36Sopenharmony_ci return -ENXIO; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci req = kmalloc(sizeof(struct adb_request), 78562306a36Sopenharmony_ci GFP_KERNEL); 78662306a36Sopenharmony_ci if (req == NULL) 78762306a36Sopenharmony_ci return -ENOMEM; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci req->nbytes = count; 79062306a36Sopenharmony_ci req->done = adb_write_done; 79162306a36Sopenharmony_ci req->arg = (void *) state; 79262306a36Sopenharmony_ci req->complete = 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ret = -EFAULT; 79562306a36Sopenharmony_ci if (copy_from_user(req->data, buf, count)) 79662306a36Sopenharmony_ci goto out; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci atomic_inc(&state->n_pending); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* If a probe is in progress or we are sleeping, wait for it to complete */ 80162306a36Sopenharmony_ci down(&adb_probe_mutex); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* Queries are special requests sent to the ADB driver itself */ 80462306a36Sopenharmony_ci if (req->data[0] == ADB_QUERY) { 80562306a36Sopenharmony_ci if (count > 1) 80662306a36Sopenharmony_ci ret = do_adb_query(req); 80762306a36Sopenharmony_ci else 80862306a36Sopenharmony_ci ret = -EINVAL; 80962306a36Sopenharmony_ci up(&adb_probe_mutex); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci /* Special case for ADB_BUSRESET request, all others are sent to 81262306a36Sopenharmony_ci the controller */ 81362306a36Sopenharmony_ci else if ((req->data[0] == ADB_PACKET) && (count > 1) 81462306a36Sopenharmony_ci && (req->data[1] == ADB_BUSRESET)) { 81562306a36Sopenharmony_ci ret = do_adb_reset_bus(); 81662306a36Sopenharmony_ci up(&adb_probe_mutex); 81762306a36Sopenharmony_ci atomic_dec(&state->n_pending); 81862306a36Sopenharmony_ci if (ret == 0) 81962306a36Sopenharmony_ci ret = count; 82062306a36Sopenharmony_ci goto out; 82162306a36Sopenharmony_ci } else { 82262306a36Sopenharmony_ci req->reply_expected = ((req->data[1] & 0xc) == 0xc); 82362306a36Sopenharmony_ci if (adb_controller && adb_controller->send_request) 82462306a36Sopenharmony_ci ret = adb_controller->send_request(req, 0); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci ret = -ENXIO; 82762306a36Sopenharmony_ci up(&adb_probe_mutex); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (ret != 0) { 83162306a36Sopenharmony_ci atomic_dec(&state->n_pending); 83262306a36Sopenharmony_ci goto out; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci return count; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciout: 83762306a36Sopenharmony_ci kfree(req); 83862306a36Sopenharmony_ci return ret; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic const struct file_operations adb_fops = { 84262306a36Sopenharmony_ci .owner = THIS_MODULE, 84362306a36Sopenharmony_ci .llseek = no_llseek, 84462306a36Sopenharmony_ci .read = adb_read, 84562306a36Sopenharmony_ci .write = adb_write, 84662306a36Sopenharmony_ci .open = adb_open, 84762306a36Sopenharmony_ci .release = adb_release, 84862306a36Sopenharmony_ci}; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci#ifdef CONFIG_PM 85162306a36Sopenharmony_cistatic const struct dev_pm_ops adb_dev_pm_ops = { 85262306a36Sopenharmony_ci .suspend = adb_suspend, 85362306a36Sopenharmony_ci .resume = adb_resume, 85462306a36Sopenharmony_ci /* Hibernate hooks */ 85562306a36Sopenharmony_ci .freeze = adb_freeze, 85662306a36Sopenharmony_ci .thaw = adb_resume, 85762306a36Sopenharmony_ci .poweroff = adb_poweroff, 85862306a36Sopenharmony_ci .restore = adb_resume, 85962306a36Sopenharmony_ci}; 86062306a36Sopenharmony_ci#endif 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic struct platform_driver adb_pfdrv = { 86362306a36Sopenharmony_ci .driver = { 86462306a36Sopenharmony_ci .name = "adb", 86562306a36Sopenharmony_ci#ifdef CONFIG_PM 86662306a36Sopenharmony_ci .pm = &adb_dev_pm_ops, 86762306a36Sopenharmony_ci#endif 86862306a36Sopenharmony_ci }, 86962306a36Sopenharmony_ci}; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic struct platform_device adb_pfdev = { 87262306a36Sopenharmony_ci .name = "adb", 87362306a36Sopenharmony_ci}; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int __init 87662306a36Sopenharmony_ciadb_dummy_probe(struct platform_device *dev) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci if (dev == &adb_pfdev) 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci return -ENODEV; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void __init 88462306a36Sopenharmony_ciadbdev_init(void) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) { 88762306a36Sopenharmony_ci pr_err("adb: unable to get major %d\n", ADB_MAJOR); 88862306a36Sopenharmony_ci return; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci adb_dev_class = class_create("adb"); 89262306a36Sopenharmony_ci if (IS_ERR(adb_dev_class)) 89362306a36Sopenharmony_ci return; 89462306a36Sopenharmony_ci device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb"); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci platform_device_register(&adb_pfdev); 89762306a36Sopenharmony_ci platform_driver_probe(&adb_pfdrv, adb_dummy_probe); 89862306a36Sopenharmony_ci} 899