root/src/add-ons/kernel/drivers/ports/usb_serial/Option.cpp
/*
 * Copyright 2011-2012 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Alexander von Gluck IV, kallisti5@unixzen.com
 */


#include "Option.h"


OptionDevice::OptionDevice(usb_device device, uint16 vendorID,
        uint16 productID, const char *description)
        :
        ACMDevice(device, vendorID, productID, description)
{
        TRACE_FUNCALLS("> OptionDevice found: %s\n", description);
}


status_t
OptionDevice::AddDevice(const usb_configuration_info *config)
{
        TRACE_FUNCALLS("> OptionDevice::AddDevice(%08x, %08x)\n", this, config);

        int portsFound = 0;
        if (config->interface_count > 0) {
                for (size_t index = 0; index < config->interface_count; index++) {
                        usb_interface_info *interface = config->interface[index].active;

                        int txEndpointID = -1;
                        int rxEndpointID = -1;
                        int irEndpointID = -1;

                        for (size_t i = 0; i < interface->endpoint_count; i++) {
                                usb_endpoint_info *endpoint = &interface->endpoint[i];

                                // Find our Interrupt endpoint
                                if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_INTERRUPT
                                        && (endpoint->descr->endpoint_address
                                                & USB_ENDPOINT_ADDR_DIR_IN) != 0) {
                                        irEndpointID = i;
                                        continue;
                                }

                                // Find our Transmit / Receive endpoints
                                if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
                                        if ((endpoint->descr->endpoint_address
                                                & USB_ENDPOINT_ADDR_DIR_IN) != 0) {
                                                rxEndpointID = i;
                                        } else {
                                                txEndpointID = i;
                                        }
                                        continue;
                                }
                        }

                        TRACE("> OptionDevice::%s: endpoint %d, tx: %d, rx: %d, ir: %d\n",
                                __func__, index, txEndpointID, rxEndpointID, irEndpointID);

                        if (txEndpointID < 0 || rxEndpointID < 0 || irEndpointID < 0)
                                continue;

                        TRACE("> OptionDevice::%s: found port at interface %d\n", __func__,
                                index);
                        portsFound++;

                        usb_endpoint_info *irEndpoint = &interface->endpoint[irEndpointID];
                        usb_endpoint_info *txEndpoint = &interface->endpoint[txEndpointID];
                        usb_endpoint_info *rxEndpoint = &interface->endpoint[rxEndpointID];
                        SetControlPipe(irEndpoint->handle);
                        SetReadPipe(rxEndpoint->handle);
                        SetWritePipe(txEndpoint->handle);
                }

                // TODO: We need to handle multiple ports
                // We use the last found serial port for now
                if (portsFound > 0) {
                        if (portsFound > 1) {
                                TRACE_ALWAYS("> OptionDevice::%s: Warning: Found more than one "
                                        "serial port on this device (%d). Only the last one is "
                                        "is used.\n", __func__, portsFound);
                        }
                        return B_OK;
                }
        }
        return ENODEV;
}


status_t
OptionDevice::ResetDevice()
{
        TRACE_FUNCALLS("> OptionDevice::ResetDevice(%08x)\n", this);
        return B_OK;
}