/* * 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); }