+++ /dev/null
-/*
- * f_charger.c -- USB HID function driver
- *
- * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
- * Copyright (C) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The Linux Foundation chooses to take subject only to the GPLv2 license
- * terms, and distributes only under these terms.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/wait.h>
-#include <linux/hid.h>
-
-struct f_charger {
- struct usb_ep *in_ep;
- struct usb_function func;
-};
-
-static inline struct f_charger *func_to_charger(struct usb_function *f)
-{
- return container_of(f, struct f_charger, func);
-}
-
-static const uint8_t the_report_descriptor[] = {
- 0x06, 0xA0, 0xFF, 0x09, 0xA5, 0xA1, 0x01, 0x09,
- 0xA6, 0x09, 0xA7, 0x15, 0x80, 0x25, 0x7F, 0x75,
- 0x08, 0x95, 0x02, 0x81, 0x02, 0x09, 0xA9, 0x15,
- 0x80, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x91,
- 0x02, 0xC0,
-};
-
-
-static struct usb_interface_descriptor charger_interface_desc = {
- .bLength = sizeof(charger_interface_desc),
- .bDescriptorType = USB_DT_INTERFACE,
- /* .bInterfaceNumber = DYNAMIC */
- .bAlternateSetting = 0,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_HID,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 0,
- /* .iInterface = DYNAMIC */
-};
-
-static struct hid_descriptor charger_hid_desc = {
- .bLength = sizeof(charger_hid_desc),
- .bDescriptorType = 0x21,
- .bcdHID = 0x0111,
- .bCountryCode = 0x00,
- .bNumDescriptors = 0x1,
- .desc[0].bDescriptorType = 0x22,
- .desc[0].wDescriptorLength = sizeof(the_report_descriptor),
-};
-
-/* Super-Speed Support */
-
-static struct usb_endpoint_descriptor charger_ss_in_ep_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 1 ,
- .bInterval = 16,
-};
-
-static struct usb_descriptor_header *charger_ss_descriptors[] = {
- (struct usb_descriptor_header *)&charger_interface_desc,
- (struct usb_descriptor_header *)&charger_hid_desc,
- (struct usb_descriptor_header *)&charger_ss_in_ep_desc,
- NULL,
-};
-
-/* High-Speed Support */
-static struct usb_endpoint_descriptor charger_hs_in_ep_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 1 ,
- .bInterval = 16,
-};
-
-static struct usb_descriptor_header *charger_hs_descriptors[] = {
- (struct usb_descriptor_header *)&charger_interface_desc,
- (struct usb_descriptor_header *)&charger_hid_desc,
- (struct usb_descriptor_header *)&charger_hs_in_ep_desc,
- NULL,
-};
-
-/* Full-Speed Support */
-
-static struct usb_endpoint_descriptor charger_fs_in_ep_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 1,
- .bInterval = 16,
-};
-
-static struct usb_descriptor_header *charger_fs_descriptors[] = {
- (struct usb_descriptor_header *)&charger_interface_desc,
- (struct usb_descriptor_header *)&charger_hid_desc,
- (struct usb_descriptor_header *)&charger_fs_in_ep_desc,
- NULL,
-};
-
-/* Strings */
-
-#define CT_FUNC_HID_IDX 0
-
-static struct usb_string ct_func_string_defs[] = {
- [CT_FUNC_HID_IDX].s = "HID Interface",
- {}, /* end of list */
-};
-
-static struct usb_gadget_strings ct_func_string_table = {
- .language = 0x0409, /* en-US */
- .strings = ct_func_string_defs,
-};
-
-static struct usb_gadget_strings *ct_func_strings[] = {
- &ct_func_string_table,
- NULL,
-};
-
-
-static void charger_disable(struct usb_function *f)
-{
- struct f_charger *charger = func_to_charger(f);
- usb_ep_disable(charger->in_ep);
- charger->in_ep->driver_data = NULL;
-}
-
-static int hid_setup(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
-{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = cdev->req;
- int status = 0;
- __u16 value, length;
- bool resp_stall = false;
-
- value = le16_to_cpu(ctrl->wValue);
- length = le16_to_cpu(ctrl->wLength);
-
- VDBG(cdev,
- "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n",
- ctrl->bRequestType, ctrl->bRequest, value);
-
- switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
- case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
- | HID_REQ_GET_REPORT):
- VDBG(cdev, "get_report\n");
-
- /* send an empty report */
- length = min_t(unsigned, length,
- charger_hid_desc.desc[0].wDescriptorLength);
- memset(req->buf, 0x0, length);
- break;
-
- case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
- | HID_REQ_GET_PROTOCOL):
- VDBG(cdev, "get_protocol\n");
- resp_stall = true;
- break;
-
- case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
- | HID_REQ_SET_REPORT):
- VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
- resp_stall = true;
- break;
-
- case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
- | HID_REQ_SET_PROTOCOL):
- VDBG(cdev, "set_protocol\n");
- resp_stall = true;
- break;
-
- case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
- | USB_REQ_GET_DESCRIPTOR):
- switch (value >> 8) {
-
- case HID_DT_HID:
- VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
- length = min_t(unsigned short, length,
- charger_hid_desc.bLength);
- memcpy(req->buf, &charger_hid_desc, length);
- break;
-
- case HID_DT_REPORT:
- VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
- length = min_t(unsigned short, length,
- charger_hid_desc.desc[0].wDescriptorLength);
- memcpy(req->buf, &the_report_descriptor, length);
- break;
-
- default:
- VDBG(cdev, "Unknown descriptor request 0x%x\n",
- value >> 8);
- resp_stall = true;
- break;
- }
- break;
-
- default:
- VDBG(cdev, "Unknown request 0x%x\n",
- ctrl->bRequest);
- resp_stall = true;
- break;
- }
-
- if (resp_stall) {
- ERROR(cdev, "usb ep stall\n");
- return -EOPNOTSUPP;
- }
-
- req->zero = 0;
- req->length = length;
- status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
- if (status < 0)
- ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
- return status;
-}
-
-static int charger_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct f_charger *charger = func_to_charger(f);
- int status = 0;
-
- VDBG(cdev, "charger_set_alt intf:%d alt:%d\n", intf, alt);
-
- if (charger->in_ep != NULL) {
- /* restart endpoint */
- if (charger->in_ep->driver_data != NULL)
- usb_ep_disable(charger->in_ep);
-
- status = config_ep_by_speed(f->config->cdev->gadget, f,
- charger->in_ep);
- if (status) {
- charger->in_ep->desc = NULL;
- ERROR(cdev, "config_ep_by_speed FAILED!\n");
- goto fail;
- }
- status = usb_ep_enable(charger->in_ep);
- if (status < 0) {
- ERROR(cdev, "Enable IN endpoint FAILED!\n");
- goto fail;
- }
- charger->in_ep->driver_data = charger;
- }
-fail:
- return status;
-}
-
-
-
-static int charger_bind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_ep *ep;
- struct f_charger *charger = func_to_charger(f);
- int status;
-
- /* allocate instance-specific interface IDs, and patch descriptors */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
-
- charger_interface_desc.bInterfaceNumber = status;
-
- /* allocate instance-specific endpoints */
- status = -ENODEV;
- ep = usb_ep_autoconfig(c->cdev->gadget, &charger_fs_in_ep_desc);
- if (!ep)
- goto fail;
- ep->driver_data = c->cdev; /* claim */
- charger->in_ep = ep;
-
- /* copy descriptors */
- f->fs_descriptors = usb_copy_descriptors(charger_fs_descriptors);
- if (!f->fs_descriptors)
- goto fail;
-
- if (gadget_is_dualspeed(c->cdev->gadget)) {
- charger_hs_in_ep_desc.bEndpointAddress =
- charger_fs_in_ep_desc.bEndpointAddress;
-
- f->hs_descriptors =
- usb_copy_descriptors(charger_hs_descriptors);
- if (!f->hs_descriptors)
- goto fail;
- }
-
- if (gadget_is_superspeed(c->cdev->gadget)) {
- charger_ss_in_ep_desc.bEndpointAddress =
- charger_fs_in_ep_desc.bEndpointAddress;
-
- f->ss_descriptors =
- usb_copy_descriptors(charger_ss_descriptors);
- if (!f->ss_descriptors)
- goto fail;
- }
-
- return 0;
-
-fail:
- ERROR(f->config->cdev, "charger_bind FAILED\n");
-
- if (f->ss_descriptors)
- usb_free_descriptors(f->ss_descriptors);
- if (f->hs_descriptors)
- usb_free_descriptors(f->hs_descriptors);
- if (f->fs_descriptors)
- usb_free_descriptors(f->fs_descriptors);
-
- return status;
-}
-
-static void charger_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_charger *charger = func_to_charger(f);
-
- /* disable/free request and end point */
- usb_ep_disable(charger->in_ep);
-
- /* free descriptors copies */
- if (gadget_is_superspeed(c->cdev->gadget))
- usb_free_descriptors(f->ss_descriptors);
- if (gadget_is_dualspeed(c->cdev->gadget))
- usb_free_descriptors(f->hs_descriptors);
-
- usb_free_descriptors(f->fs_descriptors);
-
- kfree(charger);
-}
-
-static int charger_bind_config(struct usb_configuration *c)
-{
- struct f_charger *charger;
- int status;
-
- /* allocate and initialize one new instance */
- charger = kzalloc(sizeof(*charger), GFP_KERNEL);
- if (!charger)
- return -ENOMEM;
-
- charger->func.name = "charging";
- charger->func.strings = ct_func_strings;
- charger->func.bind = charger_bind;
- charger->func.unbind = charger_unbind;
- charger->func.set_alt = charger_set_alt;
- charger->func.disable = charger_disable;
- charger->func.setup = hid_setup;
-
- status = usb_add_function(c, &charger->func);
- if (status)
- kfree(charger);
-
- return status;
-}