This is getting more common in tablets and laptops. It replaces PS/2 for the internal keyboard and pointing devices. This is simpler and cheaper than using up USB ports, and also simpler than the old and quirky PS/2 protocol. The HID spec is the same no matter what transport is used (it is also applicable for Bluetooth). Ideally we could create a separate HID bus manager that would handle all these devices in a generic way, but that is a lot of work nad extra complications for uncertain gains. For now, just move the common files to a shared directory where both drivers can use them. As a result the files are compiled twice, which is what we want, because currently they hardcode some device paths that need to be different for each driver. Change-Id: I0327f6864dd0a4372b708f7b7ecf299aa86a6ea9 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2466 Reviewed-by: Adrien Destugues <[email protected]> Tested-by: Commit checker robot <[email protected]>
272 lines
5.0 KiB
C++
272 lines
5.0 KiB
C++
/*
|
|
* Copyright 2011, Michael Lotz, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
#ifndef USERLAND_HID
|
|
#include "Driver.h"
|
|
#else
|
|
#include "UserlandHID.h"
|
|
#endif
|
|
|
|
#include "HIDWriter.h"
|
|
|
|
#include "HIDDataTypes.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
HIDWriter::HIDWriter(size_t blockSize)
|
|
: fBlockSize(blockSize),
|
|
fBufferAllocated(0),
|
|
fBufferUsed(0),
|
|
fBuffer(NULL),
|
|
fStatus(B_OK)
|
|
{
|
|
}
|
|
|
|
|
|
HIDWriter::~HIDWriter()
|
|
{
|
|
free(fBuffer);
|
|
}
|
|
|
|
|
|
// #pragma mark - High Level
|
|
|
|
|
|
status_t
|
|
HIDWriter::DefineInputPadding(uint8 count, uint8 bitLength)
|
|
{
|
|
SetReportSize(bitLength);
|
|
SetReportCount(count);
|
|
|
|
main_item_data data = { 0 };
|
|
data.data_constant = 1;
|
|
return Input(data);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::DefineInputData(uint8 count, uint8 bitLength, main_item_data data,
|
|
uint32 logicalMinimum, uint32 logicalMaximum, uint16 usagePage,
|
|
uint16 usageMinimum, uint16 usageMaximum)
|
|
{
|
|
SetReportSize(bitLength);
|
|
SetReportCount(count);
|
|
|
|
SetLogicalMinimum(logicalMinimum);
|
|
SetLogicalMaximum(logicalMaximum);
|
|
|
|
SetUsagePage(usagePage);
|
|
LocalSetUsageMinimum(usageMinimum);
|
|
LocalSetUsageMaximum(
|
|
usageMaximum == 0xffff ? usageMinimum + count - 1 : usageMaximum);
|
|
return Input(data);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::BeginCollection(uint8 collectionType, uint16 usagePage,
|
|
uint16 usageID)
|
|
{
|
|
SetUsagePage(usagePage);
|
|
LocalSetUsageID(usageID);
|
|
return BeginCollection(collectionType);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::EndCollection()
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_MAIN, ITEM_TAG_MAIN_END_COLLECTION, 0);
|
|
}
|
|
|
|
|
|
// #pragma mark - Low Level
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetUsagePage(uint16 usagePage)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_USAGE_PAGE,
|
|
usagePage);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetLogicalMinimum(uint32 logicalMinimum)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_LOGICAL_MINIMUM,
|
|
logicalMinimum);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetLogicalMaximum(uint32 logicalMaximum)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM,
|
|
logicalMaximum);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetReportSize(uint8 reportSize)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_REPORT_SIZE,
|
|
reportSize);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetReportID(uint8 reportID)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_REPORT_ID,
|
|
reportID);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::SetReportCount(uint8 reportCount)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_GLOBAL, ITEM_TAG_GLOBAL_REPORT_COUNT,
|
|
reportCount);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::LocalSetUsageID(uint16 usageID)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_LOCAL, ITEM_TAG_LOCAL_USAGE, usageID);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::LocalSetUsageMinimum(uint16 usageMinimum)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_LOCAL, ITEM_TAG_LOCAL_USAGE_MINIMUM,
|
|
usageMinimum);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::LocalSetUsageMaximum(uint16 usageMaximum)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_LOCAL, ITEM_TAG_LOCAL_USAGE_MAXIMUM,
|
|
usageMaximum);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::BeginCollection(uint8 collectionType)
|
|
{
|
|
return WriteShortItem(ITEM_TYPE_MAIN, ITEM_TAG_MAIN_COLLECTION,
|
|
collectionType);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::Input(main_item_data data)
|
|
{
|
|
main_item_data_converter converter;
|
|
converter.main_data = data;
|
|
return WriteShortItem(ITEM_TYPE_MAIN, ITEM_TAG_MAIN_INPUT,
|
|
converter.flat_data);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::Output(main_item_data data)
|
|
{
|
|
main_item_data_converter converter;
|
|
converter.main_data = data;
|
|
return WriteShortItem(ITEM_TYPE_MAIN, ITEM_TAG_MAIN_OUTPUT,
|
|
converter.flat_data);
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::Feature(main_item_data data)
|
|
{
|
|
main_item_data_converter converter;
|
|
converter.main_data = data;
|
|
return WriteShortItem(ITEM_TYPE_MAIN, ITEM_TAG_MAIN_FEATURE,
|
|
converter.flat_data);
|
|
}
|
|
|
|
|
|
// #pragma mark - Generic
|
|
|
|
|
|
status_t
|
|
HIDWriter::WriteShortItem(uint8 type, uint8 tag, uint32 value)
|
|
{
|
|
short_item item;
|
|
item.prefix.size = 0;
|
|
|
|
if (value > 0) {
|
|
if (value <= 0xff)
|
|
item.prefix.size = 1;
|
|
else if (value <= 0xffff)
|
|
item.prefix.size = 2;
|
|
else
|
|
item.prefix.size = 3; // actually means 4
|
|
}
|
|
|
|
item.prefix.type = type;
|
|
item.prefix.tag = tag;
|
|
|
|
switch (item.prefix.size) {
|
|
case 0:
|
|
return Write(&item, sizeof(item_prefix));
|
|
case 1:
|
|
item.data.as_uint8[0] = value;
|
|
return Write(&item, sizeof(item_prefix) + sizeof(uint8));
|
|
case 2:
|
|
item.data.as_uint16[0] = value;
|
|
return Write(&item, sizeof(item_prefix) + sizeof(uint16));
|
|
case 3:
|
|
item.data.as_uint32 = value;
|
|
return Write(&item, sizeof(item_prefix) + sizeof(uint32));
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
HIDWriter::Write(const void *data, size_t length)
|
|
{
|
|
if (fStatus != B_OK)
|
|
return fStatus;
|
|
|
|
size_t available = fBufferAllocated - fBufferUsed;
|
|
if (length > available) {
|
|
fBufferAllocated += length > fBlockSize ? length : fBlockSize;
|
|
uint8 *newBuffer = (uint8 *)realloc(fBuffer, fBufferAllocated);
|
|
if (newBuffer == NULL) {
|
|
fBufferAllocated -= fBlockSize;
|
|
fStatus = B_NO_MEMORY;
|
|
return fStatus;
|
|
}
|
|
|
|
fBuffer = newBuffer;
|
|
}
|
|
|
|
memcpy(fBuffer + fBufferUsed, data, length);
|
|
fBufferUsed += length;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
HIDWriter::Reset()
|
|
{
|
|
free(fBuffer);
|
|
fBuffer = NULL;
|
|
fBufferUsed = 0;
|
|
fBufferAllocated = 0;
|
|
fStatus = B_OK;
|
|
}
|