text:
/******************************************************************************

  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 Company's 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.
********************************************************************/

//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
 */

redpajama_set_name: RedPajamaGithub


text:
UniBE
Faculty of
Faculties & Institutes
Faculty of Humanities
Menu öffnen
Departments & Institutes
Department of History and Archeology
Institute of Archaeological Sciences
Department of Art and Cultural Sciences
Institute for the Study of the Middle East and Muslim Societies
The Institute of Musicology
Institute for the Science of Religion
Institute of Social Anthropology
Department of Linguistics and Literary Studies
Institute of French Language and Literature
Institute of Germanic Languages and Literatures
Institute of Italian Language and Literature
Institute of Classical Philology
Institute of Slavic Languages and Literatures
Department of Spanish Language and Literature
Walter Benjamin Kolleg
Center for Global Studies (CGS)
Center for the Study of Language and Society (CSLS)
Interdisciplinary Postgraduate Research Network (IPN)
Graduate School of the Arts and Humanities (GSAH)
Interdisciplinary Unities
Interdisciplinary Centre for Gender Studies
Interdisciplinary Research Program in Jewish Studies
Eastern European Studies Bern-Fribourg
Diversity Initiative 2020/21
Starting in Spring 2020, the Faculty of Humanities' Gleichstellungskommission is looking to fund a series of one-off projects or events to help enhance the culture of diversity in our faculty.
In keeping with the UL's plans to address "diversity" at university- and faculty-level, our Diversity Initiative 20/21 is designed to address diversity in its broadest, most inclusive terms. In this regard, we invite members of the Faculty of Humanities' student body, junior Mittelbau, senior Mittelbau and professoriate to submit their proposals for innovative, maximum-reach projects or events. These can be funded up to a maximum of CHF 5,000 each, although funding will be determined by the number and quality of proposals received. Projects and events can be scheduled for any time during 2020 or 2021.
The commission will start considering proposals in two rounds during the Spring semester 2020. Please submit your ideas no later than 17:00 on Friday 16 October using the short form (see link) and submitting it to equality@histdek.unibe.ch. Please address any questions or comments to this same email address.
Up to now, the Equal opportunities committee supported 6 applications for the Diversity Initiaive with 17'700 Swiss Francs.
Academic Mentorship for Black and Asian Women (AMBAW)
Confronting Hostile Terrains
Project open lectures
Queering Podcasts
I mondi di Alice 1'000
EthnoKino
Stateless 1'000
Diversity Initiative form (WORD, 1 page, 27KB)
Weitere Informationen über diesen Webauftritt
Site-Suche
© 2022 University of Bern
University of Bern
Hochschulstrasse 6

redpajama_set_name: RedPajamaCommonCrawl


text:
Q: Finding the overhead and distance of an unknown code based on message making algorithm for an information word M with m bits that is coded as following:

*

*M is coded into a word A using an unknown code that allows detection of not more than one error.

*the code word is the word obtained by a concatenation of A to itself. so A becomes AA and etc..

1)is it possible to know the overhead in the given code?
2)is it possible to know the distance of the code?
my attempt:
after thinking on it again and rereading the text, i began to realize(hopefully i am right now) that the question is about parity bit and not hamming. so if it can detect not more than one error, there's a parity bit, i think. based on that thought i tried to solve it:
1)I began to realize it's not about hamming code. if we can only detect not more than one error, then it's about the parity bit. so i think that if we add a bit to each byte of message, then the overhead will be 9 bytes total. so it can be calculated, i think.
2)since this sub problem is about the concatenation of A to itself, then like @YuvalFilmus said, the minimal distance should be twice now.
is it correct now? would really appreciate your comments and help.
thank you very much

A: The question is still pretty unclear, but I will provide a few hints and hope that they point you in the right direction.
The only code I'm aware of that can detect only one error and do nothing else is the repetition code \$R_2\$. This code takes a data bit \$b\$ and produces the codeword \$bb\$.
(Note that adding a parity bit can do more than detecting one error; it can detect any odd number of errors).
Now, by producing a code word that is a concatenation of \$A\$ with itself, all you're doing is using the same \$R_2\$ code again.
In summary, the mistery code is \$R_2\$, and the second code (which duplicates the word \$A\$) is just \$R_2\$ again.
The concatenation of two \$R_2\$ codes is equivalent to an \$R_4\$, where each bit is repeated four times; in other words, if the input is \$b\$, the output is \$bbbb\$. This code has distance \$4\$, can correct one error, and it can detect up to three errors.
Its code rate is \$1/4\$; it transmits four code bits per every data bit. The overhead then is \$3/4\$, or \$75\%\$.

redpajama_set_name: RedPajamaStackExchange


To make it easier to see whether a setting is on or off, you can have iPhone show an additional label on on/off switches.
Add switch setting labels. Go to Settings > General > Accessibility and turn on On/Off Labels.


text:
Johanna Alexandra "Lotte" Jacobi (geboren am 17. August 1896 in Thorn, Westpreußen; gestorben am 6. Mai 1990 in Concord, New Hampshire) war eine deutsche Fotografin auf den Gebieten des Porträts, des Theaters und der Kunst.

Leben und Werk
Jacobi entstammte in der vierten Generation einer jüdischen Fotografenfamilie und wuchs ab dem zweiten Lebensjahr in Posen auf; 1921 ging die Familie nach Berlin. Nach der Ausbildung – zunächst bei ihrem Vater – studierte Lotte Jacobi von 1925 bis 1927 in München Film und Fotografie. Danach übernahm sie das väterliche Studio in Berlin. Bereits zu dieser Zeit war sie bekannt als Fotografin der Künstler und der Kunst. Ihre Bilder erschienen in Magazinen wie der Berliner Illustrirten Zeitung oder in der Münchner Illustrierten Presse. Ende der 1920er Jahre machte Lotte Jacobi die Bekanntschaft mit der italienischen Fotografin Tina Modotti. Im September 1930 stellte Modotti ihre Fotografien in Jacobis Galerie aus.

Als das nationalsozialistische Regime ab 1933 der Jüdin Lotte Jacobi die Arbeit nahezu unmöglich machte, arbeitete sie zunächst unter verschiedenen Pseudonymen, begünstigt auch durch die Agentur Schostal. Jacobi sah sich jedoch letztlich zur Emigration gezwungen. Ihr Atelier am Berliner Kurfürstendamm 35, das sie zusammen mit ihrer Schwester Ruth Jacobi führte, wurde von dem Fotografen Hein Gorny übernommen. Sie emigrierte 1935 mit ihrem Sohn aus der zwischenzeitlich geschiedenen Ehe nach New York. Hier heiratete sie 1940 den Berliner Verleger Erich Reiß († 1951), der 1939 über Schweden nach New York emigriert war. Bis 1955 lebte sie in der Stadt und porträtierte amerikanische und emigrierte europäische Intellektuelle, Schriftsteller und Künstler.

Nachdem Lotte Jacobi New York verlassen hatte, ließ sie sich im ländlichen Deering in New Hampshire nieder, wo sie wieder ein Studio eröffnete und auch Arbeiten junger Künstler ausstellte. Ihr Nachlass umfasst 47.000 Negative.

Unter anderem hat sie folgende Persönlichkeiten porträtiert:
Berenice Abbott, W. H. Auden, Claire Bauroff, Martin Buber, Marc Chagall, W.E.B. Du Bois, Albert Einstein, Robert Frost, Heinrich George, Valeska Gert, Emil Jannings, Egon Erwin Kisch, Käthe Kollwitz, Pauline Koner, Lotte Lenya, Peter Lorre, Thomas Mann, Max Schreck, Max Planck, Paul Robeson, Eleanor Roosevelt, May Sarton, J.D. Salinger, Edward Steichen, Alfred Stieglitz, Grete Sultan, Kurt Weill, Chaim Weizmann.

Zu ihren charakteristischen Arbeiten gehören aber auch die in den 1940er und 1950er Jahren entstandenen, ohne Kamera geschaffenen, experimentellen Foto-Grafiken, später von Leo Katz als "photogenics" bezeichneten Werke.

1983 wurde Lotte Jacobi mit dem Dr.-Erich-Salomon-Preis der Deutschen Gesellschaft für Photographie für ihr Lebenswerk ausgezeichnet.

Ausstellungen  (Auswahl)
 1973 Einzelausstellung, Museum Folkwang, Essen
 1990 Einzelausstellung, Museum Folkwang, Essen
 1997 Einzelausstellung, Atelier Lotte Jacobi Berlin, New York., Das Verborgene Museum, Berlin
 2012 Lotte Jacobi – Photographien. – Köln Käthe-Kollwitz-Museum, (14. September 2012 – 6. Januar 2013)
 2013 Künstlerinnen im Dialog, Das Verborgene Museum, Berlin
 2014 Künstlerinnen im Dialog, Das Verborgene Museum, Berlin

Literatur
 Marion Beckers, Elisabeth Moortgat: Lotte Jacobi. Rußland 1932/33 – Moskau, Tadschikistan, Usbekistan. Berlin: Nishen, 1988. ISBN 3-88940-614-9
 Jutta Dick, Marina Sassenberg (Hrsg.): Jüdische Frauen im 19. und 20. Jahrhundert. Lexikon zu Leben und Werk. Reinbek bei Hamburg: Rowohlt, 1993. ISBN 3-499-16344-6
 Marion Beckers, Elisabeth Moortgat: Atelier Lotte Jacobi Berlin, New York. Berlin: Nicolai, 1997. ISBN 3-87584-634-6
 Hannelore Fischer (Hrsg.): Lotte Jacobi Photographien. mit einem Beitrag von Marion Beckers und Elisabeth Moortgat. Köln: Wienand, 2012. ISBN 978-3-86832-132-6
 Petra Gördüren, Dirk Luckow (Hrsg.): Dopplereffekt. Bilder in Kunst und Wissenschaft. Kunsthalle zu Kiel 31. Januar bis 2. Mai 2010. DuMont, Kiel 2010, ISBN 978-3-8321-9295-2
 Lotte Jacobi: Theater & Dance Photographs. Mit einem Vorwort von Cornell Capa. The Countryman Press, Woodstock 1982, ISBN 978-0914378938

Weblinks
 Guide to the Lotte Jacobi Papers, University of New Hampshire, (englisch)
 Annette Bußmann: Lotte Jacobi. Biografie, Literatur & Quellen zu Lotte Jacobi, FemBio des Instituts für Frauen-Biographieforschung
 Weitere Ausstellungen zu Lotte Jacobi, www.kunstaspekte.de
 Literatur von und über Lotte Jacobi in der Bibliothek des Jüdischen Museums Berlin
 Archiveintrag bei METROMOD von Helene Roth

Einzelnachweise

Fotograf (20. Jahrhundert)
Fotograf (Berlin)
Fotograf (New York City)
Emigrant aus dem Deutschen Reich zur Zeit des Nationalsozialismus
Deutscher
US-Amerikaner
Geboren 1896
Gestorben 1990
Frau

redpajama_set_name: RedPajamaWikipedia


Finland holiday cottage. Offering fully insulated and heated glass igloos, Golden Crown - Levin Iglut provides a peaceful retreat atop Utsuvaara Fell, in Finnish Lapland. Levi Ski Resort is 9 km away. Parking is free. Golden Crown's igloos include their a private bathroom with shower and hairdryer. Electronically adjustable beds and mountain views are also featured. Shared BBQ facilities are found in a wooden tipi hut, which has an open fireplace. There is also a seasonal restaurant open from September until April. Kittilä Airport is 17 km from the igloos.


Here's the second part of the March timeline (first part here), covering the main cyber attacks occurred between 16 and 31 March 2017.
Let's start from the mega breaches: unfortunately, but we should be used to it, the damage report of this fortnight has confirmed the trend we have been experiencing in the last months. The list includes: some cryptocurrency forums (approximately 12 million accounts compromised), the Illinois Department of Employment Security (1.4 million records compromised), and online forum called Dueling Network (6.5 million email addresses and hashed passwords).
Despite the impact is considerably smaller, the list of the victims also includes McDonald's Canada, whose career website has been hacked, compromising the personal data of around 95,000 restaurant job applicants.
The list of Cyber Espionage includes: El Machete, a massive cyber espionage campaign targeting high-profile international government organizations across the globe, a campaign targeting GitHub users, and a new attack against the German Parliament.


Convertant: third person plural present active subjunctive of convert: they who turn things upside down, they who turn things over, those that direct, they who turn things back or recoil.
We are an interesting collection of social and computer scientists that aggregate marketing trends, customer behaviors and data to increase and replicate the purchasing journeys of the most cost effective customers of our clients. We take what we've learned from our experiences at companies like IBM, Microsoft, Otter, and others and we bring those capabilities to you.
We have the experience of managing $100 million advertising budgets, running multiple companies and consulting with tens of thousands of small and medium sized businesses. Having introduced dozens of new products and services we have learned to realize the utmost importance of new and recurring revenue along with the ability to effectively communicate your proper value proposition.
We are first and foremost Cybrianians…the collectors of data. Traditionally trained in the dark arts of Internet Marketing and sales conversion we have integrated a multi-discipline approach to solving an age-old problem…how do you attract and talk to people that want to buy something. We have the world's largest Customer Purchase Behavior database available. We know "why" customers "buy".


If you're in need of a new coffee table, there are several ways to build one yourself. If you have time, you can build a sturdy and fashionable coffee table from scratch. Alternatively, you can even quickly make a coffee table by repurposing existing pieces. All of the projects only require a few tools and basic materials, all available at local hardware or second-hand stores.
Cut boards for the top. Use a table saw or hand saw to cut boards to the length you would like your coffee table to be. 32 inches (81 cm) is a good length to try, if you're not sure what size to make it. Cut four standard boards approximately two inches thick and eight inches wide, known as 2x8s, to this length to serve as the top of your table.
You can also use 2x4s or other boards. Just cut enough so that the overall width of your coffee table, when the boards are laid side-by-side, will be to your liking.
It is also possible to make a coffee table top from a single, wide slab of suitable wood. However, this might be harder to find at your local hardware store.
Piece the top together. Put wood glue on the long, two-inch thick sides of the boards you are using for the top, and slide them together so that they form one large, flat surface. Make sure that the ends line up evenly. Use long clamps to hold them in place while the glue dries.
You only have to glue the sides of the boards that are touching each other.
Secure the top. Cut 2x4 boards so that you have two lengths of 32 inches (81 cm) each. Position them so that they lie across all of the boards that you glued together to make the top. Place one on each side a couple of inches from the narrower edge of the top. The narrow ends of the 2x4s should be flush with the long sides of the coffee table top. Attach them to the top using wood screws.
Add sides (an apron), if you like. This step is optional, but can add a bit of extra charm to your project. Cut two 2x4 boards the length of your table, and two more its width. Cut the ends of each board on their narrow side at a 45 degree angle. Glue the boards to the sides of your coffee table top so that they are flush with the top. Clamp them in place, and nail or screw them to the top once the glue has dried.
Cut the pieces for the legs. Using 4x4s, cut four pieces to the height you would like your coffee table to be. Approximately 17 inches (43 cm) should be a comfortable height. You will also need to cut two pieces of plywood to 3.5 inches (8.9 cm) by 26 inches (66 cm). Finally, cut two pieces of 2x4 to the length of 19 inches (48 cm).
Piece the legs together. Take one of the pieces of plywood you cut and attach one leg to each end, screwing the plywood down on the top (square end) of each leg. Position one of the 2x4 pieces you cut 4.5 inches (11 cm) from the bottom of the other end of the legs. Screw through the legs into the 2x4 so that it sits between the two legs. Repeat this step for the other set of legs.
Stain or paint your coffee table pieces. You can choose to paint your table in a vibrant color, use varnish for a natural look, or choose a stain to give it a rich, rustic look. Follow the manufacturer's instructions, and paint/stain all of the pieces of your table, allowing plenty of time for them to dry before continuing.
Usually, manufacturers will recommend sanding the wood surface before you finish it with paint or stain.
For a classy look, you can use a light-colored stain for the tabletop, and a darker one for its legs.
Attach the legs to the top. When everything is dry, turn the tabletop over so that the bottom with the 2x4s is facing up. Turn the legs over so that the plywood attaching them at the top is sitting on top of the 2x4s. Attach the legs by driving screws through the plywood into the 2x4s. Turn everything over, and your table is done!
Make sure that the legs are evenly spaced on the bottom of the tabletop before you screw them into place.
Build a modular coffee table out of crates. Look for any kind of wooden crates, like those that hold wine, apples, eggs, or milk. The crates can be new or vintage, but you will need four of the same size. Stain or paint them as you like. Once they are dry, lay the crates on their sides and screw them together so that they form one large square.
The sides of the crates will serve as the tabletop.
Use the parts originally meant to hold the wine, milk, or other items as storage or shelf space below the tabletop.
Put a pallet on legs. Find a wooden pallet used for shipping, and four legs from an old piece of furniture or purchased new from a hardware store. Screw one leg into each corner of the bottom of the pallet. Paint or stain the whole table.
Add a piece of plexiglass on top of the pallet for a smoother feel.
Use an old door as a coffee table top. Look for an old wooden door at a thrift store or flea market. Cut the door to about half its size (unless you want a very long coffee table). Attach four furniture legs (new or old) to the flat surface of the door, one to each of its corners. You can paint or stain the door if you like, but for extra vintage charm, leave it as-is.
For a sturdier coffee table, you can screw the door to the top of an existing coffee table, or two small end tables of equal size, instead of just attaching legs to the door's corners.
Repurpose an old window as a table top. Glass makes a nice surface for any coffee table, but the frame of an old window adds a chic touch. Attach furniture legs to the corners of the window, or simply screw the window on top of a basic end table to have a small but unique coffee table.
If you have a wooden box approximately the same size as your window, you can attach the two pieces together at their edges with hinges. That way, the window will still serve as the coffee table top, but the box will provide added storage.
Repurpose an old suitcase. For this project, all you have to do is drill a hole in each corner of the back of a vintage suitcase, with the front-facing portion now facing up. Then, drive screws through the holes and into the tops of four wooden or metal table legs. The closed suitcase serves as a coffee table, but opening it reveals a stylish storage space.
For added strength, you can cut a piece of plywood to the size of the interior of the bottom of the suitcase. Place it in the bottom, and drive screws through it into the tops of the legs you are attaching.
Use reclaimed wood. Many cities now have locations that offer reclaimed wood for sale. You can often find beautiful and unique pieces that are great for coffee tables. Find a slab that's a good size for this purpose, and use a simple finish like varnish or polyurethane to preserve the natural look. Then, attach existing table legs to the bottom.
Leave imperfections like knots in the wood for a touch of rustic charm.
You can also build your own legs by using 4x4 wood cut to your preferred length. .
Some reclaimed wood centers also sell stumps or thick slices from tree trunks. If you find one of these, you can also just place the glass top from an old round table on top of the chunk of wood for a simple coffee table.
Give an existing coffee table a makeover. For a really simple project, you can take a coffee table you already have, and paint or restain it. Remove the glass, if it has any, and use paint/stain remover to strip the old finish off. Add a new paint or stain color that you prefer.
Old coffee tables can usually be found at thrift stores, flea markets, and yard sales, just waiting for a makeover.
If you are building a coffee table from reclaimed or repurposed materials, try hunting at yard sales, flea markets, and thrift stores for materials to get started with.
You can check with local stores to see if they have extra crates, pallets, and similar materials that you can use. Many stores will leave these materials by their dumpsters for people who would like to take them.
All of the other materials you'll need to assemble these projects should be available at a hardware store.
To make a coffee table, try screwing together 4 large crates, like wine, apple, or egg crates, to make a coffee table with built-in storage. Or, you can paint or stain a wooden pallet and attach it to 4 legs for an easy, simple coffee table design. If you have access to an old door or window, repurpose it as a table top for a rustic coffee table. You could also screw legs into a vintage suitcase or trunk to make a stylish coffee table with extra storage space. To learn how to build a simple coffee table using wooden boards, read on!
Thanks to all authors for creating a page that has been read 102,068 times.
"I am doing a project for my leaving certification and I found this quiet helpful."


text:
Q: Panel which stacks child elements horizontally, fitting them into the available size I'm looking for some layout panel for WPF which "stacks" its children horizontally, but fits them all into the panel, i.e. not like StackPanel which just keeps on adding them with their full width.
So for example, this magical panel is 1000 points wide. I add a child element to it, which gets the full 1000 point width. I add another one and the two children get 500 points each. I add yet another one and the three children now get 333.33333... point width. Et cetera.

A: <UniformGrid Rows="1" />

Should do the trick.

A: you can use UniformGrid, settings it's property - Rows to 1. This panel arranges childs in cell's of equal sizes.


Past programs can also be viewed on You Tube.
Our spring programs begin on Sunday, April 7 with songs from musical theatre performed by Walter Cramer and Claire Simard.
Watch for the full schedule here. Stop by Richter House on Sunday afternoons. All programs begin at 3 p.m.
May 6: Receiving, Annual Member Art Show and Sale, 3 - 7 p.m.
May 11: Opening Reception to the Annual Member Art Show, 2 - 5 p.m.
May 19: The Blue River Jazz Band from 3-4 p.m.


The elusively intriguing high priest of heartbreak Alan Power is coming to The Victoria. Don't miss the brooding, dark music maestro's crunching Americana. Debut single Prodigal Son is worth a listen, it really picks up at the end, so stick with it.


Default service provider for creating MWLogger instances.
The value should be an array suitable for use with ObjectFactory::getObjectFromSpec(). The created object is expected to implement the MWLoggerSpi interface. See ObjectFactory for additional details.
NullSpi creates MWLogger instances that will discard all logging events.
LegacySpi (default) creates LegacyLogger instances which mimic the historic implementation of MediaWiki's wfErrorLog logging implementation.
MWLoggerMonologSpi creates loggers implemented by Monolog. See Manual:MWLoggerMonologSpi for configuration settings.
This page was last edited on 19 December 2018, at 12:03.