summaryrefslogtreecommitdiff
path: root/src/usbh_driver_gp_xbox.c
diff options
context:
space:
mode:
authorAmir Hammad <amir.hammad@hotmail.com>2015-04-01 16:22:05 +0200
committerAmir Hammad <amir.hammad@hotmail.com>2015-04-01 16:22:05 +0200
commit7acc6fe474766788687a5257be21ac549bed77f3 (patch)
tree859e621840f9362e60fec116b7528ad16202871f /src/usbh_driver_gp_xbox.c
downloadsecure-hid-7acc6fe474766788687a5257be21ac549bed77f3.tar.gz
secure-hid-7acc6fe474766788687a5257be21ac549bed77f3.tar.bz2
secure-hid-7acc6fe474766788687a5257be21ac549bed77f3.zip
libusbhost: Open source USB host stack for embedded devices
First public version, date: 1.4.2015 Signed-off-by: Amir Hammad <amir.hammad@hotmail.com>
Diffstat (limited to 'src/usbh_driver_gp_xbox.c')
-rw-r--r--src/usbh_driver_gp_xbox.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/src/usbh_driver_gp_xbox.c b/src/usbh_driver_gp_xbox.c
new file mode 100644
index 0000000..52955d9
--- /dev/null
+++ b/src/usbh_driver_gp_xbox.c
@@ -0,0 +1,420 @@
+/*
+ * This file is part of the libusbhost library
+ * hosted at http://github.com/libusbhost/libusbhost
+ *
+ * Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
+ *
+ *
+ * libusbhost is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "usart_helpers.h"
+#include "usbh_driver_gp_xbox.h"
+#include "driver/usbh_device_driver.h"
+
+#include <stdint.h>
+#include <libopencm3/usb/usbstd.h>
+
+static void *gp_xbox_init(void *usbh_dev);
+static bool gp_xbox_analyze_descriptor(void *drvdata, void *descriptor);
+static void gp_xbox_poll(void *drvdata, uint32_t tflp);
+static void gp_xbox_remove(void *drvdata);
+
+static const usbh_dev_driver_info_t usbh_gp_xbox_driver_info = {
+ .deviceClass = 0xff,
+ .deviceSubClass = 0xff,
+ .deviceProtocol = 0xff,
+ .idVendor = 0x045e,
+ .idProduct = 0x028e,
+ .ifaceClass = 0xff,
+ .ifaceSubClass = 93,
+ .ifaceProtocol = 0x01
+};
+
+const usbh_dev_driver_t usbh_gp_xbox_driver = {
+ .init = gp_xbox_init,
+ .analyze_descriptor = gp_xbox_analyze_descriptor,
+ .poll = gp_xbox_poll,
+ .remove = gp_xbox_remove,
+ .info = &usbh_gp_xbox_driver_info
+};
+
+enum STATES {
+ STATE_INACTIVE,
+ STATE_READING_COMPLETE,
+ STATE_READING_REQUEST,
+ STATE_SET_CONFIGURATION_REQUEST,
+ STATE_SET_CONFIGURATION_EMPTY_READ,
+ STATE_SET_CONFIGURATION_COMPLETE
+};
+
+#define GP_XBOX_CORRECT_TRANSFERRED_LENGTH 20
+
+struct _gp_xbox_device {
+ usbh_device_t *usbh_device;
+ uint8_t buffer[USBH_GP_XBOX_BUFFER];
+ uint16_t endpoint_in_maxpacketsize;
+ uint8_t endpoint_in_address;
+ enum STATES state_next;
+ uint8_t endpoint_in_toggle;
+ uint8_t device_id;
+ uint8_t configuration_value;
+};
+typedef struct _gp_xbox_device gp_xbox_device_t;
+
+static gp_xbox_device_t gp_xbox_device[USBH_GP_XBOX_MAX_DEVICES];
+static const gp_xbox_config_t *gp_xbox_config;
+
+static bool initialized = false;
+static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox);
+
+void gp_xbox_driver_init(const gp_xbox_config_t *config)
+{
+ if (!config) {
+ return;
+ }
+ initialized = true;
+ uint32_t i;
+ gp_xbox_config = config;
+ for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
+ gp_xbox_device[i].state_next = STATE_INACTIVE;
+ }
+}
+
+/**
+ *
+ *
+ */
+static void *gp_xbox_init(void *usbh_dev)
+{
+ if (!initialized) {
+ LOG_PRINTF("driver not initialized");
+ return false;
+ }
+
+ uint32_t i;
+ gp_xbox_device_t *drvdata = 0;
+
+ // find free data space for gp_xbox device
+ for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
+ if (gp_xbox_device[i].state_next == STATE_INACTIVE) {
+ drvdata = &gp_xbox_device[i];
+ drvdata->device_id = i;
+ drvdata->endpoint_in_address = 0;
+ drvdata->endpoint_in_toggle = 0;
+ drvdata->usbh_device = usbh_dev;
+ break;
+ }
+ }
+
+ return drvdata;
+}
+
+/**
+ * Returns true if all needed data are parsed
+ */
+static bool gp_xbox_analyze_descriptor(void *drvdata, void *descriptor)
+{
+ gp_xbox_device_t *gp_xbox = drvdata;
+ uint8_t desc_type = ((uint8_t *)descriptor)[1];
+ switch (desc_type) {
+ case USB_DT_CONFIGURATION:
+ {
+ struct usb_config_descriptor *cfg = (struct usb_config_descriptor*)descriptor;
+ gp_xbox->configuration_value = cfg->bConfigurationValue;
+ }
+ break;
+ case USB_DT_DEVICE:
+ break;
+ case USB_DT_INTERFACE:
+ break;
+ case USB_DT_ENDPOINT:
+ {
+ struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor*)descriptor;
+ if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
+ uint8_t epaddr = ep->bEndpointAddress;
+ if (epaddr & (1<<7)) {
+ gp_xbox->endpoint_in_address = epaddr&0x7f;
+ if (ep->wMaxPacketSize < USBH_GP_XBOX_BUFFER) {
+ gp_xbox->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
+ } else {
+ gp_xbox->endpoint_in_maxpacketsize = USBH_GP_XBOX_BUFFER;
+ }
+ }
+
+ if (gp_xbox->endpoint_in_address) {
+ gp_xbox->state_next = STATE_SET_CONFIGURATION_REQUEST;
+ return true;
+ }
+ }
+ }
+ break;
+ // TODO Class Specific descriptors
+ default:
+ break;
+ }
+ return false;
+}
+
+static void parse_data(usbh_device_t *dev)
+{
+ gp_xbox_device_t *gp_xbox = dev->drvdata;
+
+ uint8_t *packet = gp_xbox->buffer;
+
+ gp_xbox_packet_t gp_xbox_packet;
+ gp_xbox_packet.buttons = 0;
+
+ // DPAD
+ const uint8_t data1 = packet[2];
+ const uint8_t data2 = packet[3];
+ if (data1 & (1 << 0)) {
+ gp_xbox_packet.buttons |= GP_XBOX_DPAD_TOP;
+ }
+
+ if (data1 & (1 << 1)) {
+ gp_xbox_packet.buttons |= GP_XBOX_DPAD_BOTTOM;
+ }
+
+ if (data1 & (1 << 2)) {
+ gp_xbox_packet.buttons |= GP_XBOX_DPAD_LEFT;
+ }
+
+ if (data1 & (1 << 3)) {
+ gp_xbox_packet.buttons |= GP_XBOX_DPAD_RIGHT;
+ }
+
+ // Start + select
+
+ if (data1 & (1 << 4)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_START;
+ }
+
+ if (data1 & (1 << 5)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_SELECT;
+ }
+
+ // axis buttons
+
+ if (data1 & (1 << 6)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_LEFT;
+ }
+
+ if (data1 & (1 << 7)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_RIGHT;
+ }
+
+ // buttons ABXY
+
+ if (data2 & (1 << 4)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_A;
+ }
+
+ if (data2 & (1 << 5)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_B;
+ }
+
+ if (data2 & (1 << 6)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_X;
+ }
+
+ if (data2 & (1 << 7)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_Y;
+ }
+
+ // buttons rear
+
+ if (data2 & (1 << 0)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_LT;
+ }
+
+ if (data2 & (1 << 1)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_RT;
+ }
+
+ if (data2 & (1 << 2)) {
+ gp_xbox_packet.buttons |= GP_XBOX_BUTTON_XBOX;
+ }
+
+ // rear levers
+
+ gp_xbox_packet.axis_rear_left = packet[4];
+ gp_xbox_packet.axis_rear_right = packet[5];
+ gp_xbox_packet.axis_left_x = packet[7]*256 + packet[6];
+ gp_xbox_packet.axis_left_y = packet[9]*256 + packet[8];
+ gp_xbox_packet.axis_right_x = packet[11]*256 + packet[10];
+ gp_xbox_packet.axis_right_y = packet[13]*256 + packet[12];
+
+ // call update callback
+ if (gp_xbox_config->update) {
+ gp_xbox_config->update(gp_xbox->device_id, gp_xbox_packet);
+ }
+}
+
+static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
+{
+ gp_xbox_device_t *gp_xbox = dev->drvdata;
+ switch (gp_xbox->state_next) {
+ case STATE_READING_COMPLETE:
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ parse_data(dev);
+ gp_xbox->state_next = STATE_READING_REQUEST;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ if (cb_data.transferred_length == GP_XBOX_CORRECT_TRANSFERRED_LENGTH) {
+ parse_data(dev);
+ }
+ gp_xbox->state_next = STATE_READING_REQUEST;
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ ERROR(cb_data.status);
+ gp_xbox->state_next = STATE_INACTIVE;
+ break;
+ }
+ }
+ break;
+
+ case STATE_SET_CONFIGURATION_EMPTY_READ:
+ {
+ LOG_PRINTF("|empty packet read|");
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ gp_xbox->state_next = STATE_SET_CONFIGURATION_COMPLETE;
+ device_xfer_control_read(0, 0, event, dev);
+ break;
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ gp_xbox->state_next = STATE_INACTIVE;
+ break;
+ }
+ }
+ break;
+ case STATE_SET_CONFIGURATION_COMPLETE: // Configured
+ {
+ switch (cb_data.status) {
+ case USBH_PACKET_CALLBACK_STATUS_OK:
+ gp_xbox->state_next = STATE_READING_REQUEST;
+ gp_xbox->endpoint_in_toggle = 0;
+ LOG_PRINTF("\r\ngp_xbox CONFIGURED\r\n");
+ if (gp_xbox_config->notify_connected) {
+ gp_xbox_config->notify_connected(gp_xbox->device_id);
+ }
+ break;
+
+ case USBH_PACKET_CALLBACK_STATUS_EFATAL:
+ case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
+ case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
+ ERROR(cb_data.status);
+ gp_xbox->state_next = STATE_INACTIVE;
+ break;
+ }
+ }
+ break;
+
+ case STATE_INACTIVE:
+ {
+ LOG_PRINTF("XBOX inactive");
+ }
+ break;
+ default:
+ {
+ LOG_PRINTF("Unknown state\r\n");
+ }
+ break;
+ }
+}
+
+
+static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox)
+{
+ usbh_packet_t packet;
+
+ packet.address = gp_xbox->usbh_device->address;
+ packet.data = &gp_xbox->buffer[0];
+ packet.datalen = gp_xbox->endpoint_in_maxpacketsize;
+ packet.endpoint_address = gp_xbox->endpoint_in_address;
+ packet.endpoint_size_max = gp_xbox->endpoint_in_maxpacketsize;
+ packet.endpoint_type = USBH_EPTYP_BULK;
+ packet.speed = gp_xbox->usbh_device->speed;
+ packet.callback = event;
+ packet.callback_arg = gp_xbox->usbh_device;
+ packet.toggle = &gp_xbox->endpoint_in_toggle;
+
+ gp_xbox->state_next = STATE_READING_COMPLETE;
+ usbh_read(gp_xbox->usbh_device, &packet);
+
+ // LOG_PRINTF("@gp_xbox EP1 | \r\n");
+}
+
+/**
+ *
+ * tflp time from last poll [us]
+ */
+static void gp_xbox_poll(void *drvdata, uint32_t tflp)
+{
+ (void)tflp;
+ gp_xbox_device_t *gp_xbox = drvdata;
+ usbh_device_t *dev = gp_xbox->usbh_device;
+
+ switch (gp_xbox->state_next) {
+ case STATE_READING_REQUEST:
+ {
+ read_gp_xbox_in(gp_xbox);
+ }
+ break;
+
+ case STATE_SET_CONFIGURATION_REQUEST:
+ {
+ struct usb_setup_data setup_data;
+
+ setup_data.bmRequestType = 0b00000000;
+ setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
+ setup_data.wValue = gp_xbox->configuration_value;
+ setup_data.wIndex = 0;
+ setup_data.wLength = 0;
+
+ gp_xbox->state_next = STATE_SET_CONFIGURATION_EMPTY_READ;
+
+ device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev);
+ }
+ break;
+
+ default:
+ {
+ // do nothing - probably transfer is in progress
+ }
+ break;
+ }
+}
+
+static void gp_xbox_remove(void *drvdata)
+{
+ LOG_PRINTF("Removing xbox\r\n");
+
+ gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
+ if (gp_xbox_config->notify_disconnected) {
+ gp_xbox_config->notify_disconnected(gp_xbox->device_id);
+ }
+ gp_xbox->state_next = STATE_INACTIVE;
+ gp_xbox->endpoint_in_address = 0;
+}