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:
parent
ed70a1efa3
commit
3d68ea2807
8 changed files with 648 additions and 365 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
85
include/usbh_driver_hid.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
26
src/demo.c
26
src/demo.c
|
|
@ -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
537
src/usbh_driver_hid.c
Normal 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
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue