USB打印机固件

    本文地址:http://tongxinmao.com/Article/Detail/id/73

    http://www.microchip.com/forums/m491016.aspx


    Printer Class

    Printer Device Class Document 1.1 
    IPP Protocol 1.0 and Adopters Agreement (.zip, 1.32 MB)




    Snippet of printer class (device) implementation

    This snippet is based on this example (v2.6a)
    C:\Microchip Solutions\USB Device - WinUSB - Generic Driver Demo

    Tsuneo


    usb_config.h


     // add these definitions

    /* Printer class */
    #define PRINTER_INTF_ID           0
    #define PRINTER_EP_NUM            1
    #define PRINTER_EP_OUT_SIZE      64
    #define PRINTER_EP_IN_SIZE       64


    usb_descriptor.c


    #ifndef __USB_DESCRIPTORS_C
    #define __USB_DESCRIPTORS_C
     
    /** INCLUDES *******************************************************/
    #include "./USB/usb.h"

    /** CONSTANTS ******************************************************/
    #if defined(__18CXX)
    #pragma romdata
    #endif

    /* Device Descriptor */
    ROM USB_DEVICE_DESCRIPTOR device_dsc=
    {
        0x12,                   // Size of this descriptor in bytes
        USB_DESCRIPTOR_DEVICE,  // DEVICE descriptor type
        0x0200,                 // USB Spec Release Number in BCD format        
        0x00,                   // Class Code
        0x00,                   // Subclass code
        0x00,                   // Protocol code
        USB_EP0_BUFF_SIZE,          // Max packet size for EP0, see usb_config.h
        0xFF07,                 // Vendor ID: temporary for development
        0x0100,                 // Product ID:
        0x0000,                 // Device release number in BCD format
        0x01,                   // Manufacturer string index
        0x02,                   // Product string index
        0x03,                   // Device serial number string index
        0x01                    // Number of possible configurations
    };

    /* Configuration 1 Descriptor */
    ROM BYTE configDescriptor1[]={
        // Configuration Descriptor
        0x09,                       //sizeof(USB_CFG_DSC),  // Size of this descriptor in bytes
        USB_DESCRIPTOR_CONFIGURATION,  // CONFIGURATION descriptor type
        0x20,0x00,                  // Total length of data for this cfg
        1,                          // Number of interfaces in this cfg
        1,                          // Index value of this configuration
        0,                          // Configuration string index
        _DEFAULT | _SELF,           // Attributes, see usb_device.h
        50,                         // Max power consumption (2X mA)
                               
        // Interface Descriptor
        0x09,                       //sizeof(USB_INTF_DSC),  // Size of this descriptor in bytes
        USB_DESCRIPTOR_INTERFACE,   // INTERFACE descriptor type
        PRINTER_INTF_ID,            // Interface Index
        0,                          // Alternate Setting Number
        2,                          // Number of endpoints in this intf
        0x07,                       // Class code   : Printers
        0x01,                       // Subclass code: Printers
        0x03,                       // Protocol code: 1284.4 compatible bi-directional
        0,                          // Interface string index
       
        // Endpoint Descriptor
        0x07,                       //sizeof(USB_EP_DSC)
        USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
        PRINTER_EP_NUM | _EP_OUT,   //EndpointAddress
        _BULK,                      //Attributes
        PRINTER_EP_OUT_SIZE,0x00,   //size
        1,                          //Interval

        // Endpoint Descriptor
        0x07,                       //sizeof(USB_EP_DSC)
        USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
        PRINTER_EP_NUM | _EP_IN,    //EndpointAddress
        _BULK,                      //Attributes
        PRINTER_EP_IN_SIZE,0x00,    //size
        1,                          //Interval
    };


    //Language code string descriptor
    ROM struct{BYTE bLength;BYTE bDscType;WORD string[1];}sd000={
    sizeof(sd000),USB_DESCRIPTOR_STRING,{0x0409}};

    //Manufacturer string descriptor
    ROM struct{BYTE bLength;BYTE bDscType;WORD string[25];}sd001={
    sizeof(sd001),USB_DESCRIPTOR_STRING,
    {'M','i','c','r','o','c','h','i','p',' ',
    'T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.'
    }};

    //Product string descriptor
    ROM struct{BYTE bLength;BYTE bDscType;WORD string[32];}sd002={
    sizeof(sd002),USB_DESCRIPTOR_STRING,
    {'M','i','c','r','o','c','h','i','p',' ','P','r','i','n','t','e','r',
    ' ','E','x','a','m','p','l','e',' ','D','e','v','i','c','e'}};

    //Serial number string descriptor
    ROM struct{BYTE bLength;BYTE bDscType;WORD string[4];}sd003={
    sizeof(sd003),USB_DESCRIPTOR_STRING,
    {'0','0','0','1',}};


    //Array of configuration descriptors
    ROM BYTE *ROM USB_CD_Ptr[]=
    {
        (ROM BYTE *ROM)&configDescriptor1
    };
    //Array of string descriptors
    ROM BYTE *ROM USB_SD_Ptr[]=
    {
        (ROM BYTE *ROM)&sd000,
        (ROM BYTE *ROM)&sd001,
        (ROM BYTE *ROM)&sd002,
        (ROM BYTE *ROM)&sd003
    };

    /** EOF usb_descriptors.c ***************************************************/

    #endif


    main.c

    USB_VOLATILE BOOL prn_flag_soft_reset = FALSE;

    void ProcessIO(void)
    {  
                                                            // Blink the LEDs according to the USB device status,
                                                            // but only do so if the PC application isn't connected
                                                            // and controlling the LEDs.
        if(blinkStatusValid)
        {
            BlinkUSBStatus();
        }

        if( (USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1) ) return;

                                                            // a packet comes from host
        if( !USBHandleBusy( USBGenericOutHandle ) )
        {
            BYTE received_num;

            received_num = USBHandleGetLength( USBGenericOutHandle );   // number of data bytes on OUTPacket[]
            //
            // process data on OUTPacket[] here
            //
                                                            // pass the buffer to USB engine for next packet
            USBGenericOutHandle = USBGenRead( PRINTER_EP_NUM, (BYTE*)&OUTPacket, PRINTER_EP_OUT_SIZE );
        }
                                                            // Soft_Reset comes from host
        if ( prn_flag_soft_reset ) {
            prn_flag_soft_reset = FALSE;
           
            //
            // Stop current print process
            // Clear printer buffer, if any
            //
        }

    }//end ProcessIO


    void USBCBInitEP(void)
    {


        USBEnableEndpoint( PRINTER_EP_NUM, USB_OUT_ENABLED|USB_IN_ENABLED|USB_HANDSHAKE_ENABLED|USB_DISALLOW_SETUP );
        USBGenericOutHandle = USBGenRead( PRINTER_EP_NUM, OUTPacket, PRINTER_EP_OUT_SIZE );
        prn_flag_soft_reset = FALSE;
    }


    void USBCheckPrinterRequest( void );                // prototype

    void USBCBCheckOtherReq( void )
    {
        USBCheckPrinterRequest();
    }




    //
    // class-specific request handlers for printer
    //

    // external global variables

    extern volatile CTRL_TRF_SETUP SetupPkt;
    extern volatile BYTE CtrlTrfData[];
    extern volatile BDT_ENTRY *pBDTEntryOut[];

    // definition of USB_NEXT_PING_PONG - copied from usb_device.c

    #if (USB_PING_PONG_MODE == USB_PING_PONG__NO_PING_PONG)
        #define USB_NEXT_PING_PONG 0x0000
    #elif (USB_PING_PONG_MODE == USB_PING_PONG__EP0_OUT_ONLY)
        #define USB_NEXT_PING_PONG 0x0000
    #elif (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)
        #if defined (__18CXX) || defined(__C30__)
            #define USB_NEXT_PING_PONG 0x0004
        #elif defined(__C32__)
            #define USB_NEXT_PING_PONG 0x0008
        #else
            #error "Not defined for this compiler"
        #endif
    #elif (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0)
        #define USB_NEXT_PING_PONG 0x0004
    #else
        #error "No ping pong mode defined."
    #endif


    // bRequest value for printer class-specific requests
    enum {
        PRNT_GET_DEVICE_ID   = 0,
        PRNT_GET_PORT_STATUS = 1,
        PRNT_SOFT_RESET      = 2
    };

    // Port Status bitmap
    typedef union __attribute__ ((packed))
    {
        BYTE b;
        struct __attribute__ ((packed))
        {
            unsigned                 :3;
            unsigned prn_not_error   :1;
            unsigned prn_select      :1;
            unsigned prn_paper_empty :1;
            unsigned                 :2;
        };
    } T_prn_port_status;

    // Device ID string
    //   see these links for the details
    //      http://www.undocprint.org/formats/communication_protocols/ieee_1284
    //      http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pnplpt.rtf

    typedef struct __attribute__ ((packed))
    {
        BYTE size_h, size_l;
        BYTE str[];
    } T_prn_Device_ID;

    static ROM T_prn_Device_ID prn_Device_ID =
    {
        0x00, 14 + 15 + 11 + 12 + 20,   // size of string, two-bytes, MSB first
        {                               // these strings are concatenated by compiler
            "MFG:MICROCHIP;"            //   manufacturer
            "MDL:MCHP-PR001;"           //   model
            "CMD:1284.4;"               //   PDL command set
            "CLS:PRINTER;"              //   class
            "DES:MICROCHIP PR001;"      //   description
    //      "CID:LPTENUM\Hewlett-PackardHP_La847D"   //   compatible ID: HP LaserJet 1200
        }
    };


    void USBCheckPrinterRequest( void )
    {
                                                            // Get_Device_ID
        if ( (SetupPkt.bmRequestType == (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE))
          && (SetupPkt.bRequest      == PRNT_GET_DEVICE_ID)
          && (SetupPkt.wValue        == 0)                  // config index (start with 0, == bConfigurationValue - 1)
          && (SetupPkt.bIntfID       == 0)                  // alternate interface
          && (SetupPkt.bIntfID_H     == PRINTER_INTF_ID)    // interface number, bInterfaceNumber
        ) {
                                                            // pass the Device ID string except for the last '\0'
            USBEP0SendROMPtr( (ROM BYTE *)&prn_Device_ID, prn_Device_ID.size_l + 2, USB_EP0_INCLUDE_ZERO );
        }


                                                            // Get_Port_Status
        if ( (SetupPkt.bmRequestType == (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE))
          && (SetupPkt.bRequest      == PRNT_GET_PORT_STATUS)
          && (SetupPkt.wValue        == 0)                  // must be zero
          && (SetupPkt.wIndex        == PRINTER_INTF_ID)    // interface number, bInterfaceNumber
          && (SetupPkt.wLength       == 1)                  // size of reply: must be 1
        ) {
            T_prn_port_status port_status;

            port_status.b = 0;                              // initialize status bitmap

            port_status.prn_not_error   = 1;                // 1 = No Error,    0 = Error
            port_status.prn_select      = 1;                // 1 = Selected,    0 = Not Selected
            port_status.prn_paper_empty = 0;                // 1 = Paper Empty, 0 = Paper Not Empty

            CtrlTrfData[0] = port_status.b;                 // pass the status to the stack
            USBEP0SendRAMPtr( CtrlTrfData, 1, USB_EP0_INCLUDE_ZERO );
        }


                                                            // Soft_Reset
        if (((SetupPkt.bmRequestType == (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE))
          || (SetupPkt.bmRequestType == (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_OTHER)))
          && (SetupPkt.bRequest      == PRNT_SOFT_RESET)
          && (SetupPkt.wValue        == 0)                  // must be zero
          && (SetupPkt.wIndex        == PRINTER_INTF_ID)    // interface number, bInterfaceNumber
          && (SetupPkt.wLength       == 0)                  // must be zero
        ) {
            BDT_ENTRY *p;
                                                            // if the bulk endpint is stalled, recover it
        #if defined(__C32__)
            DWORD* pUEP;

            pUEP = (DWORD*)(&U1EP0);
            pUEP += (PRINTER_EP_NUM*4);
        #else
            unsigned char* pUEP;            

            pUEP = (unsigned char*)(&U1EP0+PRINTER_EP_NUM);
        #endif
            if ( *pUEP & UEP_STALL ) {
                *pUEP &= ~UEP_STALL;                        // Clear EPSTALL bit in the UEP register
                                                            // Clear BSTALL bit on BDnSTAT for OUT endpoint
                p = pBDTEntryOut[PRINTER_EP_NUM];
        #if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)  
                p->STAT.Val = _USIE|_DAT0|_DTSEN;
                ((BYTE_VAL*)&p)->Val ^= USB_NEXT_PING_PONG; //toggle over to the next buffer
                p->STAT.Val = _USIE|_DAT1|_DTSEN;
        #else
                p->STAT.Val = _USIE|_DAT1|_DTSEN;
        #endif
                                                            // Clear BSTALL bit on BDnSTAT for IN endpoint
                p = pBDTEntryIn[PRINTER_EP_NUM];
        #if (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG)  
                p->STAT.Val = _UCPU|_DAT0;
                ((BYTE_VAL*)&p)->Val ^= USB_NEXT_PING_PONG; //toggle over the to the next buffer
                p->STAT.Val = _UCPU|_DAT1;
        #else
                p->STAT.Val = _UCPU|_DAT1;
        #endif
            }
                                                            // discard and pass the buffer
            if( !USBHandleBusy( USBGenericOutHandle ) ) {
                USBGenericOutHandle = USBGenRead( PRINTER_EP_NUM, (BYTE*)&OUTPacket, PRINTER_EP_OUT_SIZE );
            }

            prn_flag_soft_reset = TRUE;
            USBEP0Transmit( USB_EP0_NO_DATA );              // report success on STATUS stage
        }
    }

    It's a good idea.
    I've investigated Windows default INF files a little more, and found these clues.

    a) Windows have two default INF for USB printer class (class_07)

    usbprint.inf - USB\Class_07
    dot4.inf - USB\Class_07&SubClass_01&Prot_03

    When dot4.inf is applied, driver installation is interractive, not automatically.

    usbprint.inf

    [Microsoft]
    %USBPRINT.DeviceDesc% = USBPRINT_Inst,USB\Class_07,GENERIC_USB_PRINTER

    dot4.inf

    [ControlFlags]
    ExcludeFromSelect = *
    InteractiveInstall = USB\Class_07&SubClass_01&Prot_03

    [MS_Models]
    %DOT4USB.DeviceDesc% = DOT4USB_Inst,USB\Class_07&SubClass_01&Prot_03,GENERIC_USB_PRINTER


    b) Generic text-only printer is defined in another default INF, as follows.

    ntprint.inf

    [Generic]
    "Generic / Text Only" = TTY.GPD  ,GenericGeneric_/_Tex8040,Generic_/_Text_Only

    According to this MS document,
    http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pnplpt.rtf
    this device ID on the INF is generated by these manufacturer and model fields of printer "Device ID" string. (revised)

    MFG:Generic; 
    MDL:Generic_/_Text_Only; 



    Now that we know these facts, modify above code as follows.

    a) To apply usbprint.inf silently on installation, change InterfaceProtocol value on the interface descriptor, from 3 (1284.4) to 2 (generic bi-directional)


       // Interface Descriptor
       0x09,                       //sizeof(USB_INTF_DSC),  // Size of this descriptor in bytes
       USB_DESCRIPTOR_INTERFACE,   // INTERFACE descriptor type
       PRINTER_INTF_ID,            // Interface Index
       0,                          // Alternate Setting Number
       2,                          // Number of endpoints in this intf
       0x07,                       // Class code   : Printers
       0x01,                       // Subclass code: Printers
       0x02,                       // Protocol code: bi-directional    <---------------
       0,                          // Interface string index


    b) To assign "Generic / Text Only" driver silently, change printer device ID string as follows. (revised)


    static ROM T_prn_Device_ID prn_Device_ID =
    {
       0x00, 12 + 24 + 11 + 12 + 30,          // size of string, two-bytes, MSB first
       {                                      // these strings are concatenated by compiler
           "MFG:Generic;"                     //   manufacturer (case sensitive)
           "MDL:Generic_/_Text_Only;"         //   model (case sensitive)
           "CMD:1284.4;"                      //   PDL command set
           "CLS:PRINTER;"                     //   class
           "DES:Generic text only printer;"   //   description
       }
    };


    In my above post, I've mistakenly posted printer ID string (already revised)
    The actual printer ID string for text-only is as follows.

    MFG:Generic; 
    MDL:Generic_/_Text_Only; 

    Also, the code for prn_Device_ID was revised.
    It's case sensitive, and with underscore.

    With this modification, the printer firmware is recognized by Windows silently (except for tray pop-up).

    If you've tried the wrong string, sorry for your inconvenience.

    Tsuneo


    上一篇:Altium Designer技巧
    下一篇:如何使用8位单片机设计一款灵活的低成本血糖仪