root/src/add-ons/kernel/drivers/input/hid_shared/HIDWriter.cpp
/*
 * Copyright 2011, Michael Lotz, mmlr@mlotz.ch.
 * 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;
}