mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-04 23:31:53 +02:00
432 lines
10 KiB
C
432 lines
10 KiB
C
|
/*
|
||
|
* Gadget Driver for Android
|
||
|
*
|
||
|
* Copyright (C) 2008 Google, Inc.
|
||
|
* Author: Mike Lockwood <lockwood@android.com>
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/* #define DEBUG */
|
||
|
/* #define VERBOSE_DEBUG */
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/fs.h>
|
||
|
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/utsname.h>
|
||
|
#include <linux/miscdevice.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
|
||
|
#include <linux/usb/android.h>
|
||
|
#include <linux/usb/ch9.h>
|
||
|
#include <linux/usb/composite.h>
|
||
|
#include <linux/usb/gadget.h>
|
||
|
|
||
|
#include "f_mass_storage.h"
|
||
|
#include "f_adb.h"
|
||
|
|
||
|
#include "gadget_chips.h"
|
||
|
|
||
|
MODULE_AUTHOR("Mike Lockwood");
|
||
|
MODULE_DESCRIPTION("Android Composite USB Driver");
|
||
|
MODULE_LICENSE("GPL");
|
||
|
MODULE_VERSION("1.0");
|
||
|
|
||
|
static char *g_android_function = "mass";
|
||
|
module_param_named(function, g_android_function, charp, S_IRUGO);
|
||
|
|
||
|
static const char longname[] = "Gadget Android";
|
||
|
|
||
|
/* Default vendor and product IDs, overridden by platform data */
|
||
|
#define VENDOR_ID 0x0BB4
|
||
|
#define PRODUCT_ID 0x0001
|
||
|
#define ADB_PRODUCT_ID 0x0C01
|
||
|
|
||
|
/* Platform data */
|
||
|
static struct android_usb_platform_data android_platform_data = {
|
||
|
.vendor_id = 0x18D1,
|
||
|
.product_id = 0x0001,
|
||
|
.adb_product_id = 0x0002,
|
||
|
.version = 0x1,
|
||
|
.product_name = "USB Mass Storage",
|
||
|
.manufacturer_name = "Ingenic",
|
||
|
.serial_number = "01234567890ABCDEF",
|
||
|
};
|
||
|
|
||
|
/* LUN attribute table - Which also decides the amount of LUN */
|
||
|
static lun_attr_t lun_attr_table[] = {
|
||
|
{ /* LUN 0 */
|
||
|
"Ingenic", /* LUN 0 - Vendor */
|
||
|
"USB Disk", /* LUN 0 - Product */
|
||
|
0,
|
||
|
},
|
||
|
{ /* LUN 1 */
|
||
|
"Ingenic", /* LUN 1 - Vendor */
|
||
|
"SD Card Reader", /* LUN 1 - Product */
|
||
|
0,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
struct android_dev {
|
||
|
struct usb_gadget *gadget;
|
||
|
struct usb_composite_dev *cdev;
|
||
|
|
||
|
int product_id;
|
||
|
int adb_product_id;
|
||
|
int version;
|
||
|
|
||
|
int adb_enabled;
|
||
|
int nluns;
|
||
|
};
|
||
|
|
||
|
static atomic_t adb_enable_excl;
|
||
|
static struct android_dev *_android_dev;
|
||
|
|
||
|
/* string IDs are assigned dynamically */
|
||
|
|
||
|
#define STRING_MANUFACTURER_IDX 0
|
||
|
#define STRING_PRODUCT_IDX 1
|
||
|
#define STRING_SERIAL_IDX 2
|
||
|
#define STRING_MS_OS_IDX 3
|
||
|
|
||
|
#define STRING_MS_OS_ID 0xee
|
||
|
|
||
|
/* String Table */
|
||
|
static struct usb_string strings_dev[] = {
|
||
|
/* These dummy values should be overridden by platform data */
|
||
|
[STRING_MANUFACTURER_IDX].s = "Android",
|
||
|
[STRING_PRODUCT_IDX].s = "Android",
|
||
|
[STRING_SERIAL_IDX].s = "0123456789ABCDEF",
|
||
|
[STRING_MS_OS_IDX].s = "Microsoft",
|
||
|
{ } /* end of list */
|
||
|
};
|
||
|
|
||
|
static struct usb_gadget_strings stringtab_dev = {
|
||
|
.language = 0x0409, /* en-us */
|
||
|
.strings = strings_dev,
|
||
|
};
|
||
|
|
||
|
static struct usb_gadget_strings *dev_strings[] = {
|
||
|
&stringtab_dev,
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
static struct usb_device_descriptor device_desc = {
|
||
|
.bLength = sizeof(device_desc),
|
||
|
.bDescriptorType = USB_DT_DEVICE,
|
||
|
.bcdUSB = __constant_cpu_to_le16(0x0200),
|
||
|
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
||
|
.idVendor = __constant_cpu_to_le16(VENDOR_ID),
|
||
|
.idProduct = __constant_cpu_to_le16(PRODUCT_ID),
|
||
|
.bcdDevice = __constant_cpu_to_le16(0xffff),
|
||
|
.bNumConfigurations = 1,
|
||
|
};
|
||
|
|
||
|
static int android_bind_config(struct usb_configuration *c)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
printk(KERN_DEBUG "android_bind_config\n");
|
||
|
|
||
|
if (!strcmp(g_android_function, "adb")) {
|
||
|
printk(KERN_INFO"Andorid Gadget: Function -> adb.\n");
|
||
|
|
||
|
ret = adb_function_add(c);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}else{
|
||
|
printk(KERN_INFO"Android Gadget: Function -> USB Mass storage.\n");
|
||
|
|
||
|
ret = mass_storage_function_add(c, lun_attr_table, sizeof(lun_attr_table) / sizeof(lun_attr_t));
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct usb_configuration android_config = {
|
||
|
.label = "android",
|
||
|
.bind = android_bind_config,
|
||
|
.bConfigurationValue = 1,
|
||
|
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
|
||
|
.bMaxPower = 0x80, /* 250ma */
|
||
|
};
|
||
|
|
||
|
|
||
|
static int android_bind(struct usb_composite_dev *cdev)
|
||
|
{
|
||
|
struct android_dev *dev = _android_dev;
|
||
|
struct usb_gadget *gadget = cdev->gadget;
|
||
|
int gcnum;
|
||
|
int id;
|
||
|
int ret;
|
||
|
|
||
|
printk(KERN_INFO "android_bind\n");
|
||
|
|
||
|
/* Allocate string descriptor numbers ... note that string
|
||
|
* contents can be overridden by the composite_dev glue.
|
||
|
*/
|
||
|
id = usb_string_id(cdev);
|
||
|
if (id < 0)
|
||
|
return id;
|
||
|
strings_dev[STRING_MANUFACTURER_IDX].id = id;
|
||
|
device_desc.iManufacturer = id;
|
||
|
|
||
|
id = usb_string_id(cdev);
|
||
|
if (id < 0)
|
||
|
return id;
|
||
|
strings_dev[STRING_PRODUCT_IDX].id = id;
|
||
|
device_desc.iProduct = id;
|
||
|
|
||
|
id = usb_string_id(cdev);
|
||
|
if (id < 0)
|
||
|
return id;
|
||
|
strings_dev[STRING_SERIAL_IDX].id = id;
|
||
|
device_desc.iSerialNumber = id;
|
||
|
|
||
|
strings_dev[STRING_MS_OS_IDX].id = STRING_MS_OS_ID;
|
||
|
|
||
|
/* register our configuration */
|
||
|
ret = usb_add_config(cdev, &android_config);
|
||
|
if (ret) {
|
||
|
printk(KERN_ERR "usb_add_config failed\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
gcnum = usb_gadget_controller_number(gadget);
|
||
|
if (gcnum >= 0)
|
||
|
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
|
||
|
else {
|
||
|
/* gadget zero is so simple (for now, no altsettings) that
|
||
|
* it SHOULD NOT have problems with bulk-capable hardware.
|
||
|
* so just warn about unrcognized controllers -- don't panic.
|
||
|
*
|
||
|
* things like configuration and altsetting numbering
|
||
|
* can need hardware-specific attention though.
|
||
|
*/
|
||
|
pr_warning("%s: controller '%s' not recognized\n",
|
||
|
longname, gadget->name);
|
||
|
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
|
||
|
}
|
||
|
|
||
|
usb_gadget_set_selfpowered(gadget);
|
||
|
dev->cdev = cdev;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct usb_composite_driver android_usb_driver = {
|
||
|
.name = "android_usb",
|
||
|
.dev = &device_desc,
|
||
|
.strings = dev_strings,
|
||
|
.bind = android_bind,
|
||
|
};
|
||
|
|
||
|
static void enable_adb(struct android_dev *dev, int enable)
|
||
|
{
|
||
|
if (enable != dev->adb_enabled) {
|
||
|
dev->adb_enabled = enable;
|
||
|
adb_function_enable(enable);
|
||
|
|
||
|
/* set product ID to the appropriate value */
|
||
|
if (enable)
|
||
|
device_desc.idProduct =
|
||
|
__constant_cpu_to_le16(dev->adb_product_id);
|
||
|
else
|
||
|
device_desc.idProduct =
|
||
|
__constant_cpu_to_le16(dev->product_id);
|
||
|
if (dev->cdev)
|
||
|
dev->cdev->desc.idProduct = device_desc.idProduct;
|
||
|
|
||
|
/* force reenumeration */
|
||
|
if (dev->cdev && dev->cdev->gadget &&
|
||
|
dev->cdev->gadget->speed != USB_SPEED_UNKNOWN) {
|
||
|
usb_gadget_disconnect(dev->cdev->gadget);
|
||
|
msleep(10);
|
||
|
usb_gadget_connect(dev->cdev->gadget);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int adb_enable_open(struct inode *ip, struct file *fp)
|
||
|
{
|
||
|
if (atomic_inc_return(&adb_enable_excl) != 1) {
|
||
|
atomic_dec(&adb_enable_excl);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(g_android_function, "adb")) {
|
||
|
printk(KERN_INFO "enabling adb\n");
|
||
|
enable_adb(_android_dev, 1);
|
||
|
return 0;
|
||
|
}else
|
||
|
return -ENODEV;
|
||
|
|
||
|
}
|
||
|
|
||
|
static int adb_enable_release(struct inode *ip, struct file *fp)
|
||
|
{
|
||
|
printk(KERN_INFO "disabling adb\n");
|
||
|
enable_adb(_android_dev, 0);
|
||
|
atomic_dec(&adb_enable_excl);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct file_operations adb_enable_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = adb_enable_open,
|
||
|
.release = adb_enable_release,
|
||
|
};
|
||
|
|
||
|
static struct miscdevice adb_enable_device = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = "android_adb_enable",
|
||
|
.fops = &adb_enable_fops,
|
||
|
};
|
||
|
|
||
|
static int __init android_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct android_usb_platform_data *pdata = pdev->dev.platform_data;
|
||
|
struct android_dev *dev = _android_dev;
|
||
|
|
||
|
printk(KERN_INFO "android_probe:%s, pdata: %p\n", pdev->name, pdata);
|
||
|
|
||
|
if (pdata) {
|
||
|
if (pdata->vendor_id)
|
||
|
device_desc.idVendor =
|
||
|
__constant_cpu_to_le16(pdata->vendor_id);
|
||
|
if (pdata->product_id) {
|
||
|
dev->product_id = pdata->product_id;
|
||
|
device_desc.idProduct =
|
||
|
__constant_cpu_to_le16(pdata->product_id);
|
||
|
}
|
||
|
if (pdata->adb_product_id)
|
||
|
dev->adb_product_id = pdata->adb_product_id;
|
||
|
if (pdata->version)
|
||
|
dev->version = pdata->version;
|
||
|
|
||
|
if (pdata->product_name)
|
||
|
strings_dev[STRING_PRODUCT_IDX].s = pdata->product_name;
|
||
|
if (pdata->manufacturer_name)
|
||
|
strings_dev[STRING_MANUFACTURER_IDX].s =
|
||
|
pdata->manufacturer_name;
|
||
|
if (pdata->serial_number)
|
||
|
strings_dev[STRING_SERIAL_IDX].s = pdata->serial_number;
|
||
|
dev->nluns = pdata->nluns;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void android_platform_device_release(struct device *dev) {
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
static struct platform_device android_platform_device = {
|
||
|
.name = "android_usb",
|
||
|
.id = -1,
|
||
|
.dev = {
|
||
|
.platform_data = &android_platform_data,
|
||
|
.release = android_platform_device_release,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static struct platform_driver android_platform_driver = {
|
||
|
.driver = { .name = "android_usb", },
|
||
|
.probe = android_probe,
|
||
|
};
|
||
|
|
||
|
static int __init init(void)
|
||
|
{
|
||
|
struct android_dev *dev;
|
||
|
|
||
|
unsigned long status = 0;
|
||
|
|
||
|
int ret;
|
||
|
|
||
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||
|
if (!dev)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
set_bit(0, &status);
|
||
|
|
||
|
/* set default values, which should be overridden by platform data */
|
||
|
dev->product_id = PRODUCT_ID;
|
||
|
dev->adb_product_id = ADB_PRODUCT_ID;
|
||
|
_android_dev = dev;
|
||
|
|
||
|
ret = platform_device_register(&android_platform_device);
|
||
|
if (ret) {
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
set_bit(1, &status);
|
||
|
|
||
|
ret = platform_driver_register(&android_platform_driver);
|
||
|
if (ret) {
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
set_bit(2, &status);
|
||
|
|
||
|
ret = misc_register(&adb_enable_device);
|
||
|
if (ret) {
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
set_bit(3, &status);
|
||
|
|
||
|
ret = usb_composite_register(&android_usb_driver);
|
||
|
if (ret) {
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
printk(KERN_INFO "Android Gadget Initialized.\n");
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err:
|
||
|
if (test_bit(3, &status)) {
|
||
|
misc_deregister(&adb_enable_device);
|
||
|
}
|
||
|
|
||
|
if (test_bit(2, &status)) {
|
||
|
platform_driver_unregister(&android_platform_driver);
|
||
|
}
|
||
|
|
||
|
if (test_bit(1, &status)) {
|
||
|
platform_device_unregister(&android_platform_device);
|
||
|
}
|
||
|
|
||
|
if (test_bit(0, &status)) {
|
||
|
kfree(dev);
|
||
|
dev = NULL;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
module_init(init);
|
||
|
|
||
|
static void __exit cleanup(void)
|
||
|
{
|
||
|
usb_composite_unregister(&android_usb_driver);
|
||
|
misc_deregister(&adb_enable_device);
|
||
|
platform_device_unregister(&android_platform_device);
|
||
|
platform_driver_unregister(&android_platform_driver);
|
||
|
kfree(_android_dev);
|
||
|
_android_dev = NULL;
|
||
|
}
|
||
|
module_exit(cleanup);
|