blob: 54950c03c52da6f06cca086ed57d68f040359de0 [file] [log] [blame]
/*
* diag.c -- Diag Gadget driver
*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/platform_device.h>
#include "u_diag.h"
/*-------------------------------------------------------------------------*/
static const char longname[] = "diag";
USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_VENDOR_NUM 0x05C6
#define DRIVER_PRODUCT_NUM 0x9091
/* string IDs are assigned dynamically */
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
.bNumConfigurations = 1,
};
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "Qualcomm Technologies",
[USB_GADGET_PRODUCT_IDX].s = "Diag Gadget",
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* 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_function_instance *fi_diag;
static struct usb_function *f_diag;
static int diag_gadget_unbind(struct usb_composite_dev *dev)
{
usb_put_function(f_diag);
usb_put_function_instance(fi_diag);
return 0;
}
static struct usb_configuration diag_config = {
.label = "diag",
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
static int diag_bind_config(struct usb_configuration *c)
{
int status;
f_diag = usb_get_function(fi_diag);
if (IS_ERR(f_diag))
return PTR_ERR(f_diag);
status = usb_add_function(c, f_diag);
if (status < 0) {
usb_put_function(f_diag);
return status;
}
return 0;
}
static int diag_gadget_bind(struct usb_composite_dev *cdev)
{
struct diag_opts *diag_opts;
int status;
fi_diag = usb_get_function_instance("diag");
if (IS_ERR(fi_diag))
return PTR_ERR(fi_diag);
diag_opts = container_of(fi_diag, struct diag_opts, func_inst);
fi_diag->set_inst_name(fi_diag, "diag");
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto put;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
diag_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &diag_config, diag_bind_config);
if (status < 0)
goto put;
usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
put:
usb_put_function_instance(fi_diag);
return status;
}
static struct usb_composite_driver diag_gadget_driver = {
.name = (char *) longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = diag_gadget_bind,
.unbind = diag_gadget_unbind,
};
static int diag_gadget_probe(struct platform_device *pdev)
{
usb_composite_probe(&diag_gadget_driver);
return 0;
}
static int diag_gadget_remove(struct platform_device *pdev)
{
usb_composite_unregister(&diag_gadget_driver);
return 0;
}
static const struct of_device_id diag_gadget_table[] = {
{.compatible = "qti,gadget-diag"},
{},
};
MODULE_DEVICE_TABLE(of, diag_gadget_table);
static struct platform_driver diag_gadget_platform_driver = {
.probe = diag_gadget_probe,
.remove = diag_gadget_remove,
.driver = {
.name = "DIAG Gadget Platform",
.owner = THIS_MODULE,
.of_match_table = diag_gadget_table,
},
};
module_platform_driver(diag_gadget_platform_driver);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Diag Gadget Driver");
MODULE_LICENSE("Dual BSD/GPL");