/*
   Code Sample to demonstrate how one can read a string descriptor from a USB
   device.
*/
 
#include <Types.h>
#include <Memory.h>
#include <Events.h>
#include <DriverServices.h>
#include <USB.h>
 
#define TIMEOUT 60
 
//********* PROTOTYPES ******************//
void ProbeEndpointCompletionRoutine(USBPB *usbpb);
void ExpertNumToString(SInt32 num, char *str);
OSStatus GetStringDescriptor(USBDeviceRef ref, UInt16 index,
UInt16 len, UInt8 *buf);
extern void USBIdleTask(void);
 
 
void ProbeEndpointCompletionRoutine(USBPB *usbpb)
{
    if (usbpb->usbStatus == kUSBPending)
    {
        // DebugStr("\pcompletition called with pending status!");
        usbpb->usbStatus = paramErr;
    }
    usbpb->usbRefcon = false;
}
 
// This is a simple implementation of numtostring,
// This version is fully reentrant
void ExpertNumToString(SInt32 num, char *str)
{
    char buf[16];
    SInt16 cnt=0;
 
    if (num < 0)
    {
        num = -num;
        *str++ = '-';
    }
    do
    {
        buf[cnt++] = "0123456789"[num % 10];
        num = num/10;
    } while (num);
 
    while (cnt-- > 0)
    {
        *str++ = buf[cnt];
    }
    *str = '\0';
}
 
//-------------------------------
//
//GetStringDescriptor
//
// Descriptor type == 3
//-------------------------------
OSStatus GetStringDescriptor(USBDeviceRef ref, UInt16 index, UInt16 len, UInt8 *buf)
{
    UInt32    startTime;
    OSStatus  status;
    USBPB     *pb;
    char      theText[255] = "";
    char      tempStr[8] = "";
 
    pb = (USBPB *)NewPtrSysClear(sizeof(*pb));
    if (!pb)
       return(memFullErr);
 
    // set up to make the kUSBRqGetDescriptor call
    pb->pbLength = (sizeof(*pb));
    pb->usbReference = ref;
    pb->usbRefcon = true;
    pb->usbCompletion = ProbeEndpointCompletionRoutine;
    pb->pbVersion = kUSBCurrentPBVersion;
    pb->usbBMRequestType = USBMakeBMRequestType(kUSBIn, kUSBStandard,
                                              kUSBDevice);
    pb->usbBRequest = kUSBRqGetDescriptor;
    pb->usbWValue = 0x0300 | index;
    pb->usbWIndex = 0x409;
 
    /* First get the string length in preparation for the real call */
    pb->usbReqCount = 2; // 2-byte length for Unicode strings
    pb->reserved4 = len;
    pb->usbBuffer = buf;
    *buf = 0; // in case we don't get anything back!
 
    startTime = TickCount();
    status = USBDeviceRequest(pb);
 
    if (status && (status != kUSBPending))
    { // there was an immediate error - bail!
        DisposePtr((void*)pb);
        return(status);
    }
 
    while(pb->usbRefcon /*pb->usbStatus == kUSBPending*/)
    {
        USBIdleTask();
        if (pb->usbRefcon && ((startTime+TIMEOUT) < TickCount()))
        {
            pb->usbRefcon = false; // we got tired of waiting for a response
            pb->usbStatus = -1;
        }
    }
    if (pb->usbStatus > 0 )
         pb->usbStatus = noErr;
 
    /* Now make the real call */
    if (pb->usbStatus == noErr)
    {
        pb->pbLength = (sizeof(*pb));
        pb->usbReference = ref;
        pb->usbRefcon = true;
        pb->usbCompletion = ProbeEndpointCompletionRoutine;
        pb->pbVersion = kUSBCurrentPBVersion;
        pb->usbBMRequestType = USBMakeBMRequestType(kUSBIn, kUSBStandard,
                                                kUSBDevice);
        pb->usbBRequest = kUSBRqGetDescriptor;
        pb->usbWValue = 0x0300 | index;
        pb->usbWIndex = 0x409;
        pb->usbReqCount = (UInt32)*buf; // actual data length;
        pb->reserved4 = len;
        pb->usbBuffer = buf;
        *buf = 0; // in case we don't get anything back!
 
        startTime = TickCount();
        status = USBDeviceRequest(pb);
 
        if (status && (status != kUSBPending))
        { // there was an immediate error - bail!
            DisposePtr((void*)pb);
            return(status);
        }
 
        while(pb->usbRefcon /*pb->usbStatus == kUSBPending*/)
        {
            USBIdleTask();
            if (pb->usbRefcon && ((startTime+TIMEOUT) < TickCount()))
            {
                pb->usbRefcon = false; // we got tired of waiting for a response
                pb->usbStatus = -1;
            }
        }
        if (pb->usbStatus > 0 )
        pb->usbStatus = noErr;
    }
 
    status = pb->usbStatus;
    if (!status)
    {
        DisposePtr((void*)pb);
    }
    return(status);
}