in backend/usb-darwin.c [1551:1744]
static CFStringRef copy_printer_interface_deviceid(printer_interface_t printer, UInt8 alternateSetting)
{
// I have tried to make this function as neat as I can, but the possibility of needing to resend
// a request to get the entire string makes it hideous...
//
// We package the job of sending a request up into the block (^sendRequest), which takes the size
// it should allocate for the message buffer. It frees the current buffer if one is set and
// allocates one of the specified size, then performs the request. We can then easily retry by
// calling the block again if we fail to get the whole string the first time around.
#define kUSBPrintClassGetDeviceID 0
#define kDefaultNoDataTimeout 5000L
#define pack_device_id_wIndex(intf, alt) ((UInt16)((((UInt16)(intf)) << 8) | ((UInt8)(alt))))
if (printer == NULL)
return NULL;
IOReturn err = kIOReturnError;
UInt8 configurationIndex = 0;
UInt8 interfaceNumber = 0;
size_t bufferLength = 256;
CFStringRef ret = NULL;
if ((*printer)->GetConfigurationValue( printer, &configurationIndex) == kIOReturnSuccess &&
(*printer)->GetInterfaceNumber( printer, &interfaceNumber) == kIOReturnSuccess)
{
__block IOUSBDevRequestTO request;
IOReturn (^sendRequest)(size_t) = ^ (size_t size)
{
if (request.pData)
{
free(request.pData);
request.wLength = 0;
request.pData = NULL;
}
IOReturn berr = kIOReturnError;
char *buffer = malloc(size);
if (buffer == NULL)
return kIOReturnNoMemory;
request.wLength = HostToUSBWord(size);
request.pData = buffer;
berr = (*printer)->ControlRequestTO(printer, (UInt8)0, &request);
return berr;
};
/* This request takes the 0 based configuration index. IOKit returns a 1 based configuration index */
configurationIndex -= 1;
memset(&request, 0, sizeof(request));
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface);
request.bRequest = kUSBPrintClassGetDeviceID;
request.wValue = HostToUSBWord(configurationIndex);
request.wIndex = HostToUSBWord(pack_device_id_wIndex(interfaceNumber, alternateSetting));
request.noDataTimeout = kDefaultNoDataTimeout;
request.completionTimeout = 0; // Copying behavior from Generic Class Driver
err = sendRequest(bufferLength);
if (err == kIOReturnSuccess && request.wLenDone > 1)
{
UInt16 actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
if (actualLength > 2 && actualLength <= bufferLength - 2)
{
ret = CFStringCreateWithBytes(NULL, (const UInt8 *)request.pData + 2, actualLength - 2, kCFStringEncodingUTF8, false);
}
else if (actualLength > 2) {
err = sendRequest(actualLength);
if (err == kIOReturnSuccess && request.wLenDone > 0)
{
actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
ret = CFStringCreateWithBytes(NULL, (const UInt8 *)request.pData + 2, actualLength - 2, kCFStringEncodingUTF8, false);
}
}
}
if (request.pData)
free(request.pData);
}
CFStringRef manufacturer = deviceIDCopyManufacturer(ret);
CFStringRef model = deviceIDCopyModel(ret);
CFStringRef serial = deviceIDCopySerialNumber(ret);
if (manufacturer == NULL || serial == NULL || model == NULL)
{
IOUSBDevRequestTO request;
IOUSBDeviceDescriptor desc;
memset(&request, 0, sizeof(request));
request.bmRequestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBDevice );
request.bRequest = kUSBRqGetDescriptor;
request.wValue = kUSBDeviceDesc << 8;
request.wIndex = 0;
request.wLength = sizeof(desc);
request.pData = &desc;
request.completionTimeout = 0;
request.noDataTimeout = 60L;
err = (*printer)->ControlRequestTO(printer, 0, &request);
if (err == kIOReturnSuccess)
{
CFMutableStringRef extras = CFStringCreateMutable(NULL, 0);
if (manufacturer == NULL)
{
manufacturer = copy_printer_interface_indexed_description(printer, desc.iManufacturer, kUSBLanguageEnglish);
if (manufacturer && CFStringGetLength(manufacturer) > 0)
CFStringAppendFormat(extras, NULL, CFSTR("MFG:%@;"), manufacturer);
}
if (model == NULL)
{
model = copy_printer_interface_indexed_description(printer, desc.iProduct, kUSBLanguageEnglish);
if (model && CFStringGetLength(model) > 0)
CFStringAppendFormat(extras, NULL, CFSTR("MDL:%@;"), model);
}
if (desc.iSerialNumber != 0)
{
// Always look at the USB serial number since some printers
// incorrectly include a bogus static serial number in their
// IEEE-1284 device ID string...
CFStringRef userial = copy_printer_interface_indexed_description(printer, desc.iSerialNumber, kUSBLanguageEnglish);
if (userial && CFStringGetLength(userial) > 0 && (serial == NULL || CFStringCompare(serial, userial, kCFCompareCaseInsensitive) != kCFCompareEqualTo))
{
if (serial != NULL)
{
// 1284 serial number doesn't match USB serial number, so replace the existing SERN: in device ID
CFRange range = CFStringFind(ret, serial, 0);
CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(NULL, 0, ret);
CFStringReplace(deviceIDString, range, userial);
CFRelease(ret);
ret = deviceIDString;
CFRelease(serial);
}
else
{
// No 1284 serial number so add SERN: with USB serial number to device ID
CFStringAppendFormat(extras, NULL, CFSTR("SERN:%@;"), userial);
}
serial = userial;
}
else if (userial != NULL)
CFRelease(userial);
}
if (ret != NULL)
{
CFStringAppend(extras, ret);
CFRelease(ret);
}
ret = extras;
}
}
if (ret != NULL)
{
/* Remove special characters from the serial number */
CFRange range = (serial != NULL ? CFStringFind(serial, CFSTR("+"), 0) : CFRangeMake(0, 0));
if (range.length == 1)
{
range = CFStringFind(ret, serial, 0);
CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(NULL, 0, ret);
CFRelease(ret);
ret = deviceIDString;
CFStringFindAndReplace(deviceIDString, CFSTR("+"), CFSTR(""), range, 0);
}
}
if (manufacturer != NULL)
CFRelease(manufacturer);
if (model != NULL)
CFRelease(model);
if (serial != NULL)
CFRelease(serial);
if (ret != NULL && CFStringGetLength(ret) == 0)
{
CFRelease(ret);
return NULL;
}
return ret;
}