root/src/add-ons/kernel/drivers/input/hid_shared/ProtocolHandler.cpp
/*
 * Copyright 2009-2011, Michael Lotz, mmlr@mlotz.ch.
 * Distributed under the terms of the MIT License.
 */


#include <stdlib.h>
#include <ring_buffer.h>
#include <kernel.h>

#include "Driver.h"
#include "HIDCollection.h"
#include "HIDDevice.h"
#include "HIDReport.h"
#include "ProtocolHandler.h"

// includes for the different protocol handlers
#include "JoystickProtocolHandler.h"
#include "KeyboardProtocolHandler.h"
#include "MouseProtocolHandler.h"
#include "TabletProtocolHandler.h"


ProtocolHandler::ProtocolHandler(HIDDevice *device, const char *basePath,
        size_t ringBufferSize)
        :       fStatus(B_NO_INIT),
                fDevice(device),
                fBasePath(basePath),
                fPublishPath(NULL),
                fRingBuffer(NULL),
                fNextHandler(NULL)
{
        if (ringBufferSize > 0) {
                fRingBuffer = create_ring_buffer(ringBufferSize);
                if (fRingBuffer == NULL) {
                        TRACE_ALWAYS("failed to create requested ring buffer\n");
                        fStatus = B_NO_MEMORY;
                        return;
                }
        }

        fStatus = B_OK;
}


ProtocolHandler::~ProtocolHandler()
{
        if (fRingBuffer) {
                delete_ring_buffer(fRingBuffer);
                fRingBuffer = NULL;
        }

        free(fPublishPath);
}


void
ProtocolHandler::SetPublishPath(char *publishPath)
{
        free(fPublishPath);
        fPublishPath = publishPath;
}


void
ProtocolHandler::AddHandlers(HIDDevice &device, ProtocolHandler *&handlerList,
        uint32 &handlerCount)
{
        TRACE("adding protocol handlers\n");

        HIDParser &parser = device.Parser();
        HIDCollection *rootCollection = parser.RootCollection();
        if (rootCollection == NULL)
                return;

        uint32 appCollectionCount = rootCollection->CountChildrenFlat(
                COLLECTION_APPLICATION);
        TRACE("root collection holds %" B_PRIu32 " application collection%s\n",
                appCollectionCount, appCollectionCount != 1 ? "s" : "");

        for (uint32  i = 0; i < appCollectionCount; i++) {
                HIDCollection *collection = rootCollection->ChildAtFlat(
                        COLLECTION_APPLICATION, i);
                if (collection == NULL)
                        continue;

                TRACE("collection usage page %u usage id %u\n",
                        collection->UsagePage(), collection->UsageID());

                // NOTE: The driver publishes devices for all added handlers.

                // TODO: How does this work if a device is not a compound device
                // like a keyboard with built-in touchpad, but allows multiple
                // alternative configurations like a tablet that works as either
                // regular (relative) mouse, or (absolute) tablet?
                KeyboardProtocolHandler::AddHandlers(device, *collection, handlerList);
                JoystickProtocolHandler::AddHandlers(device, *collection, handlerList);
                MouseProtocolHandler::AddHandlers(device, *collection, handlerList);
                TabletProtocolHandler::AddHandlers(device, *collection, handlerList);
        }

        handlerCount = 0;
        ProtocolHandler *handler = handlerList;
        while (handler != NULL) {
                handler = handler->NextHandler();
                handlerCount++;
        }

        if (handlerCount == 0) {
                TRACE_ALWAYS("no handlers for hid device\n");
                return;
        }

        TRACE("added %" B_PRId32 " handlers for hid device\n", handlerCount);
}


status_t
ProtocolHandler::Open(uint32 flags, uint32 *cookie)
{
        return fDevice->Open(this, flags);
}


status_t
ProtocolHandler::Close(uint32 *cookie)
{
        *cookie |= PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED;
                // This lets the handlers know that this user is gone.

        return fDevice->Close(this);
}


status_t
ProtocolHandler::Read(uint32 *cookie, off_t position, void *buffer,
        size_t *numBytes)
{
        TRACE_ALWAYS("unhandled read on protocol handler\n");
        *numBytes = 0;
        return B_ERROR;
}


status_t
ProtocolHandler::Write(uint32 *cookie, off_t position, const void *buffer,
        size_t *numBytes)
{
        TRACE_ALWAYS("unhandled write on protocol handler\n");
        *numBytes = 0;
        return B_ERROR;
}


status_t
ProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, size_t length)
{
        TRACE_ALWAYS("unhandled control on protocol handler\n");
        return B_ERROR;
}


int32
ProtocolHandler::RingBufferReadable()
{
        return ring_buffer_readable(fRingBuffer);
}


status_t
ProtocolHandler::RingBufferRead(void *buffer, size_t length)
{
        ring_buffer_user_read(fRingBuffer, (uint8 *)buffer, length);
        return B_OK;
}


status_t
ProtocolHandler::RingBufferWrite(const void *buffer, size_t length)
{
        ring_buffer_write(fRingBuffer, (const uint8 *)buffer, length);
        return B_OK;
}


void
ProtocolHandler::SetNextHandler(ProtocolHandler *nextHandler)
{
        fNextHandler = nextHandler;
}

status_t
ProtocolHandler::IOGetDeviceName(const char *name, void *buffer, size_t length)
{

        if (!IS_USER_ADDRESS(buffer))
                return B_BAD_ADDRESS;

        if (user_strlcpy((char *)buffer, name, length) > 0)
                return B_OK;

        return B_ERROR;
}