root/src/kits/device/USBEndpoint.cpp
/*
 * Copyright 2007-2008, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Michael Lotz <mmlr@mlotz.ch>
 */

#include <USBKit.h>
#include <usb_raw.h>
#include <unistd.h>
#include <string.h>


BUSBEndpoint::BUSBEndpoint(BUSBInterface *interface, uint32 index, int rawFD)
        :       fInterface(interface),
                fIndex(index),
                fRawFD(rawFD)
{
        usb_raw_command command;
        command.endpoint_etc.descriptor = &fDescriptor;
        command.endpoint_etc.config_index = fInterface->Configuration()->Index();
        command.endpoint_etc.interface_index = fInterface->Index();
        command.endpoint_etc.alternate_index = fInterface->AlternateIndex();
        command.endpoint_etc.endpoint_index = fIndex;
        if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command,
                sizeof(command)) || command.endpoint_etc.status != B_USB_RAW_STATUS_SUCCESS)
                memset(&fDescriptor, 0, sizeof(fDescriptor));
}


BUSBEndpoint::~BUSBEndpoint()
{
}


uint32
BUSBEndpoint::Index() const
{
        return fIndex;
}


const BUSBInterface *
BUSBEndpoint::Interface() const
{
        return fInterface;
}


const BUSBConfiguration *
BUSBEndpoint::Configuration() const
{
        return fInterface->Configuration();
}


const BUSBDevice *
BUSBEndpoint::Device() const
{
        return fInterface->Device();
}


bool
BUSBEndpoint::IsBulk() const
{
        return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK)
                == USB_ENDPOINT_ATTR_BULK;
}


bool
BUSBEndpoint::IsInterrupt() const
{
        return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK)
                == USB_ENDPOINT_ATTR_INTERRUPT;
}


bool
BUSBEndpoint::IsIsochronous() const
{
        return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK)
                == USB_ENDPOINT_ATTR_ISOCHRONOUS;
}


bool
BUSBEndpoint::IsControl() const
{
        return (fDescriptor.attributes & USB_ENDPOINT_ATTR_MASK)
                == USB_ENDPOINT_ATTR_CONTROL;
}


bool
BUSBEndpoint::IsInput() const
{
        return (fDescriptor.endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
                == USB_ENDPOINT_ADDR_DIR_IN;
}


bool
BUSBEndpoint::IsOutput() const
{
        return (fDescriptor.endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
                == USB_ENDPOINT_ADDR_DIR_OUT;
}


uint16
BUSBEndpoint::MaxPacketSize() const
{
        return fDescriptor.max_packet_size;
}


uint8
BUSBEndpoint::Interval() const
{
        return fDescriptor.interval;
}


const usb_endpoint_descriptor *
BUSBEndpoint::Descriptor() const
{
        return &fDescriptor;
}


ssize_t
BUSBEndpoint::ControlTransfer(uint8 requestType, uint8 request, uint16 value,
        uint16 index, uint16 length, void *data) const
{
        if (length > 0 && data == NULL)
                return B_BAD_VALUE;

        usb_raw_command command;
        command.control.request_type = requestType;
        command.control.request = request;
        command.control.value = value;
        command.control.index = index;
        command.control.length = length;
        command.control.data = data;

        if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command,
                sizeof(command)) || command.control.status != B_USB_RAW_STATUS_SUCCESS)
                return B_ERROR;

        return command.control.length;
}


ssize_t
BUSBEndpoint::InterruptTransfer(void *data, size_t length) const
{
        if (length > 0 && data == NULL)
                return B_BAD_VALUE;

        usb_raw_command command;
        command.transfer.interface = fInterface->Index();
        command.transfer.endpoint = fIndex;
        command.transfer.data = data;
        command.transfer.length = length;

        if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command,
                sizeof(command)) || command.transfer.status != B_USB_RAW_STATUS_SUCCESS)
                return B_ERROR;

        return command.transfer.length;
}


ssize_t
BUSBEndpoint::BulkTransfer(void *data, size_t length) const
{
        if (length > 0 && data == NULL)
                return B_BAD_VALUE;

        usb_raw_command command;
        command.transfer.interface = fInterface->Index();
        command.transfer.endpoint = fIndex;
        command.transfer.data = data;
        command.transfer.length = length;

        if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command,
                sizeof(command)) || command.transfer.status != B_USB_RAW_STATUS_SUCCESS)
                return B_ERROR;

        return command.transfer.length;
}


ssize_t
BUSBEndpoint::IsochronousTransfer(void *data, size_t length,
        usb_iso_packet_descriptor *packetDescriptors, uint32 packetCount) const
{
        if (length > 0 && data == NULL)
                return B_BAD_VALUE;

        usb_raw_command command;
        command.isochronous.interface = fInterface->Index();
        command.isochronous.endpoint = fIndex;
        command.isochronous.data = data;
        command.isochronous.length = length;
        command.isochronous.packet_descriptors = packetDescriptors;
        command.isochronous.packet_count = packetCount;

        if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command,
                sizeof(command)) || command.isochronous.status != B_USB_RAW_STATUS_SUCCESS)
                return B_ERROR;

        return command.isochronous.length;
}


bool
BUSBEndpoint::IsStalled() const
{
        uint16 status = 0;
        Device()->ControlTransfer(USB_REQTYPE_ENDPOINT_IN,
                USB_REQUEST_GET_STATUS, USB_FEATURE_ENDPOINT_HALT,
                fDescriptor.endpoint_address, sizeof(status), &status);
        return status != 0;
}


status_t
BUSBEndpoint::ClearStall() const
{
        return Device()->ControlTransfer(USB_REQTYPE_ENDPOINT_OUT,
                USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT,
                fDescriptor.endpoint_address, 0, NULL);
}