linux_dsm_epyc7002/drivers/macintosh/adb.c
Benjamin Herrenschmidt 8c8709334c [PATCH] ppc32: Remove CONFIG_PMAC_PBOOK
This patch removes CONFIG_PMAC_PBOOK (PowerBook support).  This is now
split into CONFIG_PMAC_MEDIABAY for the actual hotswap bay that some
powerbooks have, CONFIG_PM for power management related code, and just left
out of any CONFIG_* option for some generally useful stuff that can be used
on non-laptops as well.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-06-27 15:11:43 -07:00

910 lines
21 KiB
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:
*
* - /sys/bus/adb to list the devices and infos
* - more /dev/adb to allow userland to receive the
* flow of auto-polling datas from a given device.
* - move bus probe to a kernel thread
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#include <linux/notifier.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#ifdef CONFIG_PPC
#include <asm/prom.h>
#endif
EXPORT_SYMBOL(adb_controller);
EXPORT_SYMBOL(adb_client_list);
extern struct adb_driver via_macii_driver;
extern struct adb_driver via_maciisi_driver;
extern struct adb_driver via_cuda_driver;
extern struct adb_driver adb_iop_driver;
extern struct adb_driver via_pmu_driver;
extern struct adb_driver macio_adb_driver;
static struct adb_driver *adb_driver_list[] = {
#ifdef CONFIG_ADB_MACII
&via_macii_driver,
#endif
#ifdef CONFIG_ADB_MACIISI
&via_maciisi_driver,
#endif
#ifdef CONFIG_ADB_CUDA
&via_cuda_driver,
#endif
#ifdef CONFIG_ADB_IOP
&adb_iop_driver,
#endif
#if defined(CONFIG_ADB_PMU) || defined(CONFIG_ADB_PMU68K)
&via_pmu_driver,
#endif
#ifdef CONFIG_ADB_MACIO
&macio_adb_driver,
#endif
NULL
};
static struct class *adb_dev_class;
struct adb_driver *adb_controller;
struct notifier_block *adb_client_list = NULL;
static int adb_got_sleep;
static int adb_inited;
static pid_t adb_probe_task_pid;
static DECLARE_MUTEX(adb_probe_mutex);
static struct completion adb_probe_task_comp;
static int sleepy_trackpad;
static int autopoll_devs;
int __adb_probe_sync;
#ifdef CONFIG_PM
static 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,
};
#endif
static int adb_scan_bus(void);
static int do_adb_reset_bus(void);
static void adbdev_init(void);
static int try_handler_change(int, int);
static struct adb_handler {
void (*handler)(unsigned char *, int, struct pt_regs *, int);
int original_address;
int handler_id;
int busy;
} adb_handler[16];
/*
* The adb_handler_sem mutex protects all accesses to the original_address
* and handler_id fields of adb_handler[i] for all i, and changes to the
* handler field.
* Accesses to the handler field are protected by the adb_handler_lock
* rwlock. It is held across all calls to any handler, so that by the
* time adb_unregister returns, we know that the old handler isn't being
* called.
*/
static DECLARE_MUTEX(adb_handler_sem);
static DEFINE_RWLOCK(adb_handler_lock);
#if 0
static 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");
}
#endif
static __inline__ void adb_wait_ms(unsigned int ms)
{
if (current->pid && adb_probe_task_pid &&
adb_probe_task_pid == current->pid)
msleep(ms);
else
mdelay(ms);
}
static int adb_scan_bus(void)
{
int i, highFree=0, noMovement;
int devmask = 0;
struct adb_request req;
/* assumes adb_handler[] is all zeroes at this point */
for (i = 1; i < 16; i++) {
/* see if there is anything at address i */
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
(i << 4) | 0xf);
if (req.reply_len > 1)
/* one or more devices at this address */
adb_handler[i].original_address = i;
else if (i > highFree)
highFree = i;
}
/* Note we reset noMovement to 0 each time we move a device */
for (noMovement = 1; noMovement < 2 && highFree > 0; noMovement++) {
for (i = 1; i < 16; i++) {
if (adb_handler[i].original_address == 0)
continue;
/*
* Send a "talk register 3" command to address i
* to provoke a collision if there is more than
* one device at this address.
*/
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
(i << 4) | 0xf);
/*
* Move the device(s) which didn't detect a
* collision to address `highFree'. Hopefully
* this only moves one device.
*/
adb_request(&req, NULL, ADBREQ_SYNC, 3,
(i<< 4) | 0xb, (highFree | 0x60), 0xfe);
/*
* See if anybody actually moved. This is suggested
* by HW TechNote 01:
*
* http://developer.apple.com/technotes/hw/hw_01.html
*/
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
(highFree << 4) | 0xf);
if (req.reply_len <= 1) continue;
/*
* Test whether there are any device(s) left
* at address i.
*/
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
(i << 4) | 0xf);
if (req.reply_len > 1) {
/*
* There are still one or more devices
* left at address i. Register the one(s)
* we moved to `highFree', and find a new
* value for highFree.
*/
adb_handler[highFree].original_address =
adb_handler[i].original_address;
while (highFree > 0 &&
adb_handler[highFree].original_address)
highFree--;
if (highFree <= 0)
break;
noMovement = 0;
}
else {
/*
* No devices left at address i; move the
* one(s) we moved to `highFree' back to i.
*/
adb_request(&req, NULL, ADBREQ_SYNC, 3,
(highFree << 4) | 0xb,
(i | 0x60), 0xfe);
}
}
}
/* Now fill in the handler_id field of the adb_handler entries. */
printk(KERN_DEBUG "adb devices:");
for (i = 1; i < 16; i++) {
if (adb_handler[i].original_address == 0)
continue;
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
(i << 4) | 0xf);
adb_handler[i].handler_id = req.reply[2];
printk(" [%d]: %d %x", i, adb_handler[i].original_address,
adb_handler[i].handler_id);
devmask |= 1 << i;
}
printk("\n");
return devmask;
}
/*
* This kernel task handles ADB probing. It dies once probing is
* completed.
*/
static int
adb_probe_task(void *x)
{
sigset_t blocked;
strcpy(current->comm, "kadbprobe");
sigfillset(&blocked);
sigprocmask(SIG_BLOCK, &blocked, NULL);
flush_signals(current);
printk(KERN_INFO "adb: starting probe task...\n");
do_adb_reset_bus();
printk(KERN_INFO "adb: finished probe task...\n");
adb_probe_task_pid = 0;
up(&adb_probe_mutex);
return 0;
}
static void
__adb_probe_task(void *data)
{
adb_probe_task_pid = kernel_thread(adb_probe_task, NULL, SIGCHLD | CLONE_KERNEL);
}
static DECLARE_WORK(adb_reset_work, __adb_probe_task, NULL);
int
adb_reset_bus(void)
{
if (__adb_probe_sync) {
do_adb_reset_bus();
return 0;
}
down(&adb_probe_mutex);
schedule_work(&adb_reset_work);
return 0;
}
int __init adb_init(void)
{
struct adb_driver *driver;
int i;
#ifdef CONFIG_PPC32
if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
return 0;
#endif
#ifdef CONFIG_MAC
if (!MACH_IS_MAC)
return 0;
#endif
/* xmon may do early-init */
if (adb_inited)
return 0;
adb_inited = 1;
adb_controller = NULL;
i = 0;
while ((driver = adb_driver_list[i++]) != NULL) {
if (!driver->probe()) {
adb_controller = driver;
break;
}
}
if ((adb_controller == NULL) || adb_controller->init()) {
printk(KERN_WARNING "Warning: no ADB interface detected\n");
adb_controller = NULL;
} else {
#ifdef CONFIG_PM
pmu_register_sleep_notifier(&adb_sleep_notifier);
#endif /* CONFIG_PM */
#ifdef CONFIG_PPC
if (machine_is_compatible("AAPL,PowerBook1998") ||
machine_is_compatible("PowerBook1,1"))
sleepy_trackpad = 1;
#endif /* CONFIG_PPC */
init_completion(&adb_probe_task_comp);
adbdev_init();
adb_reset_bus();
}
return 0;
}
__initcall(adb_init);
#ifdef CONFIG_PM
/*
* notify clients before sleep and reset bus afterwards
*/
int
adb_notify_sleep(struct pmu_sleep_notifier *self, int when)
{
int ret;
switch (when) {
case PBOOK_SLEEP_REQUEST:
adb_got_sleep = 1;
/* We need to get a lock on the probe thread */
down(&adb_probe_mutex);
/* Stop autopoll */
if (adb_controller->autopoll)
adb_controller->autopoll(0);
ret = notifier_call_chain(&adb_client_list, ADB_MSG_POWERDOWN, NULL);
if (ret & NOTIFY_STOP_MASK) {
up(&adb_probe_mutex);
return PBOOK_SLEEP_REFUSE;
}
break;
case PBOOK_SLEEP_REJECT:
if (adb_got_sleep) {
adb_got_sleep = 0;
up(&adb_probe_mutex);
adb_reset_bus();
}
break;
case PBOOK_SLEEP_NOW:
break;
case PBOOK_WAKE:
adb_got_sleep = 0;
up(&adb_probe_mutex);
adb_reset_bus();
break;
}
return PBOOK_SLEEP_OK;
}
#endif /* CONFIG_PM */
static int
do_adb_reset_bus(void)
{
int ret, nret;
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(autopoll_devs);
return -EBUSY;
}
if (sleepy_trackpad) {
/* Let the trackpad settle down */
adb_wait_ms(500);
}
down(&adb_handler_sem);
write_lock_irq(&adb_handler_lock);
memset(adb_handler, 0, sizeof(adb_handler));
write_unlock_irq(&adb_handler_lock);
/* That one is still a bit synchronous, oh well... */
if (adb_controller->reset_bus)
ret = adb_controller->reset_bus();
else
ret = 0;
if (sleepy_trackpad) {
/* Let the trackpad settle down */
adb_wait_ms(1500);
}
if (!ret) {
autopoll_devs = adb_scan_bus();
if (adb_controller->autopoll)
adb_controller->autopoll(autopoll_devs);
}
up(&adb_handler_sem);
nret = notifier_call_chain(&adb_client_list, ADB_MSG_POST_RESET, NULL);
if (nret & NOTIFY_STOP_MASK)
return -EBUSY;
return ret;
}
void
adb_poll(void)
{
if ((adb_controller == NULL)||(adb_controller->poll == NULL))
return;
adb_controller->poll();
}
static void
adb_probe_wakeup(struct adb_request *req)
{
complete(&adb_probe_task_comp);
}
/* Static request used during probe */
static struct adb_request adb_sreq;
static unsigned long adb_sreq_lock; // Use semaphore ! */
int
adb_request(struct adb_request *req, void (*done)(struct adb_request *),
int flags, int nbytes, ...)
{
va_list list;
int i, use_sreq;
int rc;
if ((adb_controller == NULL) || (adb_controller->send_request == NULL))
return -ENXIO;
if (nbytes < 1)
return -EINVAL;
if (req == NULL && (flags & ADBREQ_NOSEND))
return -EINVAL;
if (req == NULL) {
if (test_and_set_bit(0,&adb_sreq_lock)) {
printk("adb.c: Warning: contention on static request !\n");
return -EPERM;
}
req = &adb_sreq;
flags |= ADBREQ_SYNC;
use_sreq = 1;
} else
use_sreq = 0;
req->nbytes = nbytes+1;
req->done = done;
req->reply_expected = flags & ADBREQ_REPLY;
req->data[0] = ADB_PACKET;
va_start(list, nbytes);
for (i = 0; i < nbytes; ++i)
req->data[i+1] = va_arg(list, int);
va_end(list);
if (flags & ADBREQ_NOSEND)
return 0;
/* Synchronous requests send from the probe thread cause it to
* block. Beware that the "done" callback will be overriden !
*/
if ((flags & ADBREQ_SYNC) &&
(current->pid && adb_probe_task_pid &&
adb_probe_task_pid == current->pid)) {
req->done = adb_probe_wakeup;
rc = adb_controller->send_request(req, 0);
if (rc || req->complete)
goto bail;
wait_for_completion(&adb_probe_task_comp);
rc = 0;
goto bail;
}
rc = adb_controller->send_request(req, flags & ADBREQ_SYNC);
bail:
if (use_sreq)
clear_bit(0, &adb_sreq_lock);
return rc;
}
/* Ultimately this should return the number of devices with
the given default id.
And it does it now ! Note: changed behaviour: This function
will now register if default_id _and_ handler_id both match
but handler_id can be left to 0 to match with default_id only.
When handler_id is set, this function will try to adjust
the handler_id id it doesn't match. */
int
adb_register(int default_id, int handler_id, struct adb_ids *ids,
void (*handler)(unsigned char *, int, struct pt_regs *, int))
{
int i;
down(&adb_handler_sem);
ids->nids = 0;
for (i = 1; i < 16; i++) {
if ((adb_handler[i].original_address == default_id) &&
(!handler_id || (handler_id == adb_handler[i].handler_id) ||
try_handler_change(i, handler_id))) {
if (adb_handler[i].handler != 0) {
printk(KERN_ERR
"Two handlers for ADB device %d\n",
default_id);
continue;
}
write_lock_irq(&adb_handler_lock);
adb_handler[i].handler = handler;
write_unlock_irq(&adb_handler_lock);
ids->id[ids->nids++] = i;
}
}
up(&adb_handler_sem);
return ids->nids;
}
int
adb_unregister(int index)
{
int ret = -ENODEV;
down(&adb_handler_sem);
write_lock_irq(&adb_handler_lock);
if (adb_handler[index].handler) {
while(adb_handler[index].busy) {
write_unlock_irq(&adb_handler_lock);
yield();
write_lock_irq(&adb_handler_lock);
}
ret = 0;
adb_handler[index].handler = NULL;
}
write_unlock_irq(&adb_handler_lock);
up(&adb_handler_sem);
return ret;
}
void
adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll)
{
int i, id;
static int dump_adb_input = 0;
unsigned long flags;
void (*handler)(unsigned char *, int, struct pt_regs *, int);
/* We skip keystrokes and mouse moves when the sleep process
* has been started. We stop autopoll, but this is another security
*/
if (adb_got_sleep)
return;
id = buf[0] >> 4;
if (dump_adb_input) {
printk(KERN_INFO "adb packet: ");
for (i = 0; i < nb; ++i)
printk(" %x", buf[i]);
printk(", id = %d\n", id);
}
write_lock_irqsave(&adb_handler_lock, flags);
handler = adb_handler[id].handler;
if (handler != NULL)
adb_handler[id].busy = 1;
write_unlock_irqrestore(&adb_handler_lock, flags);
if (handler != NULL) {
(*handler)(buf, nb, regs, autopoll);
wmb();
adb_handler[id].busy = 0;
}
}
/* Try to change handler to new_id. Will return 1 if successful. */
static int try_handler_change(int address, int new_id)
{
struct adb_request req;
if (adb_handler[address].handler_id == new_id)
return 1;
adb_request(&req, NULL, ADBREQ_SYNC, 3,
ADB_WRITEREG(address, 3), address | 0x20, new_id);
adb_request(&req, NULL, ADBREQ_SYNC | ADBREQ_REPLY, 1,
ADB_READREG(address, 3));
if (req.reply_len < 2)
return 0;
if (req.reply[2] != new_id)
return 0;
adb_handler[address].handler_id = req.reply[2];
return 1;
}
int
adb_try_handler_change(int address, int new_id)
{
int ret;
down(&adb_handler_sem);
ret = try_handler_change(address, new_id);
up(&adb_handler_sem);
return ret;
}
int
adb_get_infos(int address, int *original_address, int *handler_id)
{
down(&adb_handler_sem);
*original_address = adb_handler[address].original_address;
*handler_id = adb_handler[address].handler_id;
up(&adb_handler_sem);
return (*original_address != 0);
}
/*
* /dev/adb device driver.
*/
#define ADB_MAJOR 56 /* major number for /dev/adb */
struct adbdev_state {
spinlock_t lock;
atomic_t n_pending;
struct adb_request *completed;
wait_queue_head_t wait_queue;
int inuse;
};
static void adb_write_done(struct adb_request *req)
{
struct adbdev_state *state = (struct adbdev_state *) req->arg;
unsigned long flags;
if (!req->complete) {
req->reply_len = 0;
req->complete = 1;
}
spin_lock_irqsave(&state->lock, flags);
atomic_dec(&state->n_pending);
if (!state->inuse) {
kfree(req);
if (atomic_read(&state->n_pending) == 0) {
spin_unlock_irqrestore(&state->lock, flags);
kfree(state);
return;
}
} else {
struct adb_request **ap = &state->completed;
while (*ap != NULL)
ap = &(*ap)->next;
req->next = NULL;
*ap = req;
wake_up_interruptible(&state->wait_queue);
}
spin_unlock_irqrestore(&state->lock, flags);
}
static int
do_adb_query(struct adb_request *req)
{
int ret = -EINVAL;
switch(req->data[1])
{
case ADB_QUERY_GETDEVINFO:
if (req->nbytes < 3)
break;
down(&adb_handler_sem);
req->reply[0] = adb_handler[req->data[2]].original_address;
req->reply[1] = adb_handler[req->data[2]].handler_id;
up(&adb_handler_sem);
req->complete = 1;
req->reply_len = 2;
adb_write_done(req);
ret = 0;
break;
}
return ret;
}
static int adb_open(struct inode *inode, struct file *file)
{
struct adbdev_state *state;
if (iminor(inode) > 0 || adb_controller == NULL)
return -ENXIO;
state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
if (state == 0)
return -ENOMEM;
file->private_data = state;
spin_lock_init(&state->lock);
atomic_set(&state->n_pending, 0);
state->completed = NULL;
init_waitqueue_head(&state->wait_queue);
state->inuse = 1;
return 0;
}
static int adb_release(struct inode *inode, struct file *file)
{
struct adbdev_state *state = file->private_data;
unsigned long flags;
lock_kernel();
if (state) {
file->private_data = NULL;
spin_lock_irqsave(&state->lock, flags);
if (atomic_read(&state->n_pending) == 0
&& state->completed == NULL) {
spin_unlock_irqrestore(&state->lock, flags);
kfree(state);
} else {
state->inuse = 0;
spin_unlock_irqrestore(&state->lock, flags);
}
}
unlock_kernel();
return 0;
}
static ssize_t adb_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0;
struct adbdev_state *state = file->private_data;
struct adb_request *req;
wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current);
unsigned long flags;
if (count < 2)
return -EINVAL;
if (count > sizeof(req->reply))
count = sizeof(req->reply);
if (!access_ok(VERIFY_WRITE, buf, count))
return -EFAULT;
req = NULL;
spin_lock_irqsave(&state->lock, flags);
add_wait_queue(&state->wait_queue, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
req = state->completed;
if (req != NULL)
state->completed = req->next;
else if (atomic_read(&state->n_pending) == 0)
ret = -EIO;
if (req != NULL || ret != 0)
break;
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
spin_unlock_irqrestore(&state->lock, flags);
schedule();
spin_lock_irqsave(&state->lock, flags);
}
current->state = TASK_RUNNING;
remove_wait_queue(&state->wait_queue, &wait);
spin_unlock_irqrestore(&state->lock, flags);
if (ret)
return ret;
ret = req->reply_len;
if (ret > count)
ret = count;
if (ret > 0 && copy_to_user(buf, req->reply, ret))
ret = -EFAULT;
kfree(req);
return ret;
}
static ssize_t adb_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int ret/*, i*/;
struct adbdev_state *state = file->private_data;
struct adb_request *req;
if (count < 2 || count > sizeof(req->data))
return -EINVAL;
if (adb_controller == NULL)
return -ENXIO;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
req = (struct adb_request *) kmalloc(sizeof(struct adb_request),
GFP_KERNEL);
if (req == NULL)
return -ENOMEM;
req->nbytes = count;
req->done = adb_write_done;
req->arg = (void *) state;
req->complete = 0;
ret = -EFAULT;
if (copy_from_user(req->data, buf, count))
goto out;
atomic_inc(&state->n_pending);
/* If a probe is in progress or we are sleeping, wait for it to complete */
down(&adb_probe_mutex);
/* Queries are special requests sent to the ADB driver itself */
if (req->data[0] == ADB_QUERY) {
if (count > 1)
ret = do_adb_query(req);
else
ret = -EINVAL;
up(&adb_probe_mutex);
}
/* Special case for ADB_BUSRESET request, all others are sent to
the controller */
else if ((req->data[0] == ADB_PACKET)&&(count > 1)
&&(req->data[1] == ADB_BUSRESET)) {
ret = do_adb_reset_bus();
up(&adb_probe_mutex);
atomic_dec(&state->n_pending);
if (ret == 0)
ret = count;
goto out;
} else {
req->reply_expected = ((req->data[1] & 0xc) == 0xc);
if (adb_controller && adb_controller->send_request)
ret = adb_controller->send_request(req, 0);
else
ret = -ENXIO;
up(&adb_probe_mutex);
}
if (ret != 0) {
atomic_dec(&state->n_pending);
goto out;
}
return count;
out:
kfree(req);
return ret;
}
static struct file_operations adb_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = adb_read,
.write = adb_write,
.open = adb_open,
.release = adb_release,
};
static void
adbdev_init(void)
{
if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {
printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
return;
}
devfs_mk_cdev(MKDEV(ADB_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR, "adb");
adb_dev_class = class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
class_device_create(adb_dev_class, MKDEV(ADB_MAJOR, 0), NULL, "adb");
}