static CFStringRef copy_printer_interface_deviceid()

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;
}