/*
 * PyUSB - Python module for USB Access
 *
 * Copyright 2005 - 2007 Wander Lairson Costa
 */

#if _MSC_VER >= 1400    /* Visual C++ 8.00 */
#define _CRT_SECURE_NO_DEPRECATE
#endif /* _MSC_VER */

#include "pyusb.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define DEFAULT_TIMEOUT 100

/*
 * Necessary to compile successfully in python 2.3
 * Thanks to Mark Rages for the patch
 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif

#if (PY_VERSION_HEX < 0x02050000)
typedef int Py_ssize_t;
#endif

// PYUSB_STATIC char cvsid[] = "$Id: pyusb.c,v 1.29 2009/04/06 18:03:10 wander Exp $";

/*
 * USBError
 */
PYUSB_STATIC PyObject *PyExc_USBError;

void static PyUSB_Error(int rc)
{
    char *error_message = usb_strerror();

    if (!strcmp(error_message, "No error")) {

        /* 
         * Little hack when the error description comes from return code.
         * Thanks to Nicholas Wheeler to point out the problem...
         * Also see issue #2860940
         */
        if (rc) {
            errno = 0;
            error_message = strerror(-rc);
            if (!errno) goto quit;
        }

        error_message = "Unknown error";
    }

quit:
    PyErr_SetString(PyExc_USBError, error_message);
}

#define SUPPORT_NUMBER_PROTOCOL(_Arg) \
    (PyNumber_Check(_Arg) || PyString_Check(_Arg) || PyUnicode_Check(_Arg))

#if !defined(NDEBUG)
#define DUMP_PARAMS 1
#else
#define DUMP_PARAMS 0
#endif /* NDEBUG */

#ifndef NDEBUG

/*
 * Print a buffer of data
 */
PYUSB_STATIC void printBuffer(
    const char *buffer,
    int size
    )
{
    int i;

    if (!buffer) {
        fputs("NULL\n", stderr);
        return;
    }

    for (i = 0; i < size; ++i) {
        fprintf(stderr, "%2x ", buffer[i]);
        if (i && !(i % 20)) fputc('\n', stderr);
    }

    fputc('\n', stderr);
}

#endif /* NDEBUG */

/*
 * Converts a object with number procotol to int type
 */
PYUSB_STATIC int py_NumberAsInt(
    PyObject *obj
    )
{
    PyObject *number = PyNumber_Int(obj);
    if (number) {
        int ret = PyInt_AS_LONG(number);
        Py_DECREF(number);
        return ret;
    } else {
        return 0;
    }
}

/*
 * Gets a byte from a PyObject
 * If the obj is a number, returns the number casted to a byte
 * If it is a sequence, returns the first byte representation of the sequence
 * If it is a mapping, returns the first byte representation of the first obj.values()
 */
PYUSB_STATIC u_int8_t getByte(
    PyObject *obj
    )
{
    u_int8_t byte;

    if (PyNumber_Check(obj)) {
        byte = (u_int8_t) py_NumberAsInt(obj);
    } else if (PyString_Check(obj) || PyUnicode_Check(obj)) {
        return (u_int8_t) PyString_AsString(obj)[0];
    } else if (PySequence_Check(obj)) {
        PyObject *el0;
        el0 = PySequence_GetItem(obj, 0);
        if (!el0) return 0;
        byte = getByte(el0);
        Py_DECREF(el0);
    } else if (PyMapping_Check(obj)) {
        PyObject *vals;
        vals = PyMapping_Values(obj);
        if (!vals) return 0;
        byte = getByte(vals);
        Py_DECREF(vals);
    } else {
        byte = 0;
        PyErr_BadArgument();
    }

    return byte;
}

/*
 * Gets a unsigned byte * representation of the obj
 * If the obj is a sequence, returns the elements as a c byte array representation
 * If it is a mapping, returns the c byte array representations of the obj.values()
 */
PYUSB_STATIC char *getBuffer(
    PyObject *obj,
    Py_ssize_t *size
    )
{
    char *p = NULL;

    /*
     * When the obj is a sequence type, we take the first byte from
     * the each element of the sequence
     */

    /* ok, string is a sequence too, but let us optimize it */
    if (PyString_Check(obj) || PyUnicode_Check(obj)) { 
        char *tmp;

        if (-1 != PyString_AsStringAndSize(obj, &tmp, size)) {
            p = (char *) PyMem_Malloc(*size);
            if (p) memcpy(p, tmp, *size);
        }
    } else if (PySequence_Check(obj)) {
        u_int32_t i, sz;
        PyObject *el;

        sz = PySequence_Size(obj);
        p = (char *) PyMem_Malloc(sz);

        for (i = 0; i < sz; ++i) {
            el = PySequence_GetItem(obj, i);
            p[i] = getByte(el);
            Py_DECREF(el);

            if (!p[i] && PyErr_Occurred()) {
                PyMem_Free(p);
                return NULL;
            }
        }

        *size = sz;
    } else if (PyMapping_Check(obj)) {
        PyObject *values;

        values = PyMapping_Values(obj);
        if (!values) return NULL;

        p = getBuffer(values, size);
        Py_DECREF(values);
    } else if (obj == Py_None) {
        *size = 0;
        return NULL;
    } else {
        PyErr_BadArgument();
        return NULL;
    }

    return p;
}

/*
 * Build a numeric tuple from the buffer values
 */
PYUSB_STATIC PyObject *buildTuple(
    char *buffer,
    int size
    )
{
    PyObject *ret;
    int i;

    ret = PyTuple_New(size);

    if (ret) {
        for (i = 0; i < size; ++i) {
            PyTuple_SET_ITEM(ret, i, PyInt_FromLong((u_int8_t) buffer[i]));
        }
    }

    return ret;
}

/*
 * Add a numeric constant to the dictionary
 */
PYUSB_STATIC void addConstant(
    PyObject *dict,
    const char *name,
    long value)
{
    PyObject *val;

    val = PyInt_FromLong(value); 

    if (val) { 
        PyDict_SetItemString(dict,  name, val);
        Py_DECREF(val); 
    } 
}

/*
 * Add the module constants to the module dictionary
 */
PYUSB_STATIC void installModuleConstants(
    PyObject *module
    )
{
    PyObject *dict;

    dict = PyModule_GetDict(module);

    addConstant(dict, "CLASS_PER_INTERFACE", USB_CLASS_PER_INTERFACE);
    addConstant(dict, "CLASS_AUDIO", USB_CLASS_AUDIO);
    addConstant(dict, "CLASS_COMM", USB_CLASS_COMM);
    addConstant(dict, "CLASS_HID", USB_CLASS_HID);
    addConstant(dict, "CLASS_PRINTER", USB_CLASS_PRINTER);
    addConstant(dict, "CLASS_MASS_STORAGE", USB_CLASS_MASS_STORAGE);
    addConstant(dict, "CLASS_HUB", USB_CLASS_HUB);
    addConstant(dict, "CLASS_DATA", USB_CLASS_DATA);
    addConstant(dict, "CLASS_VENDOR_SPEC", USB_CLASS_VENDOR_SPEC);
    addConstant(dict, "DT_DEVICE", USB_DT_DEVICE);
    addConstant(dict, "DT_CONFIG", USB_DT_CONFIG);
    addConstant(dict, "DT_STRING", USB_DT_STRING);
    addConstant(dict, "DT_INTERFACE", USB_DT_INTERFACE);
    addConstant(dict, "DT_ENDPOINT", USB_DT_ENDPOINT);
    addConstant(dict, "DT_HID", USB_DT_HID);
    addConstant(dict, "DT_REPORT", USB_DT_REPORT);
    addConstant(dict, "DT_PHYSICAL", USB_DT_PHYSICAL);
    addConstant(dict, "DT_HUB", USB_DT_HUB);
    addConstant(dict, "DT_DEVICE_SIZE", USB_DT_DEVICE_SIZE);
    addConstant(dict, "DT_CONFIG_SIZE", USB_DT_CONFIG_SIZE);
    addConstant(dict, "DT_INTERFACE_SIZE", USB_DT_INTERFACE_SIZE);
    addConstant(dict, "DT_ENDPOINT_SIZE", USB_DT_ENDPOINT_SIZE);
    addConstant(dict, "DT_ENDPOINT_AUDIO_SIZE", USB_DT_ENDPOINT_AUDIO_SIZE);
    addConstant(dict, "DT_HUB_NONVAR_SIZE", USB_DT_HUB_NONVAR_SIZE);
    addConstant(dict, "MAXENDPOINTS", USB_MAXENDPOINTS);
    addConstant(dict, "ENDPOINT_ADDRESS_MASK", USB_ENDPOINT_ADDRESS_MASK);
    addConstant(dict, "ENDPOINT_DIR_MASK", USB_ENDPOINT_DIR_MASK);
    addConstant(dict, "ENDPOINT_TYPE_MASK", USB_ENDPOINT_TYPE_MASK);
    addConstant(dict, "ENDPOINT_TYPE_CONTROL", USB_ENDPOINT_TYPE_CONTROL);
    addConstant(dict, "ENDPOINT_TYPE_ISOCHRONOUS", USB_ENDPOINT_TYPE_ISOCHRONOUS);
    addConstant(dict, "ENDPOINT_TYPE_BULK", USB_ENDPOINT_TYPE_BULK);
    addConstant(dict, "ENDPOINT_TYPE_INTERRUPT", USB_ENDPOINT_TYPE_INTERRUPT);
    addConstant(dict, "MAXINTERFACES", USB_MAXINTERFACES);
    addConstant(dict, "MAXALTSETTING", USB_MAXALTSETTING);
    addConstant(dict, "MAXCONFIG", USB_MAXCONFIG);
    addConstant(dict, "REQ_GET_STATUS", USB_REQ_GET_STATUS);
    addConstant(dict, "REQ_CLEAR_FEATURE", USB_REQ_CLEAR_FEATURE);
    addConstant(dict, "REQ_SET_FEATURE", USB_REQ_SET_FEATURE);
    addConstant(dict, "REQ_SET_ADDRESS", USB_REQ_SET_ADDRESS);
    addConstant(dict, "REQ_GET_DESCRIPTOR", USB_REQ_GET_DESCRIPTOR);
    addConstant(dict, "REQ_SET_DESCRIPTOR", USB_REQ_SET_DESCRIPTOR);
    addConstant(dict, "REQ_GET_CONFIGURATION", USB_REQ_GET_CONFIGURATION);
    addConstant(dict, "REQ_SET_CONFIGURATION", USB_REQ_SET_CONFIGURATION);
    addConstant(dict, "REQ_GET_INTERFACE", USB_REQ_GET_INTERFACE);
    addConstant(dict, "REQ_SET_INTERFACE", USB_REQ_SET_INTERFACE);
    addConstant(dict, "REQ_SYNCH_FRAME", USB_REQ_SYNCH_FRAME);
    addConstant(dict, "TYPE_STANDARD", USB_TYPE_STANDARD);
    addConstant(dict, "TYPE_CLASS", USB_TYPE_CLASS);
    addConstant(dict, "TYPE_VENDOR", USB_TYPE_VENDOR);
    addConstant(dict, "TYPE_RESERVED", USB_TYPE_RESERVED);
    addConstant(dict, "RECIP_DEVICE", USB_RECIP_DEVICE);
    addConstant(dict, "RECIP_INTERFACE", USB_RECIP_INTERFACE);
    addConstant(dict, "RECIP_ENDPOINT", USB_RECIP_ENDPOINT);
    addConstant(dict, "RECIP_OTHER", USB_RECIP_OTHER);
    addConstant(dict, "ENDPOINT_IN", USB_ENDPOINT_IN);
    addConstant(dict, "ENDPOINT_OUT", USB_ENDPOINT_OUT);
    addConstant(dict, "ERROR_BEGIN", USB_ERROR_BEGIN);
}

/*
 * Earlier versions of the PyUSB separate direction bit and
 * endpoint address with direction and address fields...
 * Windows version of the libusb does need direction
 * bit in endpoint address to works fine.
 * Thanks to Ray Schumacher.
 */
PYUSB_STATIC PyMemberDef Py_usb_Endpoint_Members[] = {
    {"address",
     T_UBYTE,
     offsetof(Py_usb_Endpoint, address),
     READONLY,
     "Contains the endpoint address."},

    {"type",
     T_UBYTE,
     offsetof(Py_usb_Endpoint, type),
     READONLY,
     "It contains one of the following values, \n"
     "indicating the endpoint transfer type:\n"
     "\tENDPOINT_TYPE_CONTROL\n"
     "\tENDPOINT_TYPE_ISOCHRONOUS\n"
     "\tENDPOINT_TYPE_BULK\n"
     "\tENDPOINT_TYPE_INTERRUPT\n"},

    {"maxPacketSize",
     T_USHORT,
     offsetof(Py_usb_Endpoint, maxPacketSize),
     READONLY,
     "The maximum number of data bytes the endpoint\n"
     "can transfer in a transaction."},

    {"interval",
     T_UBYTE,
     offsetof(Py_usb_Endpoint, interval),
     READONLY,
     "The maximum latency for polling interrupt endpoints, or\n"
     "the interval for polling isochronous endpoints, or the maximum NAK\n"
     "rate for high-speed bulk OUT or control endpoints."},

    {NULL}
};

PYUSB_STATIC PyMethodDef Py_usb_Endpoint_Methods[] = {
    {NULL, NULL}
};

PYUSB_STATIC PyTypeObject Py_usb_Endpoint_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.Endpoint",  /*tp_name*/
    sizeof(Py_usb_Endpoint), /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    0,                         /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Endpoint descriptor object", /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_Endpoint_Methods,   /* tp_methods */
    Py_usb_Endpoint_Members,   /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,                         /* destructor */
};

PYUSB_STATIC void set_Endpoint_fields(
    Py_usb_Endpoint *endpoint,
    struct usb_endpoint_descriptor *ep
    )
{
    endpoint->address = ep->bEndpointAddress;
    endpoint->type = ep->bmAttributes & 3;
    endpoint->maxPacketSize = ep->wMaxPacketSize;
    endpoint->interval = ep->bInterval;
    endpoint->refresh = ep->bRefresh;
    // endpoint->synchAddress - ep->bSynchAddress;
}

PYUSB_STATIC Py_usb_Endpoint *new_Endpoint(
    struct usb_endpoint_descriptor *ep
    )
{
    Py_usb_Endpoint *endpoint;

    endpoint = PyObject_New(Py_usb_Endpoint,
                            &Py_usb_Endpoint_Type);

    if (endpoint && ep)
        set_Endpoint_fields(endpoint, ep);

    return endpoint;
}

PYUSB_STATIC PyMemberDef Py_usb_Interface_Members[] = {
    {"interfaceNumber",
     T_UBYTE,
     offsetof(Py_usb_Interface, interfaceNumber),
     READONLY,
     "Identifies the interface."},

    {"alternateSetting",
     T_UBYTE,
     offsetof(Py_usb_Interface, alternateSetting),
     READONLY,
     "Alternate setting number."},

    {"interfaceClass",
     T_UBYTE,
     offsetof(Py_usb_Interface, interfaceClass),
     READONLY,
     "Similar to DeviceClass in the device descriptor, but\n"
     "for devices with a class specified by the interface."},

    {"interfaceSubClass",
     T_UBYTE,
     offsetof(Py_usb_Interface, interfaceSubClass),
     READONLY,
     "Similar to bDeviceSubClass in the device\n"
     "descriptor, but for devices with a class defined by the interface."},

    {"interfaceProtocol",
     T_UBYTE,
     offsetof(Py_usb_Interface, interfaceProtocol),
     READONLY,
     "Similar to bDeviceProtocol in the device\n"
     "descriptor, but for devices whose class is defined by the interface."},

    {"iInterface",
     T_UBYTE,
     offsetof(Py_usb_Interface, iInterface),
     READONLY,
     "Similar to iInterface in the device\n"
     "descriptor, but for devices whose class is defined by the interface."},

    {"endpoints",
     T_OBJECT,
     offsetof(Py_usb_Interface, endpoints),
     READONLY,
     "Tuple with interface endpoints."},
    
    {NULL}
};

PYUSB_STATIC void Py_usb_Interface_del(
    PyObject *self
    )
{
    Py_XDECREF((PyObject *) ((Py_usb_Interface *) self)->endpoints);
    PyObject_Del(self);
}

PYUSB_STATIC PyMethodDef Py_usb_Interface_Methods[] = {
    {NULL}
};

PYUSB_STATIC PyTypeObject Py_usb_Interface_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.Interface",           /*tp_name*/
    sizeof(Py_usb_Interface),   /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    Py_usb_Interface_del,     /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Interface descriptor object",     /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_Interface_Methods,  /* tp_methods */
    Py_usb_Interface_Members,  /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0                           /* destructor */
};

PYUSB_STATIC void set_Interface_fields(
    Py_usb_Interface *interface,
    struct usb_interface_descriptor *i
    )
{
    u_int8_t index;

    interface->interfaceNumber = i->bInterfaceNumber;
    interface->alternateSetting = i->bAlternateSetting;
    interface->interfaceClass = i->bInterfaceClass;
    interface->interfaceSubClass = i->bInterfaceSubClass;
    interface->interfaceProtocol = i->bInterfaceProtocol;
    interface->iInterface = i->iInterface;

    interface->endpoints = PyTuple_New(i->bNumEndpoints);

    if (!interface->endpoints) {
        return;
    }

    for (index = 0; index < i->bNumEndpoints; ++index)
        PyTuple_SET_ITEM(interface->endpoints, index,
                        (PyObject *) new_Endpoint(i->endpoint+index));
}

PYUSB_STATIC Py_usb_Interface *new_Interface(
    struct usb_interface_descriptor *i
    )
{
    Py_usb_Interface *interface;

    interface = PyObject_NEW(Py_usb_Interface, &Py_usb_Interface_Type);

    if (interface) {
        set_Interface_fields(interface, i);

        if (PyErr_Occurred()) {
            Py_XDECREF((PyObject *) interface);
            return NULL;
        }
    }

    return interface;
}

PYUSB_STATIC PyMemberDef Py_usb_Configuration_Members[] = {
    {"totalLength",
     T_USHORT,
     offsetof(Py_usb_Configuration, totalLength),
     READONLY,
     "The number of data bytes that the device returns,\n"
     "including the bytes for all of the configuration's interfaces and\n"
     "endpoints."},

    {"value",
     T_UBYTE,
     offsetof(Py_usb_Configuration, value),
     READONLY,
     "Identifies the configuration."},

    {"selfPowered",
     T_UBYTE,
     offsetof(Py_usb_Configuration, selfPowered),
     READONLY,
     "True if the device is self powered."},

    {"remoteWakeup",
     T_UBYTE,
     offsetof(Py_usb_Configuration, remoteWakeup),
     READONLY,
     "True if the device supports remote wakeup feature."},

    {"maxPower",
     T_UBYTE,
     offsetof(Py_usb_Configuration, maxPower),
     READONLY,
     "Specifies the device current. This is the absolute value,\n"
     "already multiplied by 2"},

    {"interfaces",
     T_OBJECT,
     offsetof(Py_usb_Configuration, interfaces),
     READONLY,
     "Tuple with a tuple of the configuration interfaces.\n"
     "Each element represents a sequence of the\n"
     "alternate settings for each interface."},

    {"iConfiguration",
     T_UBYTE,
     offsetof(Py_usb_Configuration, iConfiguration),
     RO,
     "Index to a string that describes the\n"
     "configuration."},

    {NULL}
};

PYUSB_STATIC PyMethodDef Py_usb_Configuration_Methods[] = {
    {NULL, NULL}
};

PYUSB_STATIC void Py_usb_Configuration_del(
    PyObject *self
    )
{
    Py_XDECREF((PyObject *) ((Py_usb_Configuration *) self)->interfaces);
    PyObject_Del(self);;
}

PYUSB_STATIC PyTypeObject Py_usb_Configuration_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.Configuration",       /*tp_name*/
    sizeof(Py_usb_Configuration),   /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    Py_usb_Configuration_del,  /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Configuration descriptor object",    /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_Configuration_Methods,  /* tp_methods */
    Py_usb_Configuration_Members,  /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0           /* destructor */
};

PYUSB_STATIC void set_Configuration_fields(
    Py_usb_Configuration *configuration,
    struct usb_config_descriptor *config
    )
{
    u_int8_t i, j, k;
    PyObject *t1;

    configuration->totalLength = config->wTotalLength;
    configuration->value = config->bConfigurationValue;
    configuration->iConfiguration = config->iConfiguration;
    configuration->selfPowered = (config->bmAttributes >> 6) & 1;
    configuration->remoteWakeup = (config->bmAttributes >> 5) & 1;
    configuration->maxPower = config->MaxPower << 2;

    configuration->interfaces = PyTuple_New(config->bNumInterfaces);

    if (!configuration->interfaces) return;

    for (i = 0; i < config->bNumInterfaces; ++i) {
        k = config->interface[i].num_altsetting;
        t1 = PyTuple_New(k);

        if (!t1) return;

        for (j = 0; j < k; ++j)
            PyTuple_SET_ITEM(t1, j,
                    (PyObject *) new_Interface(config->interface[i].altsetting+j));

        PyTuple_SET_ITEM(configuration->interfaces, i, t1);
    }
}

PYUSB_STATIC Py_usb_Configuration *new_Configuration(
    struct usb_config_descriptor *conf
    )
{
    Py_usb_Configuration *configuration;

    configuration = PyObject_NEW(Py_usb_Configuration, &Py_usb_Configuration_Type);

    if (configuration) {
        set_Configuration_fields(configuration, conf);

        if (PyErr_Occurred()) {
            Py_DECREF((PyObject *) configuration);
            return NULL;
        }
    }

    return configuration;
}

PYUSB_STATIC PyMemberDef Py_usb_Device_Members[] = {
    {"usbVersion",
     T_STRING_INPLACE,
     offsetof(Py_usb_Device, usbVersion),
     READONLY,
     "String containing the USB specification number that\n"
     "the device and its descriptors comply with."},

    {"deviceClass",
     T_UBYTE,
     offsetof(Py_usb_Device, deviceClass),
     READONLY,
     "For devices that belong to a class, this field may\n"
     "name the class."},

    {"deviceSubClass",
     T_UBYTE,
     offsetof(Py_usb_Device, deviceSubClass),
     READONLY,
     "For devices that belong to a class, this field may\n"
     "specify a subclass within the class."},

    {"deviceProtocol",
     T_UBYTE,
     offsetof(Py_usb_Device, deviceProtocol),
     READONLY,
     "This field may specify a protocol defined by the\n"
     "selected class or subclass."},
    
    {"maxPacketSize",
     T_UBYTE,
     offsetof(Py_usb_Device, maxPacketSize),
     READONLY,
     "The maximum packet size for Endpoint 0."},

    {"idVendor",
     T_USHORT,
     offsetof(Py_usb_Device, idVendor),
     READONLY,
     "Unique vendor identifier."},

    {"idProduct",
     T_USHORT,
     offsetof(Py_usb_Device, idProduct),
     READONLY,
     "The manufacturer assigns a Product ID to identify the\n"
     "device."},

    {"deviceVersion",
     T_STRING_INPLACE,
     offsetof(Py_usb_Device, deviceVersion),
     READONLY,
     "String containing the device's release number."},

    {"filename",
     T_STRING_INPLACE,
     offsetof(Py_usb_Device, filename),
     READONLY,
     ""},
    
    {"configurations",
     T_OBJECT,
     offsetof(Py_usb_Device, configurations),
     READONLY,
     "Tuple with the device configurations."},

    {"iManufacturer",
     T_UBYTE,
     offsetof(Py_usb_Device, iManufacturer),
     RO,
     "An index that points to a string describing the\n"
     "manufacturer."},

    {"iProduct",
     T_UBYTE,
     offsetof(Py_usb_Device, iProduct),
     RO,
     "An index that points to a string describing the product."},

    {"iSerialNumber",
     T_UBYTE,
     offsetof(Py_usb_Device, iSerialNumber),
     RO,
     "An index that points to a string containing the\n"
     "device's serial number."},

    {"devnum",
     T_UBYTE,
     offsetof(Py_usb_Device, devnum),
     RO,
     "devnum of the device.\n"},

    {NULL}
};

PYUSB_STATIC PyObject *Py_usb_Device_open(
    PyObject *self,
    PyObject *args
    )
{
    return (PyObject *) new_DeviceHandle((Py_usb_Device *) self);
}

PYUSB_STATIC PyMethodDef Py_usb_Device_Methods[] = {
    {"open",
     Py_usb_Device_open,
     METH_NOARGS,
     "open() -> DeviceHandle\n\n"
     "Open the device for use.\n"
     "Returns a DeviceHandle object."},

    {NULL, NULL}
};

PYUSB_STATIC void Py_usb_Device_del(
    PyObject *self
    )
{
    Py_XDECREF(((Py_usb_Device *) self)->configurations);
    PyObject_Del(self);;
}

PYUSB_STATIC PyTypeObject Py_usb_Device_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.Device",              /*tp_name*/
    sizeof(Py_usb_Device),     /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    Py_usb_Device_del,         /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Device descriptor object",        /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_Device_Methods,     /* tp_methods */
    Py_usb_Device_Members,     /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0                   /* destructor */
};

PYUSB_STATIC void set_Device_fields(
    Py_usb_Device *device,
    struct usb_device *dev
    )
{
    struct usb_device_descriptor *desc = &dev->descriptor;
    u_int8_t i;

    device->usbVersion[0] = ((desc->bcdUSB >> 12) & 0xf) + '0';
    device->usbVersion[1] = ((desc->bcdUSB >> 8) & 0xf) + '0';
    device->usbVersion[2] = '.';
    device->usbVersion[3] = ((desc->bcdUSB >> 4) & 0xf) + '0';
    device->usbVersion[4] = (desc->bcdUSB & 0xf) + '0';
    device->usbVersion[5] = '\0';

    device->deviceVersion[0] = ((desc->bcdDevice >> 12) & 0xf) + '0';
    device->deviceVersion[1] = ((desc->bcdDevice >> 8) & 0xf) + '0';
    device->deviceVersion[2] = '.';
    device->deviceVersion[3] = ((desc->bcdDevice >> 4) & 0xf) + '0';
    device->deviceVersion[4] = (desc->bcdDevice & 0xf) + '0';
    device->deviceVersion[5] = '\0';

    device->deviceClass = desc->bDeviceClass;
    device->deviceSubClass = desc->bDeviceSubClass;
    device->deviceProtocol = desc->bDeviceProtocol;
    device->maxPacketSize = desc->bMaxPacketSize0;
    device->idVendor = desc->idVendor;
    device->idProduct = desc->idProduct;
    device->iManufacturer = desc->iManufacturer;
    device->iProduct = desc->iProduct;
    device->iSerialNumber = desc->iSerialNumber;
    strcpy(device->filename, dev->filename);
    device->dev = dev;
    device->devnum = dev->devnum;

    if (!dev->config) {
        device->configurations = PyTuple_New(0);
        return;
    }

    device->configurations = PyTuple_New(desc->bNumConfigurations);

    if (!device->configurations) return;

    for (i = 0; i < desc->bNumConfigurations; ++i)
        PyTuple_SET_ITEM(device->configurations, i, (PyObject *) new_Configuration(dev->config+i));
}

PYUSB_STATIC Py_usb_Device *new_Device(
    struct usb_device *dev
    )
{
    Py_usb_Device *device;

    device = PyObject_NEW(Py_usb_Device, &Py_usb_Device_Type);

    if (device) {
        set_Device_fields(device, dev);

        if (PyErr_Occurred()) {
            Py_DECREF((PyObject *) device);
            return NULL;
        }
    }

    return device;
}

PYUSB_STATIC PyMemberDef Py_usb_Bus_Members[] = {
    {"dirname",
     T_STRING_INPLACE,
     offsetof(Py_usb_Bus, dirname),
     READONLY,
     ""},

    {"location",
     T_UINT,
     offsetof(Py_usb_Bus, location),
     READONLY,
     ""},

    {"devices",
     T_OBJECT,
     offsetof(Py_usb_Bus, devices),
     READONLY,
     "Tuple with the bus devices"},

    {NULL}
};

PYUSB_STATIC PyMethodDef Py_usb_Bus_Methods[] = {
    {NULL, NULL}
};

PYUSB_STATIC void Py_usb_Bus_del(
    PyObject *self
    )
{
    Py_XDECREF(((Py_usb_Bus *) self)->devices);
    PyObject_Del(self);
}

PYUSB_STATIC PyTypeObject Py_usb_Bus_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.Bus",                 /*tp_name*/
    sizeof(Py_usb_Bus),        /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    Py_usb_Bus_del,            /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "Bus object",              /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_Bus_Methods,        /* tp_methods */
    Py_usb_Bus_Members,        /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0                           /* destructor */
};

PYUSB_STATIC Py_usb_Bus *new_Bus(
    struct usb_bus *b
    )
{
    Py_usb_Bus *bus;
    u_int32_t i;
    struct usb_device *dev;

    bus = PyObject_NEW(Py_usb_Bus, &Py_usb_Bus_Type);

    if (bus) {
        bus->location = b->location;
        strcpy(bus->dirname, b->dirname);
        for(dev = b->devices, i = 0; dev; dev = dev->next) ++i;
        bus->devices = PyTuple_New(i);

        if(!bus->devices) {
            Py_DECREF((PyObject *) bus);
            return NULL;
        }

        for(dev = b->devices, i=0; dev; dev = dev->next, ++i)
            PyTuple_SET_ITEM(bus->devices, i, (PyObject *) new_Device(dev));

        if (PyErr_Occurred()) {
            Py_DECREF((PyObject *) bus);
            bus = NULL;
        }
    }

    return bus;
}

PYUSB_STATIC PyMemberDef Py_usb_DeviceHandle_Members[] = {
    {NULL}
};

/*
 * def controlMsg(requestType, request, buffer, value = 0, index = 0, timeout = 100)
 */
PYUSB_STATIC PyObject *Py_usb_DeviceHandle_controlMsg(
    PyObject *self,
    PyObject *args,
    PyObject *kwds
    )
{
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;
    int requestType;
    int request;
    int value = 0;
    int index = 0;
    char *bytes;
    PyObject *data;
    Py_ssize_t size;
    int timeout = DEFAULT_TIMEOUT;
    int ret;
    int as_read = 0;

    static char *kwlist[] = {
        "requestType",
        "request",
        "buffer",
        "value",
        "index",
        "timeout",
        NULL
    };

    if (!PyArg_ParseTupleAndKeywords(args,
                                     kwds,
                                     "iiO|iii",
                                     kwlist,
                                     &requestType,
                                     &request,
                                     &data,
                                     &value,
                                     &index,
                                     &timeout)) {
        return NULL;
    }

    /*
     * If is a number, should be a read operation...
     */
    if (PyNumber_Check(data)) {
        size = py_NumberAsInt(data);
        if (PyErr_Occurred()) return NULL;
        bytes = (char *) PyMem_Malloc(size);
        if (!bytes) return NULL;
        as_read = 1;
    } else {
        bytes = (char *) getBuffer(data, &size);
        if (PyErr_Occurred()) return NULL;
    }


#if DUMP_PARAMS

    fprintf(stderr, "controlMsg params:\n"
           "\trequestType: %d\n"
           "\trequest: %d\n"
           "\tvalue: %d\n"
           "\tindex: %d\n"
           "\ttimeout: %d\n",
           requestType,
           request,
           value,
           index,
           timeout);

    if (as_read) {
        fprintf(stderr, "\tbuffer: %d\n", size);
    } else {
        fprintf(stderr, "controlMsg buffer param:\n");
        printBuffer(bytes, size);
    }

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_control_msg(_self->deviceHandle,
                          requestType,
                          request,
                          value,
                          index,
                          bytes,
                          size,
                          timeout);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyMem_Free(bytes);
        PyUSB_Error(ret);
        return NULL;
    } else if (as_read) {
        PyObject *retObj = buildTuple(bytes, ret);
        PyMem_Free(bytes);
        return retObj;
    } else {
        PyMem_Free(bytes);
        return PyInt_FromLong(ret);
    }
}

/*
 * def setConfiguration(configuration)
 */
PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setConfiguration(
    PyObject *self,
    PyObject *args
    )
{
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;
    int configuration;
    int ret;

    if (SUPPORT_NUMBER_PROTOCOL(args)) {
        configuration = (int) PyInt_AS_LONG(args);
    } else if (PyObject_TypeCheck(args, &Py_usb_Configuration_Type)) {
        configuration = ((Py_usb_Configuration *) args)->value;
    } else {
        PyErr_BadArgument();
        return NULL;
    }

#if DUMP_PARAMS
    
    fprintf(stderr,
            "setConfiguration params:\n\tconfiguration: %d\n",
            configuration);

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_set_configuration(_self->deviceHandle, configuration);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        Py_RETURN_NONE;
    }
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_claimInterface(
    PyObject *self,
    PyObject *args
    )
{
    int interfaceNumber;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;
    int ret;

    if (SUPPORT_NUMBER_PROTOCOL(args)) {
        interfaceNumber = py_NumberAsInt(args);
        if (PyErr_Occurred()) return NULL;
    } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) {
        interfaceNumber = ((Py_usb_Interface *) args)->interfaceNumber;
    } else {
        PyErr_BadArgument();
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "claimInterface params:\n\tinterfaceNumber: %d\n",
            interfaceNumber);

#endif /* DUMP_PARAMS */

    ret = usb_claim_interface(_self->deviceHandle, interfaceNumber);
    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        _self->interfaceClaimed = interfaceNumber;
    }

    Py_RETURN_NONE;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_detachKernelDriver(
    PyObject *self,
    PyObject *args
    )
{
    int interfaceNumber;
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
    int ret;
#endif
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (SUPPORT_NUMBER_PROTOCOL(args)) {
        interfaceNumber = py_NumberAsInt(args);
        if (PyErr_Occurred()) return NULL;
    } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) {
        interfaceNumber = ((Py_usb_Interface *) args)->interfaceNumber;
    } else {
        PyErr_BadArgument();
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "detachKernelDriver params:\n\tinterfaceNumber: %d\n",
            interfaceNumber);

#endif /* DUMP_PARAMS */
    
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
    Py_BEGIN_ALLOW_THREADS
    ret = usb_detach_kernel_driver_np(_self->deviceHandle, interfaceNumber);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } 
#endif

    Py_RETURN_NONE;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_releaseInterface(
    PyObject *self,
    PyObject *args
    )
{
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (-1 != _self->interfaceClaimed) {
        int ret;
        Py_BEGIN_ALLOW_THREADS
        ret = usb_release_interface(_self->deviceHandle, _self->interfaceClaimed);
        Py_END_ALLOW_THREADS

        if (ret < 0) {
            PyUSB_Error(ret);
            return NULL;
        } else {
            _self->interfaceClaimed = -1;
        }
    } else {
        PyErr_SetString(PyExc_ValueError, "No interface claimed");
        return NULL;
    }

    Py_RETURN_NONE;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_setAltInterface(
    PyObject *self,
    PyObject *args
    )
{
    int altInterface, ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (SUPPORT_NUMBER_PROTOCOL(args)) {
        altInterface = (int) py_NumberAsInt(args);
        if (PyErr_Occurred()) return NULL;
    } else if (PyObject_TypeCheck(args, &Py_usb_Interface_Type)) {
        altInterface = ((Py_usb_Interface *) args)->alternateSetting;
    } else {
        PyErr_BadArgument();
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "setAltInterface params:\n\taltInterface: %d\n",
            altInterface);

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_set_altinterface(_self->deviceHandle, altInterface);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        Py_RETURN_NONE;
    }
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkWrite(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint;
    int timeout = DEFAULT_TIMEOUT;
    char *data;
    Py_ssize_t size;
    PyObject *bytes;
    int ret;
    PyObject *retObj;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                          "iO|i",
                          &endpoint,
                          &bytes,
                          &timeout)) {
        return NULL;
    }

    data = getBuffer(bytes, &size);
    if (PyErr_Occurred()) return NULL;

#if DUMP_PARAMS

    fprintf(stderr,
            "bulkWrite params:\n"
            "\tendpoint: %d\n"
            "\ttimeout: %d\n",
            endpoint,
            timeout);

    fprintf(stderr, "bulkWrite buffer param:\n");
    printBuffer(data, size);

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_bulk_write(_self->deviceHandle, endpoint, data, size, timeout);
    Py_END_ALLOW_THREADS

    PyMem_Free(data);

    if (ret < 0) {
                printf("%d\n", ret);
        PyUSB_Error(ret);
        return NULL;
    } else {
        retObj = PyInt_FromLong(ret);
    }

    return retObj;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_bulkRead(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint;
    int timeout = DEFAULT_TIMEOUT;
    char *buffer;
    int size;
    PyObject *ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                         "ii|i",
                         &endpoint,
                         &size,
                         &timeout)) {
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "bulkRead params:\n"
            "\tendpoint: %d\n"
            "\tsize: %d\n"
            "\ttimeout: %d\n",
            endpoint,
            size,
            timeout);

#endif /* DUMP_PARAMS */

    buffer = (char *) PyMem_Malloc(size);
    if (!buffer) return NULL;

    Py_BEGIN_ALLOW_THREADS
    size = usb_bulk_read(_self->deviceHandle, endpoint, buffer, size, timeout);
    Py_END_ALLOW_THREADS

    if (size < 0) {
        PyMem_Free(buffer);
        PyUSB_Error(size);
        return NULL;
    } else {
        ret = buildTuple(buffer, size);
        PyMem_Free(buffer);
    }

    return ret;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptWrite(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint;
    int timeout = DEFAULT_TIMEOUT;
    char *data;
    Py_ssize_t size;
    PyObject *bytes;
    int ret;
    PyObject *retObj;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                          "iO|i",
                          &endpoint,
                          &bytes,
                          &timeout)) {
        return NULL;
    }

    data = getBuffer(bytes, &size);
    if (PyErr_Occurred()) return NULL;

#if DUMP_PARAMS

    fprintf(stderr,
            "interruptWrite params:\n"
            "\tendpoint: %d\n"
            "\ttimeout: %d\n",
            endpoint,
            timeout);

    fprintf(stderr, "interruptWrite buffer param:\n");
    printBuffer(data, size);

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_interrupt_write(_self->deviceHandle, endpoint, data, size, timeout);
    Py_END_ALLOW_THREADS

    PyMem_Free(data);

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        retObj = PyInt_FromLong(ret);
    }

    return retObj;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_interruptRead(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint;
    int timeout = DEFAULT_TIMEOUT;
    char *buffer;
    int size;
    PyObject *ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                         "ii|i",
                         &endpoint,
                         &size,
                         &timeout)) {
        return NULL;
    }
    
#if DUMP_PARAMS

    fprintf(stderr,
            "interruptRead params:\n"
            "\tendpoint: %d\n"
            "\tsize: %d\n"
            "\ttimeout: %d\n",
            endpoint,
            size,
            timeout);

#endif /* DUMP_PARAMS */

    buffer = (char *) PyMem_Malloc(size);
    if (!buffer) return NULL;

    Py_BEGIN_ALLOW_THREADS
    size = usb_interrupt_read(_self->deviceHandle, endpoint, buffer, size, timeout);
    Py_END_ALLOW_THREADS

    if (size < 0) {
        PyMem_Free(buffer);
        PyUSB_Error(size);
        return NULL;
    } else {
        ret = buildTuple(buffer, size);
        PyMem_Free(buffer);
    }

    return ret;
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_resetEndpoint(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint, ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    endpoint = py_NumberAsInt(args);
    if (PyErr_Occurred()) return NULL;

#if DUMP_PARAMS

    fprintf(stderr,
            "resetEndpoint params:\n"
            "\tendpoint: %d\n",
            endpoint);

#endif /* DUMP_PARAMS */
    Py_BEGIN_ALLOW_THREADS
    ret = usb_resetep(_self->deviceHandle, endpoint);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        Py_RETURN_NONE;
    }
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_reset(
    PyObject *self,
    PyObject *args
    )
{
    int ret;

    Py_BEGIN_ALLOW_THREADS
    ret = usb_reset(((Py_usb_DeviceHandle *) self)->deviceHandle);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        Py_RETURN_NONE;
    }
}

PYUSB_STATIC PyObject *Py_usb_DeviceHandle_clearHalt(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint, ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    endpoint = py_NumberAsInt(args);
    if (PyErr_Occurred()) return NULL;

#if DUMP_PARAMS

    fprintf(stderr,
            "clearHalt params:\n"
            "\tendpoint: %d\n",
            endpoint);

#endif /* DUMP_PARAMS */

    Py_BEGIN_ALLOW_THREADS
    ret = usb_clear_halt(_self->deviceHandle, endpoint);
    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    } else {
        Py_RETURN_NONE;
    }
}

/*
 * def getString(index, len, langid = -1)
 */
PYUSB_STATIC PyObject *Py_usb_DeviceHandle_getString(
    PyObject *self,
    PyObject *args
    )
{
    int langid=-1, index;
    unsigned long len;
    PyObject *retStr;
    char *buffer;
    char *rawbuffer;
    int ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                         "ik|i",
                         &index,
                         &len,
                         &langid)) {
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "getString params:\n"
            "\tindex: %d\n"
            "\tlen: %lu\n"
            "\tlangid: %d\n",
            index,
            len,
            langid);

#endif /* DUMP_PARAMS */

    ++len;  /* for NULL termination */
    buffer = (char *) PyMem_Malloc(len);
    if (!buffer) return NULL;
    rawbuffer = (char *) PyMem_Malloc(len*2); /* for string descriptor */
    if (!rawbuffer) return NULL;

    Py_BEGIN_ALLOW_THREADS

    if (-1 == langid) {
        ret = usb_get_string_simple(_self->deviceHandle, index, buffer, len);
    } else {
        int di, si;

        ret = usb_get_string(_self->deviceHandle, index, langid, rawbuffer, len*2);
        /* convert string descriptor to ascii string */
        for (di = 0, si = 2; si < rawbuffer[0] && si < ret-1; si += 2) {
            if (rawbuffer[si + 1])   /* high byte */
                buffer[di++] = '?';
            else
                buffer[di++] = rawbuffer[si];
        }
        buffer[di] = 0;
        ret = di;
    }

    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyMem_Free(buffer);
        PyMem_Free(rawbuffer);
        PyUSB_Error(ret);
        return NULL;
    }

    retStr = PyString_FromStringAndSize(buffer, ret);
    PyMem_Free(buffer);
    return retStr;
}

/*
 * def getDescriptor(type, index, len, endpoint = -1)
 */
PYUSB_STATIC PyObject *Py_usb_DeviceHandle_getDescriptor(
    PyObject *self,
    PyObject *args
    )
{
    int endpoint=-1, type, index;
    int len;
    PyObject *retSeq;
    char *buffer;
    int ret;
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;

    if (!PyArg_ParseTuple(args,
                         "iii|i",
                         &type,
                         &index,
                         &len,
                         &endpoint)) {
        return NULL;
    }

#if DUMP_PARAMS

    fprintf(stderr,
            "getDescriptor params:\n"
            "ttype: %d\n"
            "\tindex: %d\n"
            "\tlen: %d\n"
            "\tendpoint: %d\n",
            type,
            index,
            len,
            endpoint);

#endif /* DUMP_PARAMS */

    buffer = (char *) PyMem_Malloc(len);
    if (!buffer) return NULL;

    Py_BEGIN_ALLOW_THREADS

    if (-1 == endpoint) {
        ret = usb_get_descriptor(_self->deviceHandle, type, index, buffer, len);
    } else {
        ret = usb_get_descriptor_by_endpoint(_self->deviceHandle, endpoint, type, index, buffer, len);
    }

    Py_END_ALLOW_THREADS

    if (ret < 0) {
        PyMem_Free(buffer);
        PyUSB_Error(ret);
        return NULL;
    }

    retSeq = buildTuple(buffer, ret);
    PyMem_Free(buffer);
    return retSeq;
}

PYUSB_STATIC PyMethodDef Py_usb_DeviceHandle_Methods[] = {
    {"controlMsg",
     (PyCFunction) Py_usb_DeviceHandle_controlMsg,
     METH_VARARGS | METH_KEYWORDS,
     "controlMsg(requestType, request, buffer, value=0, index=0, timeout=100) -> bytesWritten|buffer\n\n"
     "Performs a control request to the default control pipe on a device.\n"
     "Arguments:\n"
     "\trequestType: specifies the direction of data flow, the type\n"
     "\t             of request, and the recipient.\n"
     "\trequest: specifies the request.\n"
     "\tbuffer: if the transfer is a write transfer, buffer is a sequence \n"
     "\t        with the transfer data, otherwise, buffer is the number of\n"
     "\t        bytes to read.\n"
     "\tvalue: specific information to pass to the device. (default: 0)\n"
     "\tindex: specific information to pass to the device. (default: 0)\n"
     "\ttimeout: operation timeout in miliseconds. (default: 100)\n"
     "Returns the number of bytes written."},

    {"setConfiguration",
     Py_usb_DeviceHandle_setConfiguration,
     METH_O,
     "setConfiguration(configuration) -> None\n\n"
     "Sets the active configuration of a device.\n"
     "Arguments:\n"
     "\tconfiguration: a configuration value or a Configuration object."},   

    {"claimInterface",
     Py_usb_DeviceHandle_claimInterface,
     METH_O,
     "claimInterface(interface) -> None\n\n"
     "Claims the interface with the Operating System.\n"
     "Arguments:\n"
     "\tinterface: interface number or an Interface object."},
    
    {"detachKernelDriver",
     Py_usb_DeviceHandle_detachKernelDriver,
     METH_O,
     "detachKernelDriver(interface) -> None\n\n"
     "Detaches a kernel driver from the interface (if one is attached,\n"
     "we have permission and the operation is supported by the OS)\n"
     "Arguments:\n"
     "\tinterface: interface number or an Interface object."},

    {"releaseInterface",
     Py_usb_DeviceHandle_releaseInterface,
     METH_NOARGS,
     "releaseInterface() -> None\n\n"
     "Releases an interface previously claimed with claimInterface."},
    
    {"setAltInterface",
     Py_usb_DeviceHandle_setAltInterface,
     METH_O,
     "setAltInterface(alternate) -> None\n\n"
     "Sets the active alternate setting of the current interface.\n"
     "Arguments:\n"
     "\talternate: an alternate setting number or an Interface object."},

    {"bulkWrite",
     Py_usb_DeviceHandle_bulkWrite,
     METH_VARARGS,
     "bulkWrite(endpoint, buffer, timeout=100) -> bytesWritten\n\n"
     "Performs a bulk write request to the endpoint specified.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"
     "\tbuffer: sequence data buffer to write.\n"
     "\t      This parameter can be any sequence type\n"
     "\ttimeout: operation timeout in miliseconds. (default: 100)\n"
     "Returns the number of bytes written."},

    {"bulkRead",
     Py_usb_DeviceHandle_bulkRead,
     METH_VARARGS,
     "bulkRead(endpoint, size, timeout=100) -> buffer\n\n"
     "Performs a bulk read request to the endpoint specified.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"
     "\tsize: number of bytes to read.\n"
     "\ttimeout: operation timeout in miliseconds. (default: 100)\n"
     "Returns a tuple with the data read."},

    {"interruptWrite",
     Py_usb_DeviceHandle_interruptWrite,
     METH_VARARGS,
     "interruptWrite(endpoint, buffer, timeout=100) -> bytesWritten\n\n"
     "Performs a interrupt write request to the endpoint specified.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"
     "\tbuffer: sequence data buffer to write.\n"
     "\t      This parameter can be any sequence type\n"
     "\ttimeout: operation timeout in miliseconds. (default: 100)\n"
     "Returns the number of bytes written."},

    {"interruptRead",
     Py_usb_DeviceHandle_interruptRead,
     METH_VARARGS,
     "interruptRead(endpoint, size, timeout=100) -> buffer\n\n"
     "Performs a interrupt read request to the endpoint specified.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"
     "\tsize: number of bytes to read.\n"
     "\ttimeout: operation timeout in miliseconds. (default: 100)\n"
     "Returns a tuple with the data read."},

    {"resetEndpoint",
     Py_usb_DeviceHandle_resetEndpoint,
     METH_O,
     "resetEndpoint(endpoint) -> None\n\n"
     "Resets all state (like toggles) for the specified endpoint.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"},

    {"reset",
     Py_usb_DeviceHandle_reset,
     METH_NOARGS,
     "reset() -> None\n\n"
     "Resets the specified device by sending a RESET\n"
     "down the port it is connected to.\n"},

    {"clearHalt",
     Py_usb_DeviceHandle_clearHalt,
     METH_O,
     "clearHalt(endpoint) -> None\n\n"
     "Clears any halt status on the specified endpoint.\n"
     "Arguments:\n"
     "\tendpoint: endpoint number.\n"},

    {"getString",
     Py_usb_DeviceHandle_getString,
     METH_VARARGS,
     "getString(index, len, langid = -1) -> string\n\n"
     "Retrieves the string descriptor specified by index\n"
     "and langid from a device.\n"
     "Arguments:\n"
     "\tindex: index of descriptor in the device.\n"
     "\tlen: number of bytes of the string\n"
     "\tlangid: Language ID. If it is omittedi, will be\n"
     "\t        used the first language.\n"},

    {"getDescriptor",
     Py_usb_DeviceHandle_getDescriptor,
     METH_VARARGS,
     "getDescriptor(type, index, len, endpoint = -1) -> descriptor\n\n"
     "Retrieves a descriptor from the device identified by the type\n"
     "and index of the descriptor.\n"
     "Arguments:\n"
     "\ttype: descriptor type.\n"
     "\tindex: index of the descriptor.\n"
     "\tlen: descriptor length.\n"
     "\tendpoint: endpoint number from descriptor is read. If it is\n"
     "\t          omitted, the descriptor is read from default control pipe.\n"},

    {NULL, NULL}
};

PYUSB_STATIC void Py_usb_DeviceHandle_del(
    PyObject *self
    )
{
    Py_usb_DeviceHandle *_self = (Py_usb_DeviceHandle *) self;
    struct usb_dev_handle *h = _self->deviceHandle;

    if (h) {
        if (-1 != _self->interfaceClaimed) {
            usb_release_interface(_self->deviceHandle, 
                                  _self->interfaceClaimed);
        }

        usb_close(_self->deviceHandle);
    }

    PyObject_Del(self);
}

PYUSB_STATIC PyTypeObject Py_usb_DeviceHandle_Type = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "usb.DeviceHandle",        /*tp_name*/
    sizeof(Py_usb_DeviceHandle), /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    Py_usb_DeviceHandle_del,   /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /*tp_flags*/
    "DeviceHandle object",     /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Py_usb_DeviceHandle_Methods, /* tp_methods */
    Py_usb_DeviceHandle_Members, /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    PyType_GenericNew,         /* tp_new */
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0                       /* destructor */
};

PYUSB_STATIC Py_usb_DeviceHandle *new_DeviceHandle(
    Py_usb_Device *device
    )
{
    Py_usb_DeviceHandle *dh;
    struct usb_dev_handle *h;

    dh = PyObject_NEW(Py_usb_DeviceHandle, &Py_usb_DeviceHandle_Type);

    if (dh) {
        h = usb_open(device->dev);

        if (!h) {
            PyUSB_Error(0);
            Py_DECREF((PyObject *) dh);
            return NULL;
        }

        dh->deviceHandle = h;
        dh->interfaceClaimed = -1;
    }

    return dh;
}

/*
 * Global functions
 */

PYUSB_STATIC PyObject *busses(
    PyObject *self,
    PyObject *args
    )
{
    PyObject *tuple;
    struct usb_bus *bus, *b;
    u_int32_t i;
    int ret;

    ret = usb_find_busses();

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    }

    ret = usb_find_devices();

    if (ret < 0) {
        PyUSB_Error(ret);
        return NULL;
    }

    bus = usb_get_busses();

    if (!bus) {
        PyUSB_Error(0);
        return NULL;
    }

    for(i=0,b=bus;b;b=b->next) ++i;
    tuple = PyTuple_New(i);
    if (!tuple) return NULL;

    for(b=bus,i=0;b;++i,b=b->next)
        PyTuple_SET_ITEM(tuple, i, (PyObject *) new_Bus(b));

    if (PyErr_Occurred()) {
        Py_DECREF(tuple);
        return NULL;
    }

    return tuple;
}

PYUSB_STATIC PyMethodDef usb_Methods[] = {
    {"busses", busses, METH_NOARGS, "Returns a tuple with the usb busses"},
    {NULL, NULL}
};

#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif /* PyMODINIT_FUNC */

/*
 * Entry point for the module
 */
PyMODINIT_FUNC initusb(void)
{
    PyObject *module;

    module = Py_InitModule3("usb", usb_Methods,"USB access module");
    if (!module) return;

    PyExc_USBError = PyErr_NewException("usb.USBError", PyExc_IOError, NULL);
    if (!PyExc_USBError) return;
    PyModule_AddObject(module, "USBError", PyExc_USBError);
    Py_INCREF(PyExc_USBError);

    if (PyType_Ready(&Py_usb_Endpoint_Type) < 0) return;
    Py_INCREF(&Py_usb_Endpoint_Type);
    PyModule_AddObject(module, "Endpoint", (PyObject *) &Py_usb_Endpoint_Type);

    if (PyType_Ready(&Py_usb_Interface_Type) < 0) return;
    Py_INCREF(&Py_usb_Interface_Type);
    PyModule_AddObject(module, "Interface", (PyObject *) &Py_usb_Interface_Type);
    
    if (PyType_Ready(&Py_usb_Configuration_Type) < 0) return;
    Py_INCREF(&Py_usb_Configuration_Type);
    PyModule_AddObject(module, "Configuration", (PyObject *) &Py_usb_Configuration_Type);

    if (PyType_Ready(&Py_usb_Device_Type) < 0) return;
    Py_INCREF(&Py_usb_Device_Type);
    PyModule_AddObject(module, "Device", (PyObject *) &Py_usb_Device_Type);

    if (PyType_Ready(&Py_usb_Bus_Type) < 0) return;
    Py_INCREF(&Py_usb_Bus_Type);
    PyModule_AddObject(module, "Bus", (PyObject *) &Py_usb_Bus_Type);

    if (PyType_Ready(&Py_usb_DeviceHandle_Type) < 0) return;
    Py_INCREF(&Py_usb_DeviceHandle_Type);
    PyModule_AddObject(module, "DeviceHandle", (PyObject *) &Py_usb_DeviceHandle_Type);

    installModuleConstants(module);

    usb_init();
}

/*
 * vim: tabstop=4:
 */
