739 lines
20 KiB
C
739 lines
20 KiB
C
/*
|
|
* LEGO® MINDSTORMS EV3
|
|
*
|
|
* Copyright (C) 2010-2013 The LEGO Group
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* This UsbFunction file is based on and inheritated from
|
|
* the original file (f_sourcesink.c) and work done by
|
|
* David Brownell
|
|
*
|
|
* >> f_sourcesink.c - USB peripheral source/sink configuration driver <<
|
|
*
|
|
* >> Copyright (C) 2003-2008 David Brownell <<
|
|
* >> Copyright (C) 2008 by Nokia Corporation <<
|
|
*
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* HID IN/OUT Interrupt transfer FUNCTION ...
|
|
*
|
|
*/
|
|
|
|
struct f_rudolf {
|
|
struct usb_function function;
|
|
|
|
struct usb_ep *in_ep;
|
|
struct usb_ep *out_ep;
|
|
};
|
|
|
|
enum // Used for signaling the IN stuff USB-state
|
|
{ // Data from the Brick to the HOST
|
|
USB_DATA_IDLE, //
|
|
USB_DATA_BUSY, // Ongoing USB request
|
|
USB_DATA_PENDING, // Data ready for X-fer, but USB busy
|
|
USB_DATA_READY, // Initial setting
|
|
};
|
|
|
|
int input_state = USB_DATA_IDLE;
|
|
struct usb_ep *save_in_ep;
|
|
struct usb_request *save_in_req;
|
|
|
|
#ifndef PCASM
|
|
static inline struct f_rudolf *func_to_rudolf(struct usb_function *f)
|
|
{
|
|
return container_of(f, struct f_rudolf, function);
|
|
}
|
|
#else
|
|
// Keep Eclipse happy
|
|
#endif
|
|
|
|
static struct usb_interface_descriptor rudolf_intf = {
|
|
.bLength = sizeof rudolf_intf,
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
.bInterfaceNumber = 0,
|
|
.bAlternateSetting = 0,
|
|
.bNumEndpoints = 2, // Just plain in and out
|
|
.bInterfaceClass = USB_CLASS_HID, // We go for NONE custom-driver
|
|
.bInterfaceSubClass = 0,
|
|
.bInterfaceProtocol = 0,
|
|
/* .iInterface = DYNAMIC */
|
|
};
|
|
|
|
static struct hid_descriptor hs_hid_rudolf_desc = {
|
|
.bLength = sizeof hs_hid_rudolf_desc,
|
|
.bDescriptorType = HID_DT_HID,
|
|
.bcdHID = cpu_to_le16(0x0110),
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 0x01, // "The one and only"
|
|
.desc[0].bDescriptorType = 0x22, // Report Descriptor Type - 0x22 = HID
|
|
.desc[0].wDescriptorLength = sizeof hs_hid_report_descriptor,
|
|
/*.desc[0].bDescriptorType = DYNAMIC */
|
|
/*.desc[0].wDescriptorLenght= DYNAMIC */
|
|
};
|
|
|
|
static struct hid_descriptor fs_hid_rudolf_desc = {
|
|
.bLength = sizeof fs_hid_rudolf_desc,
|
|
.bDescriptorType = HID_DT_HID,
|
|
.bcdHID = cpu_to_le16(0x0110),
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 0x01, // "The one and only"
|
|
.desc[0].bDescriptorType = 0x22, // Report Descriptor Type - 0x22 = HID
|
|
.desc[0].wDescriptorLength = sizeof fs_hid_report_descriptor,
|
|
|
|
};
|
|
|
|
/* full speed support: */
|
|
|
|
static struct usb_endpoint_descriptor rudolf_out_fs_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_OUT,
|
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
|
.wMaxPacketSize = cpu_to_le16(64),
|
|
.bInterval = 1, /* 1 = 1 mSec POLL rate for FS */
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor rudolf_in_fs_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
|
.wMaxPacketSize = cpu_to_le16(64),
|
|
.bInterval = 1, /* 1 = 1 mSec POLL rate for FS */
|
|
};
|
|
|
|
/* high speed support: */
|
|
|
|
static struct usb_endpoint_descriptor rudolf_in_hs_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_IN,
|
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
|
.wMaxPacketSize = cpu_to_le16(1024),
|
|
.bInterval = 4, /* Calculated as :
|
|
* 2^(value-1) * 125uS
|
|
* i.e. value 1: 2^(1-1) * 125 uSec = 125 uSec
|
|
* - 4: 2^(4-1) * 125 uSec = 1 mSec
|
|
*/
|
|
};
|
|
|
|
static struct usb_endpoint_descriptor rudolf_out_hs_desc = {
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
.bEndpointAddress = USB_DIR_OUT,
|
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
|
.wMaxPacketSize = cpu_to_le16(1024),
|
|
.bInterval = 4, /* Calculated as :
|
|
* 2^(value-1) * 125uS
|
|
* i.e. value 1: 2^(1-1) * 125 uSec = 125 uSec
|
|
* - 4: 2^(4-1) * 125 uSec = 1 mSec
|
|
*/
|
|
};
|
|
|
|
static struct usb_descriptor_header *hs_rudolf_descs[] = {
|
|
(struct usb_descriptor_header *) &rudolf_intf,
|
|
(struct usb_descriptor_header *) &hs_hid_rudolf_desc,
|
|
(struct usb_descriptor_header *) &rudolf_in_hs_desc,
|
|
(struct usb_descriptor_header *) &rudolf_out_hs_desc,
|
|
NULL,
|
|
};
|
|
|
|
static struct usb_descriptor_header *fs_rudolf_descs[] = {
|
|
(struct usb_descriptor_header *) &rudolf_intf,
|
|
(struct usb_descriptor_header *) &fs_hid_rudolf_desc,
|
|
(struct usb_descriptor_header *) &rudolf_in_fs_desc,
|
|
(struct usb_descriptor_header *) &rudolf_out_fs_desc,
|
|
NULL,
|
|
};
|
|
|
|
/* function-specific strings: */
|
|
|
|
static struct usb_string strings_rudolf[] = {
|
|
[0].s = "Xfer data to and from EV3 brick",
|
|
{ } /* end of list */
|
|
};
|
|
|
|
static struct usb_gadget_strings stringtab_rudolf = {
|
|
.language = 0x0409, /* en-us */
|
|
.strings = strings_rudolf,
|
|
};
|
|
|
|
static struct usb_gadget_strings *rudolf_strings[] = {
|
|
&stringtab_rudolf,
|
|
NULL,
|
|
};
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static int
|
|
f_rudolf_bind(struct usb_configuration *c, struct usb_function *f)
|
|
{
|
|
struct usb_composite_dev *cdev = c->cdev;
|
|
struct f_rudolf *rudolf = func_to_rudolf(f);
|
|
int id;
|
|
|
|
/* allocate interface ID(s) */
|
|
id = usb_interface_id(c, f);
|
|
if (id < 0)
|
|
return id;
|
|
rudolf_intf.bInterfaceNumber = id;
|
|
|
|
/* allocate endpoints */
|
|
rudolf->in_ep = usb_ep_autoconfig(cdev->gadget, &rudolf_in_fs_desc);
|
|
if (!rudolf->in_ep) {
|
|
autoconf_fail:
|
|
ERROR(cdev, "%s: can't autoconfigure on %s\n",
|
|
f->name, cdev->gadget->name);
|
|
return -ENODEV;
|
|
}
|
|
rudolf->in_ep->driver_data = cdev; /* claim */
|
|
|
|
rudolf->out_ep = usb_ep_autoconfig(cdev->gadget, &rudolf_out_fs_desc);
|
|
if (!rudolf->out_ep)
|
|
goto autoconf_fail;
|
|
rudolf->out_ep->driver_data = cdev; /* claim */
|
|
|
|
|
|
/* support high speed hardware */
|
|
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
|
|
|
rudolf_in_hs_desc.bEndpointAddress =
|
|
rudolf_in_fs_desc.bEndpointAddress;
|
|
rudolf_out_hs_desc.bEndpointAddress =
|
|
rudolf_out_fs_desc.bEndpointAddress;
|
|
f->hs_descriptors = hs_rudolf_descs;
|
|
f->descriptors = fs_rudolf_descs;
|
|
}
|
|
|
|
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
|
|
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
|
|
f->name, rudolf->in_ep->name, rudolf->out_ep->name);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
f_rudolf_unbind(struct usb_configuration *c, struct usb_function *f)
|
|
{
|
|
kfree(func_to_rudolf(f));
|
|
}
|
|
|
|
static void usb_req_arm(struct usb_ep *ep, struct usb_request *req)
|
|
{
|
|
int status;
|
|
|
|
if (UsbSpeed.Speed == FULL_SPEED)
|
|
{
|
|
req->length = 64;
|
|
req->actual = 64;
|
|
}
|
|
else
|
|
{
|
|
req->length = 1024;
|
|
req->actual = 1024;
|
|
}
|
|
|
|
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
|
if (status) {
|
|
usb_ep_set_halt(ep);
|
|
/* FIXME recover later ... somehow */
|
|
}
|
|
}
|
|
|
|
static int read_data_from_host(struct usb_request *req)
|
|
{
|
|
unsigned i;
|
|
u8 *buf = req->buf;
|
|
|
|
int from_host_length = 0; // NO ACCESS LOCKS YET
|
|
|
|
// test for actual length > 0
|
|
|
|
for (i = 0; i < req->actual; i++, buf++)
|
|
{
|
|
|
|
usb_char_buffer_out[i] = *buf;
|
|
from_host_length++;
|
|
}
|
|
|
|
return (from_host_length);
|
|
}
|
|
|
|
static void write_data_to_the_host(struct usb_ep *ep, struct usb_request *req)
|
|
{
|
|
unsigned i;
|
|
u8 *buf = req->buf;
|
|
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
printk("WR to HOST req->length = %d\r\n", req->length);
|
|
#endif
|
|
|
|
#undef DEBUG
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
printk("USB = %d, %d\r\n", usb_char_buffer_in[2], usb_char_buffer_in[3]);
|
|
#endif
|
|
#undef DEBUG
|
|
|
|
for (i = 0; i < req->length; i++)
|
|
*buf++ = usb_char_buffer_in[i];
|
|
usb_char_in_length = 0; // Reset and ready
|
|
}
|
|
|
|
static void rudolf_complete(struct usb_ep *ep, struct usb_request *req)
|
|
{
|
|
struct f_rudolf *rudolf = ep->driver_data;
|
|
int status = req->status;
|
|
|
|
|
|
switch ( status ) {
|
|
|
|
case 0: /* normal completion? */
|
|
if (ep == rudolf->out_ep) // An OUT completion?
|
|
{
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
printk("Rudolf_complete OUT\n");
|
|
#endif
|
|
|
|
usb_char_out_length = read_data_from_host(req);
|
|
usb_req_arm(ep, req);
|
|
}
|
|
else // We have an INPUT request complete
|
|
{
|
|
//#define DEBUG
|
|
#ifdef DEBUG
|
|
printk("Rudolf_complete IN\n");
|
|
#endif
|
|
|
|
switch(input_state) // State of Brick data x-fer
|
|
{
|
|
case USB_DATA_READY: //should be BUSY or PENDING....
|
|
|
|
#ifdef DEBUG
|
|
printk("IN_IN_IN - READY ?????\n");
|
|
#endif
|
|
|
|
break;
|
|
|
|
case USB_DATA_PENDING: //
|
|
// #define DEBUG
|
|
#ifdef DEBUG
|
|
printk("IN_IN_IN - PENDING settes to BUSY\n");
|
|
#endif
|
|
|
|
input_state = USB_DATA_BUSY;
|
|
write_data_to_the_host(ep, req);
|
|
usb_req_arm(ep, req); // new request
|
|
break;
|
|
|
|
case USB_DATA_BUSY: //
|
|
#ifdef DEBUG
|
|
printk("IN_IN_IN - BUSY settes to READY\n");
|
|
#endif
|
|
#undef DEBUG
|
|
input_state = USB_DATA_READY;
|
|
// and relax
|
|
break;
|
|
|
|
case USB_DATA_IDLE: // too lazy
|
|
#ifdef DEBUG
|
|
printk("IN_IN_IN - IDLE\n");
|
|
#endif
|
|
//#undef DEBUG
|
|
|
|
break;
|
|
|
|
default: break; // hmmm.
|
|
}
|
|
// Reset the buffer size - Ready again
|
|
usb_char_in_length = 0;
|
|
}
|
|
break;
|
|
|
|
/* this endpoint is normally active while we're configured */
|
|
|
|
case -ESHUTDOWN: /* disconnect from host */
|
|
// REMOVED 26102012 (*pUsbSpeed).Speed = FULL_SPEED;
|
|
case -ECONNABORTED: /* hardware forced ep reset */
|
|
case -ECONNRESET: /* request dequeued */
|
|
//case -ESHUTDOWN: /* disconnect from host */
|
|
if (ep == rudolf->out_ep)
|
|
read_data_from_host(req);
|
|
free_ep_req(ep, req);
|
|
return;
|
|
|
|
case -EOVERFLOW: /* buffer overrun on read means that
|
|
we didn't provide a big enough
|
|
buffer.
|
|
*/
|
|
default:
|
|
//#if 1
|
|
// DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
|
|
// status, req->actual, req->length);
|
|
//#endif
|
|
case -EREMOTEIO: /* short read */
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static int rudolf_start_ep(struct f_rudolf *rudolf, bool is_in)
|
|
{
|
|
struct usb_ep *ep;
|
|
struct usb_request *req;
|
|
int status;
|
|
|
|
ep = is_in ? rudolf->in_ep : rudolf->out_ep;
|
|
req = alloc_ep_req(ep);
|
|
if (!req)
|
|
return -ENOMEM;
|
|
|
|
req->complete = rudolf_complete;
|
|
|
|
#ifdef DEBUG
|
|
printk("UsbSpeed.Speed = %d\n\r", UsbSpeed.Speed);
|
|
#endif
|
|
|
|
if (UsbSpeed.Speed == FULL_SPEED)
|
|
{
|
|
#ifdef DEBUG
|
|
printk("rudolf_start_ep FULL\n\r");
|
|
#endif
|
|
|
|
(*pUsbSpeed).Speed = FULL_SPEED;
|
|
req->length = 64; // Full speed max buffer size
|
|
req->actual = 64;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
printk("rudolf_start_ep HIGH\n\r");
|
|
#endif
|
|
|
|
(*pUsbSpeed).Speed = HIGH_SPEED;
|
|
req->length = 1024; // High speed max buffer size
|
|
req->actual = 1024;
|
|
}
|
|
|
|
if (is_in)
|
|
{
|
|
save_in_ep = ep;
|
|
save_in_req = req;
|
|
|
|
#ifdef DEBUG
|
|
printk("req->length = %d ***** Rudolf_Start_Ep_in\n\r", req->length);
|
|
#endif
|
|
|
|
// reinit_write_data(ep, req);
|
|
input_state = USB_DATA_BUSY;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
printk("***** Rudolf_Start_Ep_out\n");
|
|
#endif
|
|
}
|
|
|
|
status = usb_ep_queue(ep, req, GFP_ATOMIC);
|
|
|
|
if (status) {
|
|
struct usb_composite_dev *cdev;
|
|
|
|
cdev = rudolf->function.config->cdev;
|
|
ERROR(cdev, "start %s %s --> %d\n",
|
|
is_in ? "IN" : "OUT",
|
|
ep->name, status);
|
|
|
|
free_ep_req(ep, req);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void disable_rudolf(struct f_rudolf *rudolf)
|
|
{
|
|
struct usb_composite_dev *cdev;
|
|
|
|
cdev = rudolf->function.config->cdev;
|
|
disable_endpoints(cdev, rudolf->in_ep, rudolf->out_ep);
|
|
VDBG(cdev, "%s disabled\n", rudolf->function.name);
|
|
}
|
|
|
|
static int
|
|
enable_rudolf(struct usb_composite_dev *cdev, struct f_rudolf *rudolf)
|
|
{
|
|
int result = 0;
|
|
const struct usb_endpoint_descriptor *ep_in, *ep_out;
|
|
struct usb_ep *ep;
|
|
|
|
ep_in = ep_choose(cdev->gadget, &rudolf_in_hs_desc, &rudolf_in_fs_desc);
|
|
|
|
ep_out = ep_choose(cdev->gadget, &rudolf_out_hs_desc, &rudolf_out_fs_desc);
|
|
|
|
ep = rudolf->in_ep;
|
|
|
|
result = usb_ep_enable(ep, ep_in);
|
|
|
|
if (result < 0)
|
|
return result;
|
|
|
|
ep->driver_data = rudolf;
|
|
|
|
result = rudolf_start_ep(rudolf, true);
|
|
|
|
if (result < 0) {
|
|
fail:
|
|
ep = rudolf->in_ep;
|
|
usb_ep_disable(ep);
|
|
ep->driver_data = NULL;
|
|
return result;
|
|
}
|
|
|
|
/* one endpoint reads (sinks) anything OUT (from the host) */
|
|
ep = rudolf->out_ep;
|
|
result = usb_ep_enable(ep, ep_out);
|
|
|
|
if (result < 0)
|
|
goto fail;
|
|
|
|
ep->driver_data = rudolf;
|
|
|
|
result = rudolf_start_ep(rudolf, false);
|
|
|
|
if (result < 0) {
|
|
usb_ep_disable(ep);
|
|
ep->driver_data = NULL;
|
|
goto fail;
|
|
}
|
|
|
|
DBG(cdev, "%s enabled\n", rudolf->function.name);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int f_rudolf_set_alt(struct usb_function *f,
|
|
unsigned intf, unsigned alt)
|
|
{
|
|
struct f_rudolf *rudolf = func_to_rudolf(f);
|
|
struct usb_composite_dev *cdev = f->config->cdev;
|
|
|
|
/* we know alt is zero */
|
|
if (rudolf->in_ep->driver_data)
|
|
disable_rudolf(rudolf);
|
|
|
|
return enable_rudolf(cdev, rudolf);
|
|
}
|
|
|
|
static void f_rudolf_disable(struct usb_function *f)
|
|
{
|
|
struct f_rudolf *rudolf = func_to_rudolf(f);
|
|
|
|
disable_rudolf(rudolf);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
static int msg_config(struct usb_configuration *c);
|
|
|
|
static int rudolf_bind_config(struct usb_configuration *c)
|
|
{
|
|
struct f_rudolf *rudolf;
|
|
int status;
|
|
|
|
rudolf = kzalloc(sizeof *rudolf, GFP_KERNEL);
|
|
if (!rudolf)
|
|
return -ENOMEM;
|
|
|
|
rudolf->function.name = "rudolf xfer";
|
|
rudolf->function.descriptors = hs_rudolf_descs;
|
|
rudolf->function.bind = f_rudolf_bind;
|
|
rudolf->function.unbind = f_rudolf_unbind;
|
|
rudolf->function.set_alt = f_rudolf_set_alt;
|
|
rudolf->function.disable = f_rudolf_disable;
|
|
|
|
status = usb_add_function(c, &rudolf->function);
|
|
if (status) {
|
|
kfree(rudolf);
|
|
return status;
|
|
}
|
|
|
|
|
|
status = msg_config(c);
|
|
if (status) {
|
|
kfree(rudolf);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifndef PCASM
|
|
static int rudolf_setup(struct usb_configuration *c,
|
|
const struct usb_ctrlrequest *ctrl)
|
|
{
|
|
struct usb_request *req = c->cdev->req;
|
|
int value = -EOPNOTSUPP;
|
|
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
|
u16 w_value = le16_to_cpu(ctrl->wValue);
|
|
u16 w_length = le16_to_cpu(ctrl->wLength);
|
|
u16 length = 0;
|
|
|
|
/* composite driver infrastructure handles everything except
|
|
* the two control test requests.
|
|
*/
|
|
|
|
switch (ctrl->bRequest) {
|
|
|
|
/*
|
|
* These are the same vendor-specific requests supported by
|
|
* Intel's USB 2.0 compliance test devices. We exceed that
|
|
* device spec by allowing multiple-packet requests.
|
|
*
|
|
* NOTE: the Control-OUT data stays in req->buf ... better
|
|
* would be copying it into a scratch buffer, so that other
|
|
* requests may safely intervene.
|
|
*/
|
|
|
|
case 0x5b: /* control WRITE test -- fill the buffer */
|
|
if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
|
|
goto unknown;
|
|
if (w_value || w_index)
|
|
break;
|
|
/* just read that many bytes into the buffer */
|
|
if (w_length > req->length)
|
|
break;
|
|
value = w_length;
|
|
break;
|
|
case 0x5c: /* control READ test -- return the buffer */
|
|
if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
|
|
goto unknown;
|
|
if (w_value || w_index)
|
|
break;
|
|
/* expect those bytes are still in the buffer; send back */
|
|
if (w_length > req->length)
|
|
break;
|
|
value = w_length;
|
|
break;
|
|
|
|
default:
|
|
unknown:
|
|
VDBG(c->cdev,
|
|
"unknown control req%02x.%02x v%04x i%04x l%d\n",
|
|
ctrl->bRequestType, ctrl->bRequest,
|
|
w_value, w_index, w_length);
|
|
}
|
|
|
|
//HER SKAL HID DESC SENDES!!!
|
|
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
|
|
|
|
|
|
case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
|
|
| USB_REQ_GET_DESCRIPTOR):
|
|
switch (w_value >> 8) {
|
|
case HID_DT_REPORT:
|
|
//VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
|
|
length = w_length;
|
|
length = min_t(unsigned short, length,
|
|
sizeof hs_hid_report_descriptor);
|
|
memcpy(req->buf, hs_hid_report_descriptor, length);
|
|
value = length;
|
|
goto respond;
|
|
break;
|
|
|
|
default:
|
|
//VDBG(cdev, "Unknown decriptor request 0x%x\n",
|
|
// value >> 8);
|
|
goto stall;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
//VDBG(cdev, "Unknown request 0x%x\n",
|
|
// ctrl->bRequest);
|
|
goto stall;
|
|
break;
|
|
}
|
|
|
|
//HERTIL
|
|
/* respond with data transfer or status phase? */
|
|
stall:
|
|
return -EOPNOTSUPP;
|
|
|
|
respond:
|
|
if (value >= 0) {
|
|
VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
|
|
ctrl->bRequestType, ctrl->bRequest,
|
|
w_value, w_index, w_length);
|
|
req->zero = 0;
|
|
req->length = value;
|
|
value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
|
|
if (value < 0)
|
|
ERROR(c->cdev, "source/sinkc response, err %d\n",
|
|
value);
|
|
}
|
|
|
|
/* device either stalls (value < 0) or reports success */
|
|
return value;
|
|
}
|
|
#else
|
|
// Keep Eclipse happy
|
|
#endif
|
|
|
|
static struct usb_configuration rudolf_driver = {
|
|
.label = "rudolf driver",
|
|
.strings = rudolf_strings,
|
|
.bind = rudolf_bind_config,
|
|
.setup = rudolf_setup,
|
|
.bConfigurationValue = 1,
|
|
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
|
/* .iConfiguration = DYNAMIC */
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
|
|
int rudolf_add(struct usb_composite_dev *cdev, bool autoresume)
|
|
{
|
|
int id;
|
|
|
|
/* allocate string ID(s) */
|
|
id = usb_string_id(cdev);
|
|
if (id < 0)
|
|
return id;
|
|
strings_rudolf[0].id = id;
|
|
|
|
rudolf_intf.iInterface = id;
|
|
rudolf_driver.iConfiguration = 1; // id;
|
|
|
|
/* support autoresume for remote wakeup testing */
|
|
if (autoresume)
|
|
rudolf_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
|
|
|
/* support OTG systems */
|
|
if (gadget_is_otg(cdev->gadget)) {
|
|
rudolf_driver.descriptors = otg_desc;
|
|
rudolf_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
|
}
|
|
|
|
return usb_add_config(cdev, &rudolf_driver);
|
|
}
|