Files
OpenCellular/chip/g/usb_hid.c
Bill Richardson 30f8fdaa91 Cr50: Cleaner API for USB_DECLARE_IFACE callbacks
The control endpoint (EP0) can receive some Setup packets that
are specific to individual Interfaces. The USB_DECLARE_IFACE
macro is used to register the callbacks that an interface
implementation provides to handle those Setup packets.

This change cleans up the callback API a bit, so that we don't
have to export the internal workings of the Cr50's EP0 interrupt
handler.

BUG=chrome-os-partner:34893
BRANCH=none
TEST=make buildall, manual

Connect the Cr50 to my workstation via USB:
* /bin/dmesg reports no errors
* verify EP0 with lsusb -v -d 18d1:5014
* verify EP1 with './extra/usb_console -e 1 -p 5014' (reverses
  case of input text)
* verify EP2 with the 'hid' command on the EC console (types a 'g')

Change-Id: I9ac22f6a74f360f201c58e9ef39e3576834578a8
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/317269
Reviewed-by: Dominic Rizzo <domrizzo@google.com>
2015-12-11 11:22:02 -08:00

160 lines
4.3 KiB
C

/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "clock.h"
#include "common.h"
#include "config.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "link_defs.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_descriptor.h"
#include "usb_hid.h"
/* Console output macro */
#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args)
#define HID_REPORT_SIZE 8
/* HID descriptors */
const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_HID) =
{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = USB_IFACE_HID,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = USB_HID_SUBCLASS_BOOT,
.bInterfaceProtocol = USB_HID_PROTOCOL_KEYBOARD,
.iInterface = USB_STR_HID_NAME,
};
const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_HID, 81) =
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x80 | USB_EP_HID,
.bmAttributes = 0x03 /* Interrupt endpoint */,
.wMaxPacketSize = HID_REPORT_SIZE,
.bInterval = 32 /* ms polling interval */
};
const struct usb_hid_descriptor USB_CUSTOM_DESC(USB_IFACE_HID, hid) =
{
.bLength = 9,
.bDescriptorType = USB_HID_DT_HID,
.bcdHID = 0x0100,
.bCountryCode = 0x00, /* Hardware target country */
.bNumDescriptors = 1,
.desc = {
{.bDescriptorType = USB_HID_DT_REPORT,
.wDescriptorLength = 45}
}
};
/* HID : Report Descriptor */
static const uint8_t report_desc[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x05, 0x07, /* Usage Page (Key Codes) */
0x19, 0xE0, /* Usage Minimum (224) */
0x29, 0xE7, /* Usage Maximum (231) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data, Variable, Absolute), ;Modifier byte */
0x95, 0x01, /* Report Count (1) */
0x75, 0x08, /* Report Size (8) */
0x81, 0x01, /* Input (Constant), ;Reserved byte */
0x95, 0x06, /* Report Count (6) */
0x75, 0x08, /* Report Size (8) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x65, /* Logical Maximum(101) */
0x05, 0x07, /* Usage Page (Key Codes) */
0x19, 0x00, /* Usage Minimum (0) */
0x29, 0x65, /* Usage Maximum (101) */
0x81, 0x00, /* Input (Data, Array), ;Key arrays (6 bytes) */
0xC0, /* End Collection */
0x00 /* Padding */
};
static uint8_t hid_ep_buf[HID_REPORT_SIZE];
static struct g_usb_desc hid_ep_desc;
void set_keyboard_report(uint64_t rpt)
{
memcpy(hid_ep_buf, &rpt, sizeof(rpt));
hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_RDY | DIEPDMA_IOC |
DIEPDMA_TXBYTES(HID_REPORT_SIZE);
/* enable TX */
GR_USB_DIEPCTL(USB_EP_HID) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
}
static void hid_tx(void)
{
/* clear IT */
GR_USB_DIEPINT(USB_EP_HID) = 0xffffffff;
return;
}
static void hid_reset(void)
{
hid_ep_desc.flags = DIEPDMA_LAST | DIEPDMA_BS_HOST_BSY | DIEPDMA_IOC;
hid_ep_desc.addr = hid_ep_buf;
GR_USB_DIEPDMA(USB_EP_HID) = (uint32_t)&hid_ep_desc;
GR_USB_DIEPCTL(USB_EP_HID) = DXEPCTL_MPS(HID_REPORT_SIZE) |
DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_INT |
DXEPCTL_TXFNUM(USB_EP_HID);
GR_USB_DAINTMSK |= DAINT_INEP(USB_EP_HID);
}
USB_DECLARE_EP(USB_EP_HID, hid_tx, hid_tx, hid_reset);
static int hid_iface_request(struct usb_setup_packet *req)
{
if ((req->bmRequestType & USB_DIR_IN) &&
req->bRequest == USB_REQ_GET_DESCRIPTOR &&
req->wValue == (USB_HID_DT_REPORT << 8)) {
/* Setup : HID specific : Get Report descriptor */
return load_in_fifo(report_desc,
MIN(req->wLength,
sizeof(report_desc)));
}
/* Anything else we'll stall */
return -1;
}
USB_DECLARE_IFACE(USB_IFACE_HID, hid_iface_request);
static int command_hid(int argc, char **argv)
{
uint8_t keycode = 0x0a; /* 'G' key */
if (argc >= 2) {
char *e;
keycode = strtoi(argv[1], &e, 16);
if (*e)
return EC_ERROR_PARAM1;
}
/* press then release the key */
set_keyboard_report((uint32_t)keycode << 16);
udelay(50 * MSEC);
set_keyboard_report(0x000000);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(hid, command_hid,
"[<HID keycode>]",
"test USB HID driver",
NULL);