/******************************************************************************
USB Host Printer Client Driver
Summary:
This is the Printer client driver file for a USB Embedded Host device.
Description:
This is the Printer client driver file for a USB Embedded Host device.
It allows an embedded application to utilize a USB printer to provide
printed output.
USB printers utilize the USB Printer Class to communicate with a USB
Host. This class defines the USB transfer type, the endpoint structure,
a device requests that can be performed. The actual commands sent to
the printer, however, are dictated by the printer language used by the
particular printer.
Many different printer languages are utilized by the wide variety of
printers on the market. Typically, low end printers receive printer-specific
binary data, utilizing the processing power of the USB Host to perform
all of the complex calculations required to translate text and graphics to
a simple binary representation. This works well when a PC is the USB Host,
but it is not conducive to an embedded application with limited resources.
Many printers on the market use a command based printer language, relying
on the printer itself to interpret commands to produce the desired output.
Some languages are standardized across printers from a particular
manufacturer, and some are used across multiple manufacturer. This method
lends itself better to embedded applications by allowing the printer to
take on some of the computational overhead. Microchip provides support for
some printer languages, including PostScript and PCL 5. Additional printer
language can be implemented. Refer to the USB Embedded Host Printer Class
application notes for more details on implementing printer language support.
Remarks:
This driver should be used in a project with usb_host.c to provided the USB
Embedded Host and hardware interfaces, plus one or more language support
files.
To interface with USB Embedded Host layer, the routine USBHostPrinterInitialize()
should be specified as the Initialize() function, and
USBHostPrinterEventHandler() should be specified as the EventHandler()
function in the usbClientDrvTable[] array declared in usb_config.c.
This driver requires transfer events from usb_host.c, so
USB_ENABLE_TRANSFER_EVENT must be defined.
Since the printer class is performed with bulk transfers,
USB_SUPPORT_BULK_TRANSFERS must be defined.
*******************************************************************************/
//DOM-IGNORE-BEGIN
/******************************************************************************
FileName: usb_host_printer.c
Dependencies: None
Processor: PIC24F/PIC32MX
Compiler: C30/C32
Company: Microchip Technology, Inc.
Software License Agreement
The software supplied herewith by Microchip Technology Incorporated
(the Company) for its PICmicro® Microcontroller is intended and
supplied to you, the Companys customer, for use solely and
exclusively on Microchip PICmicro Microcontroller products. The
software is owned by the Company and/or its supplier, and is
protected under applicable copyright laws. All rights are reserved.
Any use in violation of the foregoing restrictions may subject the
user to criminal sanctions under applicable laws, as well as to
civil liability for the breach of the terms and conditions of this
license.
THIS SOFTWARE IS PROVIDED IN AN AS IS CONDITION. NO WARRANTIES,
WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
Change History:
Rev Description
---------- -----------
2.6 - 2.6a No chance except stack revision number
2.7 Minor updates to USBHostPrinterGetStatus() header
to better describe the function requirements and
operation.
Changed how transfer queues are handled to do a peek
now before removing the item from the queue.
2.7a Provided macro wrapped versions of malloc() and free()
so that a user can override these functions easily.
********************************************************************/
//DOM-IGNORE-END
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "GenericTypeDefs.h"
#include "usb_config.h"
#include "struct_queue.h"
#include "USB\usb.h"
#include "USB\usb_host_printer.h"
#ifdef USB_PRINTER_LANGUAGE_PCL_5
#include "USB\usb_host_printer_pcl_5.h"
#endif
#ifdef USB_PRINTER_LANGUAGE_POSTSCRIPT
#include "USB\usb_host_printer_postscript.h"
#endif
#ifndef USB_MALLOC
#define USB_MALLOC(size) malloc(size)
#endif
#ifndef USB_FREE
#define USB_FREE(ptr) free(ptr)
#endif
#define USB_FREE_AND_CLEAR(ptr) {USB_FREE(ptr); ptr = NULL;}
//#define DEBUG_MODE
//#define DEBUG_PRINT_COMMANDS
#if defined( DEBUG_MODE ) || defined( DEBUG_PRINT_COMMANDS )
#include "uart2.h"
#endif
// *****************************************************************************
// *****************************************************************************
// Section: Configuration
// *****************************************************************************
// *****************************************************************************
#if !defined(USB_ENABLE_TRANSFER_EVENT)
#error The USB Host Printer Client Driver requires transfer events.
#endif
// *****************************************************************************
// *****************************************************************************
// Section: Constants
// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
// Section: Interface and Protocol Constants
// *****************************************************************************
#define DEVICE_CLASS_PRINTER 0x07 // Class code for Printers
#define DEVICE_SUBCLASS_PRINTERS 0x01 // SubClass code for Printers
#define DEVICE_INTERFACE_UNIDIRECTIONAL 0x01 // Protocol code for unidirectional interface
#define DEVICE_INTERFACE_BIDIRECTIONAL 0x02 // Protocol code for bidirectional interface
#define DEVICE_INTERFACE_IEEE1284_4 0x03 // Protocol code for IEEE 1284.4 interface
// *****************************************************************************
// *****************************************************************************
// Section: Data Structures
// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
/* Printer Transfer Queue Information
This structure contains the information needed for one entry in the transfer
queue.
*/
typedef struct _USB_PRINTER_QUEUE_ITEM
{
DWORD size;
BYTE *data;
BYTE flags;
} USB_PRINTER_QUEUE_ITEM;
// *****************************************************************************
/* Printer Transfer Queue
This is the structure for the printer transfer queue.
*/
typedef struct _USB_PRINTER_QUEUE
{
int head;
int tail;
int count;
USB_PRINTER_QUEUE_ITEM buffer[USB_PRINTER_TRANSFER_QUEUE_SIZE];
} USB_PRINTER_QUEUE;
// *****************************************************************************
/* Printer Device Information
This structure contains information about an attached device, including
status flags and device identification.
*/
typedef struct _USB_PRINTER_DEVICE
{
USB_PRINTER_DEVICE_ID ID; // Identification information about the device
BYTE clientDriverID;
DWORD rxLength; // Number of bytes received in the last IN transfer
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
char* deviceIDString;
WORD deviceIDStringLength;
WORD deviceIDStringIndex;
#endif
USB_PRINTER_LANGUAGE_HANDLER languageHandler;
BYTE endpointIN; // Bulk IN endpoint
BYTE endpointOUT; // Bulk OUT endpoint
USB_PRINTER_QUEUE transferQueueIN;
USB_PRINTER_QUEUE transferQueueOUT;
union
{
BYTE value; // BYTE representation of device status flags
struct
{
BYTE inUse : 1; // This array member is in use
BYTE initialized : 1; // Driver has been initialized
BYTE txBusy : 1; // Driver busy transmitting data
BYTE rxBusy : 1; // Driver busy receiving data
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
BYTE deviceIDStringLengthValid : 1; // Device ID string length is valid
#endif
};
} flags; // Printer client driver status flags
} USB_PRINTER_DEVICE;
// *****************************************************************************
// *****************************************************************************
// Section: Global Variables
// *****************************************************************************
// *****************************************************************************
BYTE currentPrinterRecord;
extern USB_PRINTER_INTERFACE usbPrinterClientLanguages[];
USB_PRINTER_DEVICE usbPrinters[USB_MAX_PRINTER_DEVICES];
extern USB_PRINTER_SPECIFIC_INTERFACE usbPrinterSpecificLanguage[];
// *****************************************************************************
// *****************************************************************************
// Section: Local Prototypes
// *****************************************************************************
// *****************************************************************************
BOOL _USBHostPrinter_FindDevice( BYTE address );
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
BOOL _USBHostPrinter_GetDeviceIDString( void );
#endif
BYTE _USBHostPrinter_ReadFromQueue( BYTE deviceAddress );
BYTE _USBHostPrinter_WriteFromQueue( BYTE deviceAddress );
// *****************************************************************************
// *****************************************************************************
// Section: Host Stack Interface Functions
// *****************************************************************************
// *****************************************************************************
/****************************************************************************
Function:
BOOL USBHostPrinterInitialize ( BYTE address, DWORD flags, BYTE clientDriverID )
Summary:
This function is called by the USB Embedded Host layer when a printer
attaches.
Description:
This routine is a call out from the USB Embedded Host layer to the USB
printer client driver. It is called when a "printer" device has been
connected to the host. Its purpose is to initialize and activate the USB
Printer client driver.
Preconditions:
The device has been configured.
Parameters:
BYTE address - Device's address on the bus
DWORD flags - Initialization flags
BYTE clientDriverID - Client driver identification for device requests
Return Values:
TRUE - Initialization was successful
FALSE - Initialization failed
Remarks:
Multiple client drivers may be used in a single application. The USB
Embedded Host layer will call the initialize routine required for the
attached device.
***************************************************************************/
BOOL USBHostPrinterInitialize ( BYTE address, DWORD flags, BYTE clientDriverID )
{
BYTE endpointIN;
BYTE endpointOUT;
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
BYTE errorCode;
#endif
WORD i;
WORD j;
BYTE *pDesc;
BYTE *pDescriptor;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Printer Client Init called\r\n" );
#endif
for (currentPrinterRecord=0; currentPrinterRecord<USB_MAX_PRINTER_DEVICES; currentPrinterRecord++)
{
if (!usbPrinters[currentPrinterRecord].flags.inUse) break;
}
if (currentPrinterRecord == USB_MAX_PRINTER_DEVICES)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: No more space\r\n" );
#endif
return FALSE; // We have no more room for a new device.
}
pDesc = USBHostGetDeviceDescriptor(address);
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
usbPrinters[currentPrinterRecord].deviceIDString = (char *)USB_MALLOC( ((USB_DEVICE_DESCRIPTOR*)pDesc)->bMaxPacketSize0 );
if (usbPrinters[currentPrinterRecord].deviceIDString == NULL)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Out of memory for device ID string length\r\n" );
#endif
return FALSE; // Out of memory
}
#endif
// Initialize state
usbPrinters[currentPrinterRecord].rxLength = 0;
usbPrinters[currentPrinterRecord].flags.value = 0x01; // Set the inUse flag.
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
usbPrinters[currentPrinterRecord].deviceIDStringIndex = 0;
#endif
// Save device the address, VID, & PID, and client driver ID.
usbPrinters[currentPrinterRecord].ID.deviceAddress = address;
pDesc += 8;
usbPrinters[currentPrinterRecord].ID.vid = (WORD)*pDesc; pDesc++;
usbPrinters[currentPrinterRecord].ID.vid |= ((WORD)*pDesc) << 8; pDesc++;
usbPrinters[currentPrinterRecord].ID.pid = (WORD)*pDesc; pDesc++;
usbPrinters[currentPrinterRecord].ID.pid |= ((WORD)*pDesc) << 8; pDesc++;
usbPrinters[currentPrinterRecord].clientDriverID = clientDriverID;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: USB Printer Client Initalized: flags=0x" );
UART2PutHex( flags );
UART2PrintString( " address=" );
UART2PutDec( address );
UART2PrintString( " VID=0x" );
UART2PutHex( usbPrinters[currentPrinterRecord].ID.vid >> 8 );
UART2PutHex( usbPrinters[currentPrinterRecord].ID.vid & 0xFF );
UART2PrintString( " PID=0x" );
UART2PutHex( usbPrinters[currentPrinterRecord].ID.pid >> 8 );
UART2PutHex( usbPrinters[currentPrinterRecord].ID.pid & 0xFF );
UART2PrintString( "\r\n" );
#endif
// Extract the bulk IN and OUT endpoint that the printer uses.
pDescriptor = (BYTE *)USBHostGetCurrentConfigurationDescriptor( address );
i = 0;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Checking descriptor " );
UART2PutDec( pDescriptor[i+5] );
UART2PrintString( " ...\r\n" );
#endif
// Find the next interface descriptor.
while (i < ((USB_CONFIGURATION_DESCRIPTOR *)pDescriptor)->wTotalLength)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Checking interface...\r\n" );
#endif
// See if we are pointing to an interface descriptor.
if (pDescriptor[i+1] == USB_DESCRIPTOR_INTERFACE)
{
if (USBHostDeviceSpecificClientDriver( address ) ||
((pDescriptor[i+5] == DEVICE_CLASS_PRINTER) &&
(pDescriptor[i+6] == DEVICE_SUBCLASS_PRINTERS) &&
((pDescriptor[i+7] == DEVICE_INTERFACE_UNIDIRECTIONAL) ||
(pDescriptor[i+7] == DEVICE_INTERFACE_BIDIRECTIONAL))))
{
// Either this client driver was specified in the TPL for this
// exact device, or the interface has the correct class,
// subclass, and protocol values for the printer class.
#ifdef DEBUG_MODE
if (USBHostDeviceSpecificClientDriver( address ))
{
UART2PrintString( "PRN: Explicit client driver support in TPL.\r\n" );
}
#endif
// Look for bulk IN and OUT endpoints.
endpointIN = 0;
endpointOUT = 0;
// Scan for endpoint descriptors.
i += pDescriptor[i];
while (pDescriptor[i+1] == USB_DESCRIPTOR_ENDPOINT)
{
if (pDescriptor[i+3] == 0x02) // Bulk
{
if (((pDescriptor[i+2] & 0x80) == 0x80) && (endpointIN == 0))
{
endpointIN = pDescriptor[i+2];
}
if (((pDescriptor[i+2] & 0x80) == 0x00) && (endpointOUT == 0))
{
endpointOUT = pDescriptor[i+2];
}
}
i += pDescriptor[i];
}
if ((endpointIN != 0) && (endpointOUT != 0))
{
// Initialize the device endpoint information.
usbPrinters[currentPrinterRecord].endpointIN = endpointIN;
usbPrinters[currentPrinterRecord].endpointOUT = endpointOUT;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Bulk endpoint IN: " );
UART2PutHex( endpointIN );
UART2PrintString( " Bulk endpoint OUT: " );
UART2PutHex( endpointOUT );
UART2PrintString( "\r\n" );
#endif
USBHostSetNAKTimeout( address, endpointIN, 1, USB_NUM_BULK_NAKS );
USBHostSetNAKTimeout( address, endpointOUT, 1, USB_NUM_BULK_NAKS );
// See if the printer language has been specified explicitly.
j = 0;
usbPrinters[currentPrinterRecord].languageHandler = NULL;
while ((usbPrinterSpecificLanguage[j].vid != 0x0000) &&
(usbPrinters[currentPrinterRecord].languageHandler == NULL))
{
if ((usbPrinterSpecificLanguage[j].vid == usbPrinters[currentPrinterRecord].ID.vid) &&
(usbPrinterSpecificLanguage[j].pid == usbPrinters[currentPrinterRecord].ID.pid))
{
usbPrinters[currentPrinterRecord].languageHandler = usbPrinterClientLanguages[usbPrinterSpecificLanguage[j].languageIndex].languageCommandHandler;
usbPrinters[currentPrinterRecord].ID.support = usbPrinterSpecificLanguage[j].support;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Explicit language support. Flags: " );
UART2PutHex( (BYTE)usbPrinters[currentPrinterRecord].ID.support.val );
UART2PrintString( "\r\n" );
#endif
}
j ++;
}
if (usbPrinters[currentPrinterRecord].languageHandler != NULL)
{
// We have a printer language that we can use with this printer.
// Complete the client driver attachment.
usbPrinters[currentPrinterRecord].flags.initialized = 1;
USBHostPrinterCommand( usbPrinters[currentPrinterRecord].ID.deviceAddress, USB_PRINTER_ATTACHED,
USB_DATA_POINTER_RAM(&(usbPrinters[currentPrinterRecord].ID.support)), sizeof(USB_PRINTER_FUNCTION_SUPPORT), 0 );
// Tell the application layer that we have a device.
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_ATTACH, &(usbPrinters[currentPrinterRecord].ID), sizeof(USB_PRINTER_DEVICE_ID) );
}
else
{
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
// No printer language has been specified for this printer.
// Get the printer device ID string, so we can try to determine
// what printer languages it supports.
errorCode = USBHostIssueDeviceRequest( address, USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE,
PRINTER_DEVICE_REQUEST_GET_DEVICE_ID, 0, 0, ((USB_DEVICE_DESCRIPTOR*)pDeviceDescriptor)->bMaxPacketSize0,
(BYTE *)usbPrinters[currentPrinterRecord].deviceIDString, USB_DEVICE_REQUEST_GET,
usbPrinters[currentPrinterRecord].clientDriverID );
if (errorCode)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Cannot get device ID string length - " );
UART2PutHex( errorCode );
UART2PrintString( "\r\n" );
#endif
// If we cannot read the ID string, then we cannot determine the required printer language.
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
}
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Getting device ID string length...\r\n" );
#endif
#else
// No printer language has been specified for this printer.
// We cannot enumerate it.
#ifdef DEBUG_MODE
UART2PrintString( "PRN: No printer language specified.\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
#endif
}
return TRUE;
}
}
}
// Jump to the next descriptor in this configuration.
i += pDescriptor[i];
}
// This client driver could not initialize the device
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Device is not a printer.\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
} // USBHostPrinterInitialize
/****************************************************************************
Function:
BOOL USBHostPrinterEventHandler ( BYTE address, USB_EVENT event,
void *data, DWORD size )
Summary:
This routine is called by the Host layer to notify the general client of
events that occur.
Description:
This routine is called by the Host layer to notify the general client of
events that occur. If the event is recognized, it is handled and the
routine returns TRUE. Otherwise, it is ignored and the routine returns
FALSE.
This routine can notify the application with the following events:
* EVENT_PRINTER_ATTACH
* EVENT_PRINTER_DETACH
* EVENT_PRINTER_TX_DONE
* EVENT_PRINTER_RX_DONE
* EVENT_PRINTER_REQUEST_DONE
* EVENT_PRINTER_UNSUPPORTED
Preconditions:
None
Parameters:
BYTE address - Address of device with the event
USB_EVENT event - The bus event that occured
void *data - Pointer to event-specific data
DWORD size - Size of the event-specific data
Return Values:
TRUE - The event was handled
FALSE - The event was not handled
Remarks:
None
***************************************************************************/
BOOL USBHostPrinterEventHandler ( BYTE address, USB_EVENT event, void *data, DWORD size )
{
USB_PRINTER_QUEUE_ITEM *transfer;
#ifdef DEBUG_MODE
//UART2PrintString( "PRN: Receiving event\r\n" );
#endif
// Make sure it was for our device
if (!_USBHostPrinter_FindDevice( address ))
{
return FALSE; // The device was not found.
}
// Handle specific events.
switch (event)
{
case EVENT_DETACH:
// Purge the IN and OUT transfer queues.
while (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE );
}
while (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE );
if (transfer->flags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( transfer->data );
}
}
// Tell the printer language support that the device has been detached.
USBHostPrinterCommand( usbPrinters[currentPrinterRecord].ID.deviceAddress, USB_PRINTER_DETACHED, USB_NULL, 0, 0 );
// Notify that application that the device has been detached.
USB_HOST_APP_EVENT_HANDLER(usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_DETACH,
&usbPrinters[currentPrinterRecord].ID.deviceAddress, sizeof(BYTE) );
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
USB_FREE( usbPrinters[currentPrinterRecord].deviceIDString );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: USB Printer Client Device Detached: address=" );
UART2PutDec( address );
UART2PrintString( "\r\n" );
#endif
return TRUE;
case EVENT_TRANSFER:
#ifdef DEBUG_MODE
//UART2PrintString( "PRN: TRANSFER event occurred.\r\n" );
#endif
if ( (data != NULL) && (size == sizeof(HOST_TRANSFER_DATA)) )
{
DWORD dataCount = ((HOST_TRANSFER_DATA *)data)->dataCount;
if ( ((HOST_TRANSFER_DATA *)data)->bEndpointAddress == (USB_IN_EP | usbPrinters[currentPrinterRecord].endpointIN) )
{
usbPrinters[currentPrinterRecord].flags.rxBusy = 0;
usbPrinters[currentPrinterRecord].rxLength = dataCount;
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE );
if (transfer->flags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( transfer->data );
}
if (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
_USBHostPrinter_ReadFromQueue( usbPrinters[currentPrinterRecord].ID.deviceAddress );
}
if (transfer->flags & USB_PRINTER_TRANSFER_NOTIFY)
{
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_RX_DONE, &dataCount, sizeof(DWORD) );
}
}
else if ( ((HOST_TRANSFER_DATA *)data)->bEndpointAddress == (USB_OUT_EP | usbPrinters[currentPrinterRecord].endpointOUT) )
{
usbPrinters[currentPrinterRecord].flags.txBusy = 0;
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE );
if (transfer->flags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( transfer->data );
}
if (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
_USBHostPrinter_WriteFromQueue( usbPrinters[currentPrinterRecord].ID.deviceAddress );
}
if (transfer->flags & USB_PRINTER_TRANSFER_NOTIFY)
{
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_TX_DONE, &dataCount, sizeof(DWORD) );
}
}
else if ((((HOST_TRANSFER_DATA *)data)->bEndpointAddress & 0x7F) == 0)
{
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
if (!usbPrinters[currentPrinterRecord].flags.initialized)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Check Device ID string...\r\n" );
#endif
if (!_USBHostPrinter_GetDeviceIDString())
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Sending Unsupported Printer event...\r\n" );
#endif
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_UNSUPPORTED, NULL, 0 );
}
}
else
#endif
{
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_REQUEST_DONE, &dataCount, sizeof(DWORD) );
}
}
else
{
// Event is on an unknown endpoint.
return FALSE;
}
return TRUE;
}
else
{
// The data does not match what was expected for this event.
return FALSE;
}
break;
case EVENT_BUS_ERROR:
// A bus error occurred. Clean up the best we can.
#ifdef DEBUG_MODE
UART2PrintString( "PRN: BUS_ERROR event occurred.\r\n" );
#endif
if ( (data != NULL) && (size == sizeof(HOST_TRANSFER_DATA)) )
{
if ( ((HOST_TRANSFER_DATA *)data)->bEndpointAddress == (USB_IN_EP | usbPrinters[currentPrinterRecord].endpointIN) )
{
usbPrinters[currentPrinterRecord].flags.rxBusy = 0;
usbPrinters[currentPrinterRecord].rxLength = 0;
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE );
if (transfer->flags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( transfer->data );
}
if (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
_USBHostPrinter_ReadFromQueue( usbPrinters[currentPrinterRecord].ID.deviceAddress );
}
// if (transfer->flags & USB_PRINTER_TRANSFER_NOTIFY)
{
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_RX_ERROR, NULL, ((HOST_TRANSFER_DATA *)data)->bErrorCode );
}
}
else if ( ((HOST_TRANSFER_DATA *)data)->bEndpointAddress == (USB_OUT_EP | usbPrinters[currentPrinterRecord].endpointOUT) )
{
usbPrinters[currentPrinterRecord].flags.txBusy = 0;
transfer = StructQueueRemove( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE );
if (transfer->flags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( transfer->data );
}
if (StructQueueIsNotEmpty( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
_USBHostPrinter_WriteFromQueue( usbPrinters[currentPrinterRecord].ID.deviceAddress );
}
// if (transfer->flags & USB_PRINTER_TRANSFER_NOTIFY)
{
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_TX_ERROR, NULL, ((HOST_TRANSFER_DATA *)data)->bErrorCode );
}
}
else if ((((HOST_TRANSFER_DATA *)data)->bEndpointAddress & 0x7F) == 0)
{
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
if (!usbPrinters[currentPrinterRecord].flags.initialized &&
!usbPrinters[currentPrinterRecord].flags.deviceIDStringLengthValid)
{
// The printer does not support the GET_DEVICE_ID printer class request.
// We cannot determine a printer language for this printer.
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Required specific printer language not found\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_UNSUPPORTED, NULL, 0 );
}
else
#endif
{
// The printer gave an error during an application control transfer.
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_REQUEST_ERROR, NULL, ((HOST_TRANSFER_DATA *)data)->bErrorCode );
}
}
else
{
// Event is on an unknown endpoint.
return FALSE;
}
return TRUE;
}
else
{
// The data does not match what was expected for this event.
return FALSE;
}
break;
case EVENT_SUSPEND:
case EVENT_RESUME:
default:
break;
}
return FALSE;
} // USBHostPrinterEventHandler
// *****************************************************************************
// *****************************************************************************
// Section: Application Callable Functions
// *****************************************************************************
// *****************************************************************************
/****************************************************************************
Function:
BYTE USBHostPrinterCommand( BYTE deviceAddress, USB_PRINTER_COMMAND command,
USB_DATA_POINTER data, DWORD size, BYTE flags )
Summary:
This is the primary user interface function for the printer client
driver. It is used to issue all printer commands.
Description:
This is the primary user interface function for the printer client
driver. It is used to issue all printer commands. Each generic printer
command is translated to the appropriate command for the supported
language and is enqueued for transfer to the printer. Before calling
this routine, it is recommended to call the function
USBHostPrinterCommandReady() to determine if there is space available
in the printer's output queue.
Preconditions:
None
Parameters:
BYTE address - Device's address on the bus
USB_PRINTER_COMMAND command - Command to execute. See the enumeration
USB_PRINTER_COMMAND for the list of
valid commands and their requirements.
USB_DATA_POINTER data - Pointer to the required data. Note that
the caller must set transferFlags
appropriately to indicate if the pointer is
a RAM pointer or a ROM pointer.
DWORD size - Size of the data. For some commands, this
parameter is used to hold the data itself.
BYTE transferFlags - Flags that indicate details about the
transfer operation. Refer to these flags
* USB_PRINTER_TRANSFER_COPY_DATA
* USB_PRINTER_TRANSFER_STATIC_DATA
* USB_PRINTER_TRANSFER_NOTIFY
* USB_PRINTER_TRANSFER_FROM_ROM
* USB_PRINTER_TRANSFER_FROM_RAM
Returns:
See the USB_PRINTER_ERRORS enumeration. Also, refer to the printer
language command handler function, such as
USBHostPrinterLanguagePostScript().
Example:
<code>
if (USBHostPrinterCommandReady( address ))
{
USBHostPrinterCommand( address, USB_PRINTER_JOB_START, NULL, 0, 0 );
}
</code>
Remarks:
Use the definitions USB_DATA_POINTER_RAM() and USB_DATA_POINTER_ROM()
to cast data pointers. For example:
<code>
USBHostPrinterCommand( address, USB_PRINTER_TEXT, USB_DATA_POINTER_RAM(buffer), strlen(buffer), 0 );
</code>
When developing new commands, keep in mind that the function
USBHostPrinterCommandReady() will often be used before calling this
function to see if there is space available in the output transfer queue.
USBHostPrinterCommandReady() will routine TRUE if a single space is
available in the output queue. Therefore, each command can generate only
one output transfer.
***************************************************************************/
BYTE USBHostPrinterCommand( BYTE deviceAddress, USB_PRINTER_COMMAND command,
USB_DATA_POINTER data, DWORD size, BYTE flags )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
// The device was not found.
return USB_PRINTER_UNKNOWN_DEVICE;
}
return usbPrinters[currentPrinterRecord].languageHandler( deviceAddress, command, data, size, flags );
}
/****************************************************************************
Function:
BOOL USBHostPrinterCommandReady( BYTE deviceAddress )
Description:
This interface is used to check if the client driver has space available
to enqueue another transfer.
Preconditions:
None
Parameters:
deviceAddress - USB Address of the device
Return Values:
TRUE - The printer client driver has room for at least one more
transfer request, or the device is not attached. The latter
allows this routine to be called without generating an
infinite loop if the device detaches.
FALSE - The transfer queue is full.
Example:
<code>
if (USBHostPrinterCommandReady( address ))
{
USBHostPrinterCommand( address, USB_PRINTER_JOB_START, NULL, 0, 0 );
}
</code>
Remarks:
This routine will return TRUE if a single transfer can be enqueued. Since
this routine is the check to see if USBHostPrinterCommand() can be called,
every command can generate at most one transfer.
***************************************************************************/
BOOL USBHostPrinterCommandReady( BYTE deviceAddress )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
// The device was not found.
return TRUE;
}
if (StructQueueSpaceAvailable( 1, &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
return TRUE;
}
return FALSE;
}
/****************************************************************************
Function:
BOOL USBHostPrinterDeviceDetached( BYTE deviceAddress )
Description:
This interface is used to check if the device has been detached from the
bus.
Preconditions:
None
Parameters:
BYTE deviceAddress - USB Address of the device.
Return Values:
TRUE - The device has been detached, or an invalid deviceAddress is given.
FALSE - The device is attached
Example:
<code>
if (USBHostPrinterDeviceDetached( deviceAddress ))
{
// Handle detach
}
</code>
Remarks:
The event EVENT_PRINTER_DETACH can also be used to detect a detach.
***************************************************************************/
BOOL USBHostPrinterDeviceDetached( BYTE deviceAddress )
{
if (_USBHostPrinter_FindDevice( deviceAddress ))
{
return FALSE;
}
return TRUE;
}
/****************************************************************************
Function:
DWORD USBHostPrinterGetRxLength( BYTE deviceAddress )
Description:
This function retrieves the number of bytes copied to user's buffer by
the most recent call to the USBHostPrinterRead() function.
Preconditions:
The device must be connected and enumerated.
Parameters:
BYTE deviceAddress - USB Address of the device
Returns:
Returns the number of bytes most recently received from the Generic
device with address deviceAddress.
Remarks:
None
***************************************************************************/
DWORD USBHostPrinterGetRxLength( BYTE deviceAddress )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
return 0;
}
return usbPrinters[currentPrinterRecord].rxLength;
}
/****************************************************************************
Function:
BYTE USBHostPrinterGetStatus( BYTE deviceAddress, BYTE *status )
Summary:
This function issues the Printer class-specific Device Request to
obtain the printer status.
Description:
This function issues the Printer class-specific Device Request to
obtain the printer status. The returned status should have the following
format, per the USB specification. Any deviation will be due to the
specific printer implementation.
* Bit 5 - Paper Empty; 1 = paper empty, 0 = paper not empty
* Bit 4 - Select; 1 = selected, 0 = not selected
* Bit 3 - Not Error; 1 = no error, 0 = error
* All other bits are reserved.
The *status parameter is not updated until the EVENT_PRINTER_REQUEST_DONE
event is thrown. Until that point the value of *status is unknown.
The *status parameter will only be updated if this function returns
USB_SUCCESS. If this function returns with any other error code then the
EVENT_PRINTER_REQUEST_DONE event will not be thrown and the status field
will not be updated.
Preconditions:
The device must be connected and enumerated.
Parameters:
deviceAddress - USB Address of the device
*status - pointer to the returned status byte
Returns:
See the return values for the USBHostIssueDeviceRequest() function.
Remarks:
None
***************************************************************************/
BYTE USBHostPrinterGetStatus( BYTE deviceAddress, BYTE *status )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
return USB_UNKNOWN_DEVICE;
}
return USBHostIssueDeviceRequest( deviceAddress,
USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE,
PRINTER_DEVICE_REQUEST_GET_PORT_STATUS,
0, 0x0000, 1,
status, USB_DEVICE_REQUEST_GET, usbPrinters[currentPrinterRecord].clientDriverID );
}
/****************************************************************************
Function:
void USBHostPrinterRead( BYTE deviceAddress, BYTE *buffer, DWORD length,
BYTE transferFlags )
Description:
Use this routine to receive from the device and store it into memory.
Preconditions:
The device must be connected and enumerated.
Parameters:
deviceAddress - USB Address of the device.
buffer - Pointer to the data buffer
length - Number of bytes to be transferred
transferFlags - Flags for how to perform the operation
Return Values:
USB_SUCCESS - The Read was started successfully
(USB error code) - The Read was not started. See USBHostRead() for
a list of errors.
Example:
<code>
if (!USBHostPrinterRxIsBusy( deviceAddress ))
{
USBHostPrinterRead( deviceAddress, &buffer, sizeof(buffer), 0 );
}
</code>
Remarks:
None
***************************************************************************/
BYTE USBHostPrinterRead( BYTE deviceAddress, void *buffer, DWORD length,
BYTE transferFlags )
{
USB_PRINTER_QUEUE_ITEM *transfer;
// Validate the call
if (!_USBHostPrinter_FindDevice( deviceAddress ) ||
!usbPrinters[currentPrinterRecord].flags.initialized)
{
return USB_INVALID_STATE;
}
// Check transfer path
if (StructQueueIsFull( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
return USB_PRINTER_BUSY;
}
// Add the newest transfer to the queue
transfer = StructQueueAdd( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE);
transfer->data = buffer;
transfer->size = length;
transfer->flags = transferFlags;
if (usbPrinters[currentPrinterRecord].flags.rxBusy)
{
// The request has been queued. We'll execute it when the current transfer is complete.
return USB_SUCCESS;
}
else
{
return _USBHostPrinter_ReadFromQueue( deviceAddress );
}
} // USBHostPrinterRead
/****************************************************************************
Function:
BYTE USBHostPrinterReset( BYTE deviceAddress )
Description:
This function issues the Printer class-specific Device Request to
perform a soft reset.
Preconditions:
The device must be connected and enumerated.
Parameters:
BYTE deviceAddress - USB Address of the device
Returns:
See the return values for the USBHostIssueDeviceRequest() function.
Remarks:
Not all printers support this command.
***************************************************************************/
BYTE USBHostPrinterReset( BYTE deviceAddress )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
return USB_UNKNOWN_DEVICE;
}
return USBHostIssueDeviceRequest( deviceAddress,
USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE,
PRINTER_DEVICE_REQUEST_SOFT_RESET,
0, 0x0000, 0,
NULL, USB_DEVICE_REQUEST_SET, usbPrinters[currentPrinterRecord].clientDriverID );
}
/****************************************************************************
Function:
BOOL USBHostPrinterRxIsBusy( BYTE deviceAddress )
Summary:
This interface is used to check if the client driver is currently busy
receiving data from the device.
Description:
This interface is used to check if the client driver is currently busy
receiving data from the device. This function is intended for use with
transfer events. With polling, the function USBHostPrinterRxIsComplete()
should be used.
Preconditions:
None
Parameters:
deviceAddress - USB Address of the device
Return Values:
TRUE - The device is receiving data or an invalid deviceAddress is
given.
FALSE - The device is not receiving data
Example:
<code>
if (!USBHostPrinterRxIsBusy( deviceAddress ))
{
USBHostPrinterRead( deviceAddress, &Buffer, sizeof( Buffer ) );
}
</code>
Remarks:
None
***************************************************************************/
BOOL USBHostPrinterRxIsBusy( BYTE deviceAddress )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
// The device was not found.
return TRUE;
}
if (usbPrinters[currentPrinterRecord].flags.rxBusy)
{
return TRUE;
}
return FALSE;
}
/****************************************************************************
Function:
BYTE USBHostPrinterWrite( BYTE deviceAddress, void *buffer, DWORD length,
BYTE transferFlags )
Description:
Use this routine to transmit data from memory to the device. This
routine will not usually be called by the application directly. The
application will use the USBHostPrinterCommand() function, which will
call the appropriate printer language support function, which will
utilize this routine.
Preconditions:
The device must be connected and enumerated.
Parameters:
BYTE deviceAddress - USB Address of the device.
void *buffer - Pointer to the data buffer
DWORD length - Number of bytes to be transferred
BYTE transferFlags - Flags for how to perform the operation
Return Values:
USB_SUCCESS - The Write was started successfully.
USB_PRINTER_UNKNOWN_DEVICE - Device not found or has not been initialized.
USB_PRINTER_BUSY - The printer's output queue is full.
(USB error code) - The Write was not started. See USBHostWrite() for
a list of errors.
Remarks:
None
***************************************************************************/
BYTE USBHostPrinterWrite( BYTE deviceAddress, void *buffer, DWORD length,
BYTE transferFlags)
{
USB_PRINTER_QUEUE_ITEM *transfer;
// Validate the call
if (!_USBHostPrinter_FindDevice( deviceAddress ) ||
!usbPrinters[currentPrinterRecord].flags.initialized)
{
return USB_PRINTER_UNKNOWN_DEVICE;
}
// Check transfer path
if (StructQueueIsFull( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE ))
{
// The queue is full. If the caller was using heap space for the data,
// deallocate the memory.
if (transferFlags & USB_PRINTER_TRANSFER_COPY_DATA)
{
USB_FREE( buffer );
}
return USB_PRINTER_BUSY;
}
// Add the newest transfer to the queue
transfer = StructQueueAdd( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE );
transfer->data = buffer;
transfer->size = length;
transfer->flags = transferFlags;
if (usbPrinters[currentPrinterRecord].flags.txBusy)
{
// The request has been queued. We'll execute it when the current transfer is complete.
return USB_SUCCESS;
}
else
{
// We can send the request now. We still have to put it in the
// queue, because that's where the event handler gets its information.
return _USBHostPrinter_WriteFromQueue( deviceAddress );
}
}
/****************************************************************************
Function:
BOOL USBHostPrinterWriteComplete( BYTE deviceAddress )
Description:
This interface is used to check if the client driver is currently
transmitting data to the printer, or if it is between transfers but still
has transfers queued.
Preconditions:
None
Parameters:
deviceAddress - USB Address of the device
Return Values:
TRUE - The device is done transmitting data or an invalid deviceAddress
is given.
FALSE - The device is transmitting data or has a transfer in the queue.
Remarks:
None
***************************************************************************/
BOOL USBHostPrinterWriteComplete( BYTE deviceAddress )
{
if (!_USBHostPrinter_FindDevice( deviceAddress ))
{
// The device was not found.
return TRUE;
}
if (usbPrinters[currentPrinterRecord].flags.txBusy ||
!(StructQueueIsEmpty( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE )))
{
return FALSE;
}
return TRUE;
}
// *****************************************************************************
// *****************************************************************************
// Section: Internal Functions
// *****************************************************************************
// *****************************************************************************
/****************************************************************************
Function:
BOOL _USBHostPrinter_FindDevice( BYTE address )
Description:
This function looks in the array of printers to see if there is currently
a printer attached at the indicated device. If so, it returns TRUE, and
the global variable currentPrinterRecord is changed to the index of the
printer. If not, it returns FALSE. Note that the function implies
nothing about the status of the printer; it may not be ready to use.
Preconditions:
None
Parameters:
deviceAddress - USB Address of the device.
Return Values:
TRUE - The indicated device is attached
FALSE - The indicated device is not attached
Remarks:
None
***************************************************************************/
BOOL _USBHostPrinter_FindDevice( BYTE address )
{
for (currentPrinterRecord=0; currentPrinterRecord<USB_MAX_PRINTER_DEVICES; currentPrinterRecord++)
{
if (usbPrinters[currentPrinterRecord].flags.inUse &&
(usbPrinters[currentPrinterRecord].ID.deviceAddress == address))
{
return TRUE;
}
}
return FALSE; // The device was not found.
}
/****************************************************************************
Function:
BOOL _USBHostPrinter_GetDeviceIDString( void )
Description:
This routine performs most of the process required to get the device ID
string. The initial request to get the length is performed in the
initialization handler. This routine handles the processing of the
length, the request for the entire string, and processing of the string.
The format of this string is specified by IEEE 1284.
Preconditions:
None
Parameters:
None - None
Return Values:
TRUE - Processing is proceeding normally
FALSE - Cannot read the device ID string, or cannot find a printer
language to use to communicate with the printer.
Remarks:
This function is available only if USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
has been defined.
***************************************************************************/
#ifdef USB_PRINTER_ALLOW_DYNAMIC_LANGUAGE_DETERMINATION
BOOL _USBHostPrinter_GetDeviceIDString( void )
{
BYTE errorCode;
if (!usbPrinters[currentPrinterRecord].flags.deviceIDStringLengthValid)
{
// This transfer was to get the length of the string.
// The length is a 2-byte value, MSB first.
usbPrinters[currentPrinterRecord].deviceIDStringLength = ( (BYTE)usbPrinters[currentPrinterRecord].deviceIDString[0] << 8) + (BYTE)usbPrinters[currentPrinterRecord].deviceIDString[1];
usbPrinters[currentPrinterRecord].flags.deviceIDStringLengthValid = 1;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Got device ID string length: " );
UART2PutHex( usbPrinters[currentPrinterRecord].deviceIDStringLength >> 8);
UART2PutHex( usbPrinters[currentPrinterRecord].deviceIDStringLength );
UART2PrintString( "\r\n" );
#endif
USB_FREE( usbPrinters[currentPrinterRecord].deviceIDString );
usbPrinters[currentPrinterRecord].deviceIDString = (char *)USB_MALLOC( usbPrinters[currentPrinterRecord].deviceIDStringLength + 3 );
if (usbPrinters[currentPrinterRecord].deviceIDString == NULL)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Out of memory for the device ID string.\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
}
else
{
// Get the full string
errorCode = USBHostIssueDeviceRequest( usbPrinters[currentPrinterRecord].ID.deviceAddress,
USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE,
PRINTER_DEVICE_REQUEST_GET_DEVICE_ID, 0, 0, usbPrinters[currentPrinterRecord].deviceIDStringLength,
(BYTE *)usbPrinters[currentPrinterRecord].deviceIDString, USB_DEVICE_REQUEST_GET,
usbPrinters[currentPrinterRecord].clientDriverID );
if (errorCode)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Cannot get device ID string - " );
UART2PutHex( errorCode );
UART2PrintString( "\r\n" );
#endif
// If we cannot read the ID string, then we cannot determine the required printer language.
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
}
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Getting device ID string ...\r\n" );
#endif
}
}
else
{
char *commandSet;
WORD i;
WORD semicolonLocation;
// Null terminate the device ID string so we can do string manipulation on it.
usbPrinters[currentPrinterRecord].deviceIDString[usbPrinters[currentPrinterRecord].deviceIDStringLength + 2] = 0;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Got device ID string: (Length " );
UART2PutHex( usbPrinters[currentPrinterRecord].deviceIDString[0] );
UART2PutHex( usbPrinters[currentPrinterRecord].deviceIDString[1] );
UART2PrintString( ")\r\n" );
for (i=2; i<usbPrinters[currentPrinterRecord].deviceIDStringLength; i++)
{
UART2PutChar( usbPrinters[currentPrinterRecord].deviceIDString[i] );
}
UART2PrintString( "\r\n" );
#endif
// Determine if one of the languages we are currently using can support this
// printer. Languages should be listed in ascending order of preference
// in the usbPrinterClientLanguages array, so we can stop looking as soon
// as we find a match.
commandSet = strstr( &(usbPrinters[currentPrinterRecord].deviceIDString[2]), "COMMAND SET:" );
if (!commandSet)
{
commandSet = strstr( &(usbPrinters[currentPrinterRecord].deviceIDString[2]), "CMD:" );
}
if (!commandSet)
{
// No printer language support is indicated.
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Device ID string did not contain command set\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
}
// Replace the semicolon at the end of the COMMAND SET: specification with a
// null, so we don't get a false positive based on some other portion of the
// device ID string. If we don't find a semicolon, set the location to 0.
for (semicolonLocation = 0;
(commandSet[semicolonLocation] != 0) && (commandSet[semicolonLocation] != ';');
semicolonLocation ++ ) {};
if (commandSet[semicolonLocation] == 0)
{
semicolonLocation = 0;
}
else
{
commandSet[semicolonLocation] = 0;
}
// Convert the command set to all upper case.
for (i=0; i<semicolonLocation; i++)
{
commandSet[i] = toupper( commandSet[i] );
}
// Look for a supported printer language in the array of available languages.
i = 0;
usbPrinters[currentPrinterRecord].languageHandler = NULL;
while ((usbPrinterClientLanguages[i].isLanguageSupported != NULL) &&
(usbPrinters[currentPrinterRecord].languageHandler == NULL))
{
if (usbPrinterClientLanguages[i].isLanguageSupported( commandSet, &(usbPrinters[currentPrinterRecord].ID.support) ))
{
usbPrinters[currentPrinterRecord].languageHandler = usbPrinterClientLanguages[i].languageCommandHandler;
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Printer language support: " );
UART2PutHex( usbPrinters[currentPrinterRecord].ID.support.val );
UART2PrintString( "\r\n" );
#endif
}
i ++;
}
// Restore the device ID string to its original state.
if (semicolonLocation != 0)
{
commandSet[semicolonLocation] = ';';
}
// See if we were able to find a printer language.
if (usbPrinters[currentPrinterRecord].languageHandler == NULL)
{
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Required printer language not found\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.value = 0;
return FALSE;
}
// We have a printer language that we can use with this printer.
#ifdef DEBUG_MODE
UART2PrintString( "PRN: Required printer language found\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.initialized = 1;
USBHostPrinterCommand( usbPrinters[currentPrinterRecord].ID.deviceAddress, USB_PRINTER_ATTACHED,
USB_DATA_POINTER_RAM(&(usbPrinters[currentPrinterRecord].ID.support)), sizeof(USB_PRINTER_FUNCTION_SUPPORT), 0 );
// Tell the application layer that we have a device.
USB_HOST_APP_EVENT_HANDLER( usbPrinters[currentPrinterRecord].ID.deviceAddress, EVENT_PRINTER_ATTACH,
&(usbPrinters[currentPrinterRecord].ID), sizeof(USB_PRINTER_DEVICE_ID) );
}
return TRUE;
}
#endif
/****************************************************************************
Function:
void _USBHostPrinter_ReadFromQueue( BYTE deviceAddress )
Description:
This routine initiates the IN transfer described by the first entry
in the queue.
Preconditions:
* The receive path must be clear before calling this routine.
* The queue must contain at least one valid entry.
* currentPrinterRecord must be valid.
Parameters:
deviceAddress - USB Address of the device.
Return Values:
USB_SUCCESS - The Read was started successfully
(USB error code) - The Read was not started. See USBHostRead() for
a list of errors.
Remarks:
None
***************************************************************************/
BYTE _USBHostPrinter_ReadFromQueue( BYTE deviceAddress )
{
BYTE returnValue;
USB_PRINTER_QUEUE_ITEM *transfer;
transfer = StructQueuePeekTail( &(usbPrinters[currentPrinterRecord].transferQueueIN), USB_PRINTER_TRANSFER_QUEUE_SIZE );
usbPrinters[currentPrinterRecord].flags.rxBusy = 1;
usbPrinters[currentPrinterRecord].rxLength = 0;
returnValue = USBHostRead( deviceAddress, USB_IN_EP|usbPrinters[currentPrinterRecord].endpointIN, (BYTE *)(transfer->data), transfer->size );
if (returnValue != USB_SUCCESS)
{
usbPrinters[currentPrinterRecord].flags.rxBusy = 0; // Clear flag to allow re-try
}
return returnValue;
}
/****************************************************************************
Function:
void _USBHostPrinter_WriteFromQueue( BYTE deviceAddress )
Description:
This routine initiates the OUT transfer described by the first entry
in the queue.
Preconditions:
* The transmit path must be clear before calling this routine.
* The queue must contain at least one valid entry.
* currentPrinterRecord must be valid.
Parameters:
deviceAddress - USB Address of the device.
Return Values:
USB_SUCCESS - The Read was started successfully
(USB error code) - The Read was not started. See USBHostWrite() for
a list of errors.
Remarks:
None
***************************************************************************/
BYTE _USBHostPrinter_WriteFromQueue( BYTE deviceAddress )
{
BYTE returnValue;
USB_PRINTER_QUEUE_ITEM *transfer;
#if defined( DEBUG_MODE ) || defined( DEBUG_PRINT_COMMANDS )
//BYTE i;
#endif
transfer = StructQueuePeekTail( &(usbPrinters[currentPrinterRecord].transferQueueOUT), USB_PRINTER_TRANSFER_QUEUE_SIZE );
#if defined( DEBUG_MODE )
//UART2PrintString( "PRN: OUT, " );
#endif
#if defined( DEBUG_MODE ) || defined( DEBUG_PRINT_COMMANDS )
//for (i=0; i<transfer->size; i++) UART2PutChar(transfer->data[i]);
//UART2PutHex( transfer->size >> 8 );
//UART2PutHex( transfer->size );
//UART2PrintString( "\r\n" );
#endif
usbPrinters[currentPrinterRecord].flags.txBusy = 1;
returnValue = USBHostWrite( deviceAddress, USB_OUT_EP|usbPrinters[currentPrinterRecord].endpointOUT, (BYTE *)(transfer->data), transfer->size );
if (returnValue != USB_SUCCESS)
{
usbPrinters[currentPrinterRecord].flags.txBusy = 0; // Clear flag to allow re-try
}
return returnValue;
}
/*************************************************************************
* EOF usb_client_generic.c
*/
|