make hid_mouse driver generic HID driver

+ keyboard support
+ SET_REPORT commands - usually leds on keyboards (WIP)

- missing parsing of HID report descriptor

Signed-off-by: Amir Hammad <amir.hammad@hotmail.com>
This commit is contained in:
Amir Hammad 2016-09-09 18:37:22 +02:00
parent ed70a1efa3
commit 3d68ea2807
8 changed files with 648 additions and 365 deletions

View file

@ -20,7 +20,7 @@ This means no allocation and reallocation is affecting performance
- HUB
- Gamepad - XBox compatible Controller
- mouse (draft: only displays raw data)
- Generic Human Interface driver: mouse, keyboard (raw data)
- USB MIDI devices (raw data + note on/off)
## Steps to compile library and demo

View file

@ -38,10 +38,10 @@
// Set this wisely
#define BUFFER_ONE_BYTES (2048)
// MOUSE
#define USBH_HID_MOUSE_MAX_DEVICES (2)
#define USBH_HID_MOUSE_BUFFER (32)
// HID class devices
#define USBH_HID_MAX_DEVICES (2)
#define USBH_HID_BUFFER (256)
#define USBH_HID_REPORT_BUFFER (4)
// MIDI
// Maximal number of midi devices connected to whatever hub

85
include/usbh_driver_hid.h Normal file
View file

@ -0,0 +1,85 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 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/>.
*
*/
#ifndef USBH_DRIVER_HID_
#define USBH_DRIVER_HID_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
struct _hid_mouse_config {
/**
* @brief this is called when some data is read when polling the device
* @param device_id handle of HID device
* @param data pointer to the data
* @param length count of bytes in the data
*
* TODO: make better interface that provides data contained in the report descriptor
*
*/
void (*hid_in_message_handler)(uint8_t device_id, const uint8_t *data, uint32_t length);
};
typedef struct _hid_mouse_config hid_config_t;
/**
* @brief hid_mouse_driver_init initialization routine - this will initialize internal structures of this device driver
* @param config
* @see hid_mouse_config_t
*/
void hid_driver_init(const hid_config_t *config);
/**
* @brief hid_set_report
* @param device_id handle of HID device
* @returns true on success, false otherwise
*/
bool hid_set_report(uint8_t device_id, uint8_t val);
enum HID_TYPE {
HID_TYPE_NONE,
HID_TYPE_MOUSE,
HID_TYPE_KEYBOARD,
};
/**
* @brief hid_get_type
* @param device_id handle of HID device
* @return type of attached HID
* @see enum HID_TYPE
*/
enum HID_TYPE hid_get_type(uint8_t device_id);
/**
* @brief hid_is_connected
* @param device_id handle of HID device
* @return true if the device with device_id is connected
*/
bool hid_is_connected(uint8_t device_id);
extern const usbh_dev_driver_t usbh_hid_driver;
END_DECLS
#endif

View file

@ -1,53 +0,0 @@
/*
* 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/>.
*
*/
#ifndef USBH_DRIVER_HID_MOUSE_
#define USBH_DRIVER_HID_MOUSE_
#include "usbh_core.h"
#include <stdint.h>
BEGIN_DECLS
struct _hid_mouse_config {
/**
* @brief this is called when some data is read when polling the device
* @param device_id
* @param data pointer to the data (only 4 bytes are valid!)
*/
void (*mouse_in_message_handler)(uint8_t device_id, const uint8_t *data);
};
typedef struct _hid_mouse_config hid_mouse_config_t;
/**
* @brief hid_mouse_driver_init initialization routine - this will initialize internal structures of this device driver
* @param config
* @see hid_mouse_config_t
*/
void hid_mouse_driver_init(const hid_mouse_config_t *config);
extern const usbh_dev_driver_t usbh_hid_mouse_driver;
END_DECLS
#endif

View file

@ -14,7 +14,7 @@ add_library (usbhost
${inc}/usbh_core.h
${inc}/usbh_driver_ac_midi.h
${inc}/usbh_driver_gp_xbox.h
${inc}/usbh_driver_hid_mouse.h
${inc}/usbh_driver_hid.h
${inc}/usbh_driver_hub.h
${inc}/usbh_lld_stm32f4.h
${inc}/driver/usbh_device_driver.h
@ -23,7 +23,7 @@ add_library (usbhost
usbh_driver_ac_midi.c
usbh_driver_ac_midi_private.h
usbh_driver_gp_xbox.c
usbh_driver_hid_mouse.c
usbh_driver_hid.c
usbh_driver_hub.c
usbh_driver_hub_private.h
usbh_lld_stm32f4.c

View file

@ -23,7 +23,7 @@
#include "usart_helpers.h" /// provides LOG_PRINTF macros used for debugging
#include "usbh_core.h" /// provides usbh_init() and usbh_poll()
#include "usbh_lld_stm32f4.h" /// provides low level usb host driver for stm32f4 platform
#include "usbh_driver_hid_mouse.h" /// provides usb device driver Human Interface Device - type mouse
#include "usbh_driver_hid.h" /// provides generic usb device driver for Human Interface Device (HID)
#include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported)
#include "usbh_driver_gp_xbox.h" /// provides usb device driver for Gamepad: Microsoft XBOX compatible Controller
#include "usbh_driver_ac_midi.h" /// provides usb device driver for midi class devices
@ -116,7 +116,7 @@ static void gpio_setup(void)
static const usbh_dev_driver_t *device_drivers[] = {
&usbh_hub_driver,
&usbh_hid_mouse_driver,
&usbh_hid_driver,
&usbh_gp_xbox_driver,
&usbh_midi_driver,
0
@ -148,17 +148,29 @@ static const gp_xbox_config_t gp_xbox_config = {
.notify_disconnected = &gp_xbox_disconnected
};
static void mouse_in_message_handler(uint8_t device_id, const uint8_t *data)
static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length)
{
(void)device_id;
(void)data;
if (length < 4) {
LOG_PRINTF("data too short, type=%d\n", hid_get_type(device_id));
return;
}
// print only first 4 bytes, since every mouse should have at least these four set.
// Report descriptors are not read by driver for now, so we do not know what each byte means
LOG_PRINTF("MOUSE EVENT %02X %02X %02X %02X \n", data[0], data[1], data[2], data[3]);
LOG_PRINTF("HID EVENT %02X %02X %02X %02X \n", data[0], data[1], data[2], data[3]);
if (hid_get_type(device_id) == HID_TYPE_KEYBOARD) {
static int x = 0;
if (x != data[2]) {
x = data[2];
hid_set_report(device_id, x);
}
}
}
static const hid_mouse_config_t mouse_config = {
.mouse_in_message_handler = &mouse_in_message_handler
static const hid_config_t hid_config = {
.hid_in_message_handler = &hid_in_message_handler
};
static void midi_in_message_handler(int device_id, uint8_t *data)
@ -200,7 +212,7 @@ int main(void)
*
* Pass configuration struct where the callbacks are defined
*/
hid_mouse_driver_init(&mouse_config);
hid_driver_init(&hid_config);
hub_driver_init();
gp_xbox_driver_init(&gp_xbox_config);
midi_driver_init(&midi_config);

537
src/usbh_driver_hid.c Normal file
View file

@ -0,0 +1,537 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 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 "usbh_core.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hid.h"
#include "usart_helpers.h"
#include <libopencm3/usb/usbstd.h>
#include <libopencm3/usb/hid.h>
#include <stdint.h>
#define USB_HID_SET_REPORT 0x09
#define USB_HID_SET_IDLE 0x0A
enum STATES {
STATE_INACTIVE,
STATE_READING_REQUEST,
STATE_READING_COMPLETE_AND_CHECK_REPORT,
STATE_SET_REPORT_EMPTY_READ,
STATE_SET_CONFIGURATION_REQUEST,
STATE_SET_CONFIGURATION_EMPTY_READ,
STATE_GET_REPORT_DESCRIPTOR_READ_SETUP,// configuration is complete at this point. We write request
STATE_GET_REPORT_DESCRIPTOR_READ,
STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE,// after the read finishes, we parse that descriptor
STATE_SET_IDLE,
STATE_SET_IDLE_COMPLETE,
};
enum REPORT_STATE {
REPORT_STATE_NULL,
REPORT_STATE_READY,
REPORT_STATE_WRITE_PENDING,
REPORT_STATE_WRITE_PENDING_DATA,
REPORT_STATE_EMPTY_READ,
};
struct _hid_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_HID_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;
uint16_t report0_length;
enum REPORT_STATE report_state;
uint8_t report_data[USBH_HID_REPORT_BUFFER];
uint8_t report_data_length;
enum HID_TYPE hid_type;
uint8_t interface_number;
};
typedef struct _hid_device hid_device_t;
struct hid_report_decriptor {
struct usb_hid_descriptor header;
struct _report_descriptor_info {
uint8_t bDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) report_descriptors_info[];
} __attribute__((packed));
static hid_device_t hid_device[USBH_HID_MAX_DEVICES];
static hid_config_t hid_config;
static bool initialized = false;
void hid_driver_init(const hid_config_t *config)
{
uint32_t i;
initialized = true;
hid_config = *config;
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
hid_device[i].state_next = STATE_INACTIVE;
}
}
static void *init(void *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\r\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hid_device_t *drvdata = 0;
// find free data space for HID device
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
if (hid_device[i].state_next == STATE_INACTIVE) {
drvdata = &hid_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->report0_length = 0;
drvdata->usbh_device = (usbh_device_t *)usbh_dev;
drvdata->report_state = REPORT_STATE_NULL;
drvdata->hid_type = HID_TYPE_NONE;
break;
}
}
return drvdata;
}
static void parse_report_descriptor(hid_device_t *hid, const uint8_t *buffer, uint32_t length)
{
// TODO
// Do some parsing!
// add some checks
hid->report_state = REPORT_STATE_READY;
// TODO: parse this from buffer!
hid->report_data_length = 1;
(void)buffer;
(void)length;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hid_device_t *hid = (hid_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
const struct usb_config_descriptor * cfg = (const struct usb_config_descriptor*)descriptor;
hid->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
{
const struct usb_device_descriptor *devDesc = (const struct usb_device_descriptor *)descriptor;
(void)devDesc;
}
break;
case USB_DT_INTERFACE:
{
const struct usb_interface_descriptor *ifDesc = (const struct usb_interface_descriptor *)descriptor;
if (ifDesc->bInterfaceProtocol == 0x01) {
hid->hid_type = HID_TYPE_KEYBOARD;
hid->interface_number = ifDesc->bInterfaceNumber;
} else if (ifDesc->bInterfaceProtocol == 0x02) {
hid->hid_type = HID_TYPE_MOUSE;
hid->interface_number = ifDesc->bInterfaceNumber;
}
}
break;
case USB_DT_ENDPOINT:
{
const struct usb_endpoint_descriptor *ep = (const struct usb_endpoint_descriptor *)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
hid->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_HID_BUFFER) {
hid->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
hid->endpoint_in_maxpacketsize = USBH_HID_BUFFER;
}
}
}
}
break;
case USB_DT_HID:
{
const struct hid_report_decriptor *desc = (const struct hid_report_decriptor *)descriptor;
if (desc->header.bNumDescriptors > 0 && desc->report_descriptors_info[0].bDescriptorType == USB_DT_REPORT) {
hid->report0_length = desc->report_descriptors_info[0].wDescriptorLength;
}
}
break;
default:
break;
}
if (hid->endpoint_in_address && hid->report0_length) {
hid->state_next = STATE_SET_CONFIGURATION_REQUEST;
return true;
}
return false;
}
static void report_event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_device_t *hid = (hid_device_t *)dev->drvdata;
switch (hid->report_state) {
case REPORT_STATE_WRITE_PENDING:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
hid->report_state = REPORT_STATE_WRITE_PENDING_DATA;
device_xfer_control_write_data(hid->report_data, hid->report_data_length, report_event, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
break;
case REPORT_STATE_WRITE_PENDING_DATA:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
hid->report_state = REPORT_STATE_EMPTY_READ;
device_xfer_control_read(0, 0, report_event, dev);
LOG_PRINTF("reading empty\n");
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
break;
case REPORT_STATE_EMPTY_READ:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
hid->report_state = REPORT_STATE_READY;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
break;
default:
break;
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_device_t *hid = (hid_device_t *)dev->drvdata;
switch (hid->state_next) {
case STATE_READING_COMPLETE_AND_CHECK_REPORT:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (hid_config.hid_in_message_handler) {
hid_config.hid_in_message_handler(hid->device_id, hid->buffer, cb_data.transferred_length);
}
hid->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->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:
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_SETUP;
device_xfer_control_read(0, 0, event, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_SETUP:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
{
hid->endpoint_in_toggle = 0;
// We support only the first report descriptor with index 0
// limit the size of the report descriptor!
if (hid->report0_length > USBH_HID_BUFFER) {
hid->report0_length = USBH_HID_BUFFER;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_REPORT << 8;
setup_data.wIndex = 0;
setup_data.wLength = hid->report0_length;
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev);
}
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE;
device_xfer_control_read(hid->buffer, hid->report0_length, event, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE: // read complete, SET_IDLE to 0
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
LOG_PRINTF("READ REPORT COMPLETE \n");
hid->state_next = STATE_READING_REQUEST;
hid->endpoint_in_toggle = 0;
parse_report_descriptor(hid, hid->buffer, cb_data.transferred_length);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
default:
break;
}
}
static void read_hid_in_endpoint(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_packet_t packet;
packet.address = hid->usbh_device->address;
packet.data.in = &hid->buffer[0];
packet.datalen = hid->endpoint_in_maxpacketsize;
packet.endpoint_address = hid->endpoint_in_address;
packet.endpoint_size_max = hid->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = hid->usbh_device->speed;
packet.callback = event;
packet.callback_arg = hid->usbh_device;
packet.toggle = &hid->endpoint_in_toggle;
hid->state_next = STATE_READING_COMPLETE_AND_CHECK_REPORT;
usbh_read(hid->usbh_device, &packet);
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_device_t *dev = hid->usbh_device;
switch (hid->state_next) {
case STATE_READING_REQUEST:
{
read_hid_in_endpoint(drvdata);
}
break;
case STATE_SET_CONFIGURATION_REQUEST:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
setup_data.wValue = hid->configuration_value;
setup_data.wIndex = 0;
setup_data.wLength = 0;
hid->state_next = STATE_SET_CONFIGURATION_EMPTY_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev);
}
break;
default:
// do nothing - probably transfer is in progress
break;
}
}
static void remove(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
hid->state_next = STATE_INACTIVE;
hid->endpoint_in_address = 0;
}
bool hid_set_report(uint8_t device_id, uint8_t val)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("invalid device id");
return false;
}
hid_device_t *hid = &hid_device[device_id];
if (hid->report_state != REPORT_STATE_READY) {
LOG_PRINTF("reporting is not ready\n");
// store and update afterwards
return false;
}
if (hid->report_data_length == 0) {
LOG_PRINTF("reporting is not available (report len=0)\n");
return false;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_HID_SET_REPORT;
setup_data.wValue = 0x02 << 8;
setup_data.wIndex = hid->interface_number;
setup_data.wLength = hid->report_data_length;
hid->report_data[0] = val;
hid->report_state = REPORT_STATE_WRITE_PENDING;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data), report_event, hid->usbh_device);
return true;
}
bool hid_is_connected(uint8_t device_id)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("is connected: invalid device id");
return false;
}
return hid_device[device_id].state_next == STATE_INACTIVE;
}
enum HID_TYPE hid_get_type(uint8_t device_id)
{
if (hid_is_connected(device_id)) {
return HID_TYPE_NONE;
}
return hid_device[device_id].hid_type;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x03, // HID class
.ifaceSubClass = -1,
.ifaceProtocol = -1, // Do not care
};
const usbh_dev_driver_t usbh_hid_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

View file

@ -1,298 +0,0 @@
/*
* 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 "usbh_core.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hid_mouse.h"
#include "usart_helpers.h"
#include <libopencm3/usb/usbstd.h>
enum STATES {
STATE_INACTIVE,
STATE_READING_COMPLETE,
STATE_READING_REQUEST,
STATE_SET_CONFIGURATION_REQUEST,
STATE_SET_CONFIGURATION_EMPTY_READ,
STATE_SET_CONFIGURATION_COMPLETE
};
struct _hid_mouse_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_HID_MOUSE_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 _hid_mouse_device hid_mouse_device_t;
static hid_mouse_device_t mouse_device[USBH_HID_MOUSE_MAX_DEVICES];
static const hid_mouse_config_t *mouse_config;
#include <stdint.h>
static bool initialized = false;
void hid_mouse_driver_init(const hid_mouse_config_t *config)
{
uint32_t i;
initialized = true;
mouse_config = config;
for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) {
mouse_device[i].state_next = STATE_INACTIVE;
}
}
/**
*
*
*/
static void *init(void *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hid_mouse_device_t *drvdata = 0;
// find free data space for mouse device
for (i = 0; i < USBH_HID_MOUSE_MAX_DEVICES; i++) {
if (mouse_device[i].state_next == STATE_INACTIVE) {
drvdata = &mouse_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->usbh_device = (usbh_device_t *)usbh_dev;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hid_mouse_device_t *mouse = (hid_mouse_device_t *)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;
mouse->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)) {
mouse->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_HID_MOUSE_BUFFER) {
mouse->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
mouse->endpoint_in_maxpacketsize = USBH_HID_MOUSE_BUFFER;
}
}
if (mouse->endpoint_in_address) {
mouse->state_next = STATE_SET_CONFIGURATION_REQUEST;
return true;
}
}
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_mouse_device_t *mouse = (hid_mouse_device_t *)dev->drvdata;
switch (mouse->state_next) {
case STATE_READING_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
mouse->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->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:
mouse->state_next = STATE_SET_CONFIGURATION_COMPLETE;
device_xfer_control_read(0, 0, event, dev);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_SET_CONFIGURATION_COMPLETE: // Configured
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
mouse->state_next = STATE_READING_REQUEST;
mouse->endpoint_in_toggle = 0;
LOG_PRINTF("\nMOUSE CONFIGURED\n");
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
case USBH_PACKET_CALLBACK_STATUS_EFATAL:
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
ERROR(cb_data.status);
mouse->state_next = STATE_INACTIVE;
break;
}
}
break;
default:
break;
}
}
static void read_mouse_in(void *drvdata)
{
hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata;
usbh_packet_t packet;
packet.address = mouse->usbh_device->address;
packet.data.in = &mouse->buffer[0];
packet.datalen = mouse->endpoint_in_maxpacketsize;
packet.endpoint_address = mouse->endpoint_in_address;
packet.endpoint_size_max = mouse->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = mouse->usbh_device->speed;
packet.callback = event;
packet.callback_arg = mouse->usbh_device;
packet.toggle = &mouse->endpoint_in_toggle;
mouse->state_next = STATE_READING_COMPLETE;
usbh_read(mouse->usbh_device, &packet);
// LOG_PRINTF("@MOUSE EP1 | \n");
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata;
usbh_device_t *dev = mouse->usbh_device;
switch (mouse->state_next) {
case STATE_READING_REQUEST:
{
read_mouse_in(drvdata);
}
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 = mouse->configuration_value;
setup_data.wIndex = 0;
setup_data.wLength = 0;
mouse->state_next = STATE_SET_CONFIGURATION_EMPTY_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data), event, dev);
}
break;
default:
// do nothing - probably transfer is in progress
break;
}
}
static void remove(void *drvdata)
{
hid_mouse_device_t *mouse = (hid_mouse_device_t *)drvdata;
mouse->state_next = STATE_INACTIVE;
mouse->endpoint_in_address = 0;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x03,
.ifaceSubClass = -1,
.ifaceProtocol = 0x02
};
const usbh_dev_driver_t usbh_hid_mouse_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};