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 = &comp;
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