Linux下hp打印機驅動hplip分析


Hplip分析

        版本號是2.14,源代碼位置:http://hplipopensource.com

圖的來源:http://hplipopensource.com/node/128

實踐中使用的打印機型號:Deskjet 1010.分析的目的就是搞清楚一個灰色地帶---打印機通信.


1.D-Bus初始化流程

D-Bus的初始化相同是在ui4/devmgr5.py開始的。

ui4/devmgr5.py

01 class DevMgr5(QMainWindow,  Ui_MainWindow):

02     ......

03     # TODO: Make sbus init mandatory success, else exit

04     def initDBus(self):

05         self.dbus_loop = DBusQtMainLoop(set_as_default=True)

06         self.dbus_avail, self.service, self.session_bus = device.init_dbus(self.dbus_loop)

07     ......

08        # Receive events from the session bus

09          self.session_bus.add_signal_receiver(self.handleSessionSignal, sender_keyword='sender',

10              destination_keyword='dest', interface_keyword='interface',

11              member_keyword='member', path_keyword='path')

12     ......

這里調用了base/device.py中的init_dbus()。從第9行能夠看出handleSessionSignalD-Busserver端回調函數。

base/device.py

01 #

02 # DBus Support

03 #

04 

05 def init_dbus(dbus_loop=None):

06     ......

07         try:

08             if dbus_loop is None:

09                 session_bus = dbus.SessionBus()

10             else:

11                 session_bus = dbus.SessionBus(dbus_loop)

12     ......

13         try:

14             log.debug("Connecting to com.hplip.StatusService (try #1)...")

15             service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")

16             dbus_avail = True

17         except dbus.exceptions.DBusException, e:

18             try:

19                 os.waitpid(-1, os.WNOHANG)

20             except OSError:

21                 pass

22 

23             path = utils.which('hp-systray')

24     ......

25             log.debug("Running hp-systray: %s --force-startup" % path)

26 

27             os.spawnlp(os.P_NOWAIT, path, 'hp-systray', '--force-startup')

28 

29             log.debug("Waiting for hp-systray to start...")

30             time.sleep(1)

31 

32             t = 2

33             while True:

34                 try:

35                     log.debug("Connecting to com.hplip.StatusService (try #%d)..." % t)

36                     service = session_bus.get_object('com.hplip.StatusService', "/com/hplip/StatusService")

37     ......

38 

39     return dbus_avail, service,  session_bus

40 

41     ......

42 

27行啟動了hp-systray作為d-bus的server端。D-busserver端啟動完成。

2.D-busserverhp-systray端啟動

啟動命令例如以下:

$ hp-systray --force-startup -g

說明:-g是調試模式啟動

if __name__ == '__main__':
    ......
    if child_pid1:
        # parent (UI)
        os.close(w1)
        ......                
        else: # qt4
            try:
                import ui4.systemtray as systray
            except ImportError:
                log.error("Unable to load Qt4 support. Is it installed?

") mod.unlockInstance() sys.exit(1) try: systray.run(r1) finally: mod.unlockInstance() else: # child (dbus & device i/o [qt4] or dbus [qt3]) os.close(r1) if ui_toolkit == 'qt4': r2, w2 = os.pipe() r3, w3 = os.pipe() child_pid2 = os.fork() if child_pid2: # parent (dbus) os.close(r2) import hpssd hpssd.run(w1, w2, r3) else: # child (device i/o) os.close(w2) import hpdio hpdio.run(r2, w3) ......

啟動了hpssdhpdiohpssd前者會從w3管道從讀取hpdio寫入的打印機狀態信息。

以下單說hpdio怎樣獲取打印機狀態當hpdio run起來的時候會做以下調用run(hpdio.py)->queryDevice(device.py)->status.StatusType10(status.py)->StatusType10Status(status.py)來獲取打印機狀態。

    在queryDevice這個函數中出現了6種與打印機通信方式,各自是:

  1. Type 1/2 (s: or VSTATUS:) status
  2. Type 3/9 LaserJet PML
  3. Type 6: LJ XML
  4. Type 8: LJ PJL
  5. Type 10: LEDM
  6. Type 11: LEDM_FF_CC_0

而我眼下分析的這款打印機使用是第5種Type 10: LEDM通信協議 HPMUD_S_EWS_LEDM_CHANNEL。每種通信通信都有關於打印機狀態值的定義,另外從這款打印機的DeviceId能夠看出應該同一時候也是支持PJL的,這里先僅僅說第5種LEDM通信方式.LEDM的大體通信方式是基於HTTP,通信USB傳輸一個HTTP的GET指令,然后再通過USB讀取打印機返回的HTTP超文本,一個是XML.

StatusType10Status調用流程:

  1. StatusType10FetchUrl  從打印機獲取狀態數據
  2. # Parse the product status XML 解析xml,提取狀態值
StatusType10FetchUrl調用流程:
  1. getUrl_LEDM
  2. LocalOpenerEWS_LEDM().openhp()
openhp調用流程:(進入LEDM處理流程)
  1. writeEWS_LEDM
  2. readEWS_LEDM
  3. readLEDMData

3.刷新狀態的流程

toolbox.py

01 else: # qt4

02     ......

03     from ui4.devmgr5 import DevMgr5

04     ......

第三行能夠看出啟動了ui4文件夾下的devmgr5這個python

ui4/devmgr5.py

01 class DevMgr5(QMainWindow,  Ui_MainWindow):

02     ......

03     def initUI(self):

04     ......

05         self.DeviceRefreshAction.setIcon(QIcon(load_pixmap("refresh1", "16x16")))

06         self.connect(self.DeviceRefreshAction, SIGNAL("triggered()"), self.DeviceRefreshAction_activated)

07     ......

08    def DeviceRefreshAction_activated(self):

09        self.DeviceRefreshAction.setEnabled(False)

10        self.requestDeviceUpdate()

11        self.DeviceRefreshAction.setEnabled(True)

12        ......

13    def requestDeviceUpdate(self, dev=None, item=None):

14        """ Submit device update request to update thread. """

15

16       if dev is None:

17            dev = self.cur_device

18

19        if dev is not None:

20            dev.error_state = ERROR_STATE_REFRESHING

21            self.updateDevice(dev, update_tab=False)

22

23            self.sendMessage(dev.device_uri, '', EVENT_DEVICE_UPDATE_REQUESTED)

24      ......

25     def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,

26                     job_id=0, title=''):

27 

28         device.Event(device_uri, printer_name, event_code, username,

29                     job_id, title).send_via_dbus(self.session_bus)

30     .....

從第06行能夠看出DeviceRefreshAction的槽是DeviceRefreshAction_activated在行8行,接着調用了requestDeviceUpdate。然后調用了sendMessage。然后調用了device.Event。這個在device.py中。在

base/device.py

01 class Event(object):

02     ......

03     def send_via_dbus(self, session_bus, interface='com.hplip.StatusService'):

04         if session_bus is not None and dbus_avail:

05             log.debug("Sending event %d to %s (via dbus)..." % (self.event_code, interface))

06             msg = lowlevel.SignalMessage('/', interface, 'Event')

07             msg.append(signature=self.dbus_fmt, *self.as_tuple())

08             session_bus.send_message(msg)

09     ......

10 

這里調用的send_message是獲取的d-bus實例的send_message,它在hpdio.py中。

hpdio.py

01 def send_message(device_uri, event_code, bytes_written=0):

02     args = [device_uri, '', event_code, prop.username, 0, '', '', bytes_written]

03     msg = lowlevel.SignalMessage('/', 'com.hplip.StatusService', 'Event')

04     msg.append(signature='ssisissi', *args)

05     SessionBus().send_message(msg)

這里是標准的d-bus通信。D-bus在收到這個消息后,會怎么處理,且看以后分析。以上是向server端請求事件。server端收到事件后會調用handleSessionSignal回調。

handleSessionSignal -> handleStatusReply -> updateDevice。


4.client與server端交互

        clienthp-toolbox,server端hp-systray.他們分別啟動后,怎樣進行交互是一個重點,基於server端是有自己的消息通知和界面顯示的,只是僅僅是一般的事件信息。

hp-toolbox能夠主動獲取打印機信息。

        server端會主動向打印機設備獲取狀態信息,client獲取的要是server保存好的狀態信息。這個基本屬於 生產者-消費者 之間的關系。

        

5.LEDM通信協議

       全稱: Low End Data Model(在hplib的status.py中有介紹:def StatusType10(func): # Low End Data Model)。

眼下已經是HP一個專利專利EP2556480A1.

打開channel調用流程:

openEWS_LEDM -> __openChannel -> hpmudext.open_channel -> hpmud處理

讀數據流程:

readEWS_LEDM -> __readChannel -> hpmudext.read_channel -> hpmud處理

寫命令流程:

writeEWS_LEDM -> __writeChannel -> hpmudext.write_channel -> hpmud處理

 

       以獲取ProductStatusDyn記錄一下收發數據的情況。 

        發送命令(報文):

GET /DevMgmt/ProductStatusDyn.xml HTTP/1.1#015#012Accept: text/plain#015#012Host:localhost#015#012User-Agent:hplip#015#012#015#012
       接收數據:

<?xml version="1.0" encoding="UTF-8"?>
<!-- THIS DATA SUBJECT TO DISCLAIMER(S) INCLUDED WITH THE PRODUCT OF ORIGIN. -->
<psdyn:ProductStatusDyn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:dd="http://www.hp.com/schemas/imaging/con/dictionaries/1.0/" xmlns:ad="http://www.hp.com/schemas/imaging/con/ledm/alertdetails/2007/10/31" xmlns:pscat="http://www.hp.com/schemas/imaging/con/ledm/productstatuscategories/2007/10/31" xmlns:locid="http://www.hp.com/schemas/imaging/con/ledm/localizationids/2007/10/31" xmlns:psdyn="http://www.hp.com/schemas/imaging/con/ledm/productstatusdyn/2007/10/31" xsi:schemaLocation="http://www.hp.com/schemas/imaging/con/dictionaries/1.0/ ../schemas/dd/DataDictionaryMasterLEDM.xsd http://www.hp.com/schemas/imaging/con/ledm/alertdetails/2007/10/31 ../schemas/AlertDetails.xsd http://www.hp.com/schemas/imaging/con/ledm/productstatuscategories/2007/10/31 ../schemas/ProductStatusCategories.xsd http://www.hp.com/schemas/imaging/con/ledm/localizationids/2007/10/31 ../schemas/LocalizationIds.xsd http://www.hp.com/schemas/imaging/con/ledm/productstatusdyn/2007/10/31 ../schemas/ProductStatusDyn.xsd">
	<dd:Version>
		<dd:Revision>SVN-IPG-LEDM.216</dd:Revision>
		<dd:Date>2011-02-08</dd:Date>
	</dd:Version>
	<psdyn:Status>
		<pscat:StatusCategory>closeDoorOrCover</pscat:StatusCategory>
		<locid:StringId>65568</locid:StringId>
	</psdyn:Status>
	<psdyn:AlertTable>
		<dd:ModificationNumber>6</dd:ModificationNumber>
		<psdyn:Alert>
			<ad:ProductStatusAlertID>closeDoorOrCover</ad:ProductStatusAlertID>
			<locid:StringId>65568</locid:StringId>
			<dd:SequenceNumber>5</dd:SequenceNumber>
			<ad:Severity>Error</ad:Severity>
			<ad:AlertPriority>1</ad:AlertPriority>
			<ad:AlertDetails>
				<ad:AlertDetailsDoorCoverLocation>cover</ad:AlertDetailsDoorCoverLocation>
			</ad:AlertDetails>
			<dd:ResourceURI>/DevMgmt/ProductConfigDyn.xml</dd:ResourceURI>
			<dd:ResourceType>ledm:hpLedmProductConfigDyn</dd:ResourceType>
		</psdyn:Alert>
		<psdyn:Alert>
			<ad:ProductStatusAlertID>cartridgeMissing</ad:ProductStatusAlertID>
			<locid:StringId>65537</locid:StringId>
			<dd:SequenceNumber>4</dd:SequenceNumber>
			<ad:Severity>Error</ad:Severity>
			<ad:AlertPriority>11</ad:AlertPriority>
			<ad:AlertDetails>
				<ad:AlertDetailsMarkerColor>Black</ad:AlertDetailsMarkerColor>
				<ad:AlertDetailsConsumableTypeEnum>inkCartridge</ad:AlertDetailsConsumableTypeEnum>
				<ad:AlertDetailsMarkerLocation>1</ad:AlertDetailsMarkerLocation>
			</ad:AlertDetails>
			<dd:ResourceURI>/DevMgmt/ConsumableConfigDyn.xml</dd:ResourceURI>
			<dd:ResourceType>ledm:hpLedmConsumableConfigDyn</dd:ResourceType>
		</psdyn:Alert>
		<psdyn:Alert>
			<ad:ProductStatusAlertID>cartridgeMissing</ad:ProductStatusAlertID>
			<locid:StringId>65537</locid:StringId>
			<dd:SequenceNumber>3</dd:SequenceNumber>
			<ad:Severity>Error</ad:Severity>
			<ad:AlertPriority>11</ad:AlertPriority>
			<ad:AlertDetails>
				<ad:AlertDetailsMarkerColor>CyanMagentaYellow</ad:AlertDetailsMarkerColor>
				<ad:AlertDetailsConsumableTypeEnum>inkCartridge</ad:AlertDetailsConsumableTypeEnum>
				<ad:AlertDetailsMarkerLocation>0</ad:AlertDetailsMarkerLocation>
			</ad:AlertDetails>
			<dd:ResourceURI>/DevMgmt/ConsumableConfigDyn.xml</dd:ResourceURI>
			<dd:ResourceType>ledm:hpLedmConsumableConfigDyn</dd:ResourceType>
		</psdyn:Alert>
	</psdyn:AlertTable>
</psdyn:ProductStatusDyn>
        pscat:StatusCategory字段記錄的正是打印機的當前狀態。

6.hpmud分析

        MUlti-point transport Driver or HPMUD represents the I/O layer for HPLIP. HPMUD does not depend on Linux specific libusb extensions. This means any UNIX/Linux derivative that supports libusb may work with HPLIP. A public HPLIP "C" API is exposed through hpmud. See hpmud.h for documentation. A python wrapper for hpmud, called hpmudext, is also available.

        以上是官網介紹,如開頭圖中所看到的,hpmud是負責真正和打印機設備進行通信的,它基於libusb開發,所以能夠執行於所以含libusb的系統中。

hpmud相同實現了多種通信方式:

  1. musb                       基於眼下經常使用的libusb通信.(本次採用打印機正是用這樣的方式)
  2. musb_libusb01     基於眼下較老版本號的libusb通信.
  3. jd                              基於jetdirect 的打印機通信.
  4. pml                          和python層相應。是一種打印語言
  5. pp                             Parallel port 並口通信方式
        這里僅僅說musb通信方式。因為基於libusb,所以這里的musb就類似於java和c中的jni僅僅是一個接口的轉接,musb會把python傳送過來的指令通過libusb直接丟給打印機設備。
        另符上hpmud的調試方法,調試宏DBG的實現是syslog,把這個宏打開會將log信息輸出到/var/log/syslog中。


7.C語言實現狀態獲取

        經過20多天的,無情的看源代碼,最終有所獲。最終把Device.py中獲取狀態的部分用C語言實現了。提取了關鍵部分。如今能夠正常獲取打印機狀態。因為之前沒有接觸過HTTP協議,一個錯誤的報文格式使得我連續非常多天都處於迷茫之中,如今看來在於心智還是不全然成熟。在感覺要成功的時候就靜不下心來了,不在閱讀最后一段代碼,然后有時那段代碼才是最重要的。"Door Open"從打印機到PC屏幕的流程最終走通了。

#include <stdio.h>
#include <string.h>
#include <hpmud.h>
#include <malloc.h>

#ifdef  DEBUG
#define debug(fmt,args...)  debug (fmt ,##args)
#define debugX(level,fmt,args...) if (DEBUG>=level) debug(fmt,##args);
#else
#define debug(fmt,args...)
#define debugX(level,fmt,args...)
#endif  /* DEBUG */

static HPMUD_DEVICE dd;
static HPMUD_CHANNEL cd;

#define MALLOC(type, n)  (type*)malloc(n*sizeof(type))

static int __readChannel(int bytes_to_read, int* reply, int allow_short_read, int timeout)
{
    bytes_to_read = 1024;
    char data[1024] = {0};
    int ret = 0;
    int num_bytes = 0;
    int len = 0;
    int *p = reply;
    while (1)
    {
        ret = hpmud_read_channel(dd, cd, data, 1024, timeout, &num_bytes);
        debug("Result code=%d\n", ret);
        len = strlen(data);
        if(ret == HPMUD_R_IO_TIMEOUT)
        {
            debug("I/O timeout\n");
            break;
        }
        if(ret != HPMUD_R_OK)
        {
            debug("Channel read error\n");
            break;
        }

        //debug("read_buf:%s\n", data);

        if(!len)
        {
            debug("End of data\n");
            break;
        }

        memcpy(p, data, len);

        if (num_bytes == bytes_to_read)
        {
            debug("Full read complete.\n");
            break;
        }

        if (allow_short_read && (num_bytes < bytes_to_read))
        {
            debug("Allowed short read of %d of %d bytes complete.\n", num_bytes, bytes_to_read);
            break;
        }
    }

    debug("Saved %d total bytes to stream.\n", num_bytes);
    return num_bytes;
}

void readLEDMData()
{
    int timeout = 6;
    const char* END_OF_DATA="0\r\n\r\n";
    int bytes_read = 0;
    int bytes_requested = 1024;
    char temp_buf[1024] = {0};  //大小要一致
    int chunkedFlag = 1;
    char* result = NULL;
    char *reply = (int*)MALLOC(unsigned char, 5*1024);
    char *offset = reply;
    if(reply == NULL)
    {
        fprintf(stderr, "MALLC FAILURE!\n");
        return;
    }

    bytes_read = __readChannel(bytes_requested, reply, 1, timeout);
    offset += bytes_read;
    //debug("%s:%s\n", __func__, reply);

    // 默認chunked分塊.
    chunkedFlag = 1;

    //result = strtok(reply, "\n");

    //debug("result=%s\n", result);

    while (bytes_read > 0)
    {
        bytes_read = __readChannel(bytes_requested, (int*)temp_buf, 1, timeout);

        //reply.write(temp_buf.getvalue());
        memcpy(offset, temp_buf, bytes_read);
        debug("%s:%s\n", __func__, offset);
        offset += bytes_read;

        if(!chunkedFlag) // Unchunked data
        {
            // do nothing!
        }
        else // Chunked data end
        {
            //END_OF_DATA == temp_buf.getvalue();
            if(!strncmp(temp_buf, END_OF_DATA, sizeof(END_OF_DATA)))
                break;
        }
    }

    printf("%s:%s\n", __func__, reply);

    free(reply);
    reply = NULL;
}

int main(void)
{
    enum HPMUD_RESULT res;
    const char *device_uri = "hp:/usb/Deskjet_1010_series?serial=CN39I18M1805S8";
    enum HPMUD_IO_MODE io_mode = HPMUD_RAW_MODE;

    // 打開設備獲得
    res = hpmud_open_device(device_uri, io_mode, &dd);
    if (res != HPMUD_R_OK)
    {
        fprintf(stderr, "error opening device (code=%d)\n", res);
        return 1;
    }

    // 打開頻道獲得頻道id
    res = hpmud_open_channel(dd, HPMUD_S_EWS_LEDM_CHANNEL, &cd);
    if (res != HPMUD_R_OK)
    {
        fprintf(stderr, "error opening channel (code=%d)\n", res);
        return 1;
    }

    char buf[1024] = "GET /DevMgmt/ProductStatusDyn.xml HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n";
    //char ConsumableConfigDyn[] = "GET /DevMgmt/ConsumableConfigDyn.xml HTTP/1.1#015#012Accept: text/plain#015#012Host:localhost#015#012User-Agent:hplip#015#012#015#012";
    int bit = 0;

    // 寫入命令
    res = hpmud_write_channel(dd, cd, buf, 100, 6, &bit);
    if (res != HPMUD_R_OK)
    {
        fprintf(stderr, "error hpmud_write_channel (code=%d)\n", res);
        return 1;
    }

    readLEDMData();
    return 0;

    hpmud_close_channel(dd, cd);
    hpmud_close_device(dd);

    return 0;
}

        本程序會從打印出所得到狀態的數據,pscat:StatusCategory字段中的就是狀態值了,事實上能到了如《 關於打印機狀態的獲取》提到15個左右的狀態,可是這個協議所規定的狀態所有羅列出來:

字串

狀態

編號

processing

STATUS_PRINTER_PRINTING

1002

ready

STATUS_PRINTER_IDLE

1000

closeDoorOrCover

STATUS_PRINTER_DOOR_OPEN

1802

shuttingDown

STATUS_PRINTER_TURNING_OFF

1003

cancelJob

STATUS_PRINTER_CANCELING

1005

trayEmptyOrOpen

STATUS_PRINTER_OUT_OF_PAPER

1009

jamInPrinter

STATUS_PRINTER_MEDIA_JAM

1014

hardError

STATUS_PRINTER_HARD_ERROR

1018

outputBinFull

STATUS_PRINTER_OUTPUT_BIN_FULL

1002

unexpectedSizeInTray

sizeMismatchInTray

STATUS_PRINTER_MEDIA_SIZE_MISMATCH

1023

insertOrCloseTray2

STATUS_PRINTER_TRAY_2_MISSING

1029

scannerError

EVENT_SCANNER_FAIL

2002

scanProcessing

EVENT_START_SCAN_JOB

2000

scannerAdfLoaded

EVENT_SCAN_ADF_LOADED

2004

scanToDestinationNotSet

EVENT_SCAN_TO_DESTINATION_NOTSET

2005

scanWaitingForPC

EVENT_SCAN_WAITING_FOR_PC

2006

scannerAdfJam

EVENT_SCAN_ADF_JAM

2007

scannerAdfDoorOpen

EVENT_SCAN_ADF_DOOR_OPEN

2008

faxProcessing

EVENT_START_FAX_JOB

3000

faxSending

STATUS_FAX_TX_ACTIVE

3004

faxReceiving

STATUS_FAX_RX_ACTIVE

3005

faxDialing

EVENT_FAX_DIALING

3006

faxConnecting

EVENT_FAX_CONNECTING

3007

faxSendError

EVENT_FAX_SEND_ERROR

3008

faxErrorStorageFull

EVENT_FAX_ERROR_STORAGE_FULL

3009

faxReceiveError

EVENT_FAX_RECV_ERROR

3010

faxBlocking

EVENT_FAX_BLOCKING

3011

inPowerSave

STATUS_PRINTER_POWER_SAVE

1046

incorrectCartridge

STATUS_PRINTER_CARTRIDGE_WRONG

1047

cartridgeMissing

STATUS_PRINTER_CARTRIDGE_MISSING

1048

missingPrintHead

STATUS_PRINTER_PRINTHEAD_MISSING

 

1049

scannerADFMispick

STATUS_SCANNER_ADF_MISPICK

1050

mediaTooShortToAutoDuplex

STATUS_PRINTER_PAPER_TOO_SHORT_TO_AUTODUPLEX

1051

insertOrCloseTray

STATUS_PRINTER_TRAY_2_3_DOOR_OPEN

1052

inkTooLowToPrime

STATUS_PRINTER_INK_TOO_LOW_TO_PRIME

1053

cartridgeVeryLow

STATUS_PRINTER_VERY_LOW_ON_INK

1054

wasteMarkerCollectorAlmostFull

STATUS_PRINTER_SERVICE_INK_CONTAINER_ALMOST_FULL

1055

wasteMarkerCollectorFull

STATUS_PRINTER_SERVICE_INK_CONTAINER_FULL

1056

wasteMarkerCollectorFullPrompt

STATUS_PRINTER_SERVICE_INK_CONTAINER_FULL_PROMPT

1057

missingDuplexer

STATUS_PRINTER_DUPLEX_MODULE_MISSING

1058

printBarStall

STATUS_PRINTER_PRINTHEAD_JAM

1059

outputBinClosed

STATUS_PRINTER_CLEAR_OUTPUT_AREA

1060

outputBinOpened

STATUS_PRINTER_CLEAR_OUTPUT_AREA

1060

reseatDuplexer

STATUS_PRINTER_RESEAT_DUPLEXER

1061

unexpectedTypeInTray

STATUS_PRINTER_MEDIA_TYPE_MISMATCH

1042

manuallyFeed

STATUS_MANUALLY_FEED

1062

 

STATUS_UNKNOWN_CODE

1065

         擴展:眼下僅僅是上一個課題的總結。這之后的應用還有不少的問題,怎樣應用。以及和利用設備節點打印是否沖突,以及是否能打印也基於hpmud。

。。


8.意外收獲

        老天真的會眷戀努力的人,剛在PC上實現基於HPMUD的狀態獲取,考慮着進行三步走中的更為繁瑣的后兩步(移植到嵌入式Linux+移植到Android)的時候,上天又送我一份大禮--打印機狀態獲取的還有一種方式:通過DeviceId

        這個可能不一定適合全部打印機。可是能夠確定的是全然適應我如今正在調試的這款。

關於DeviceId從一開始看打印機相關的東西的時候最先接觸到的就是這個DeviceId,能夠說我對它的感情也是跌宕起伏。從一喜得DeviceId。到認為其作用單一。到如今的強大無比。

        以下說點正經的,在《互聯網打印協議-rfc2911》的printer-state-reasons章節規定了打印機異常狀態碼。也就是說除了使用HP自己定義的LEDM獲取的狀態外,相同還能夠通過標准的IPP協議得到狀態值。由此也能延伸出一個問題,CUPS應該也能夠獲取打印機狀態了。可是卻沒有做不論什么顯示。至於是為什么。這個還是比較玄乎。

        詳細來看DeviceID中含有狀態的“S”字段:S:038000C484a01021002c1f01100c2881100;首先說明的是這個當中的都是16進制的數。前兩位是版本號信息,依據版本號信息不同。狀態碼所在的位置也不同,比方這里的版本號號為03,那么狀態碼在第16位的兩位數這里為00轉換為10進制也是0。這個狀態代表空暇。有時候代碼比語言更有說服力。

java版本號:

      public int getPrinterStatusCode() {
          String deviceId = getPrinterDeivceId();
          int ippStatus = -1;
          int pSf = 2;
  
          if(deviceId == null)
              return ippStatus;
          
          // somthing
          String str[] = deviceId.split(";S:");
          if(str.length > 1)
          {
              if(str[1] != null)
              {
                  int ver=Integer.parseInt(str[1].substring(0,2), 16);
                         /* Position pointer to printer state subfield. */
                  switch (ver)
                  {
                    case 0:
                    case 1:
                    case 2:
                       pSf+=12;
                       break;
                    case 3:
                       pSf+=14;
                       break;
                    case 4:
                       pSf+=18;
                       break;
                    default:
                       Slog.w(LOG_TAG, "WARNING: unknown S-field version=" + ver + "\n");
                       pSf+=12;
                       break;
                  }
                  ippStatus = Integer.parseInt(str[1].substring(pSf, pSf + 2), 16);
              }
          }
          return ippStatus;
      }

C版本號:

static int get_printer_status_code(const char* device_id)
{
	const char* id = device_id;
    char *pSf;
    int ver;
    int status = 0;

    /* Check for valid S-field in device id string. */
    if ((pSf = strstr(id, ";S:")) == NULL)
    {
    	/* No S-field, use status register instead of device id. */
    	/* do nothing */
    	goto bugout;
    }
    else
    {
       /* Valid S-field, get version number. */
       pSf+=3;
       ver = 0;
       HEX2INT(*pSf, ver);
       pSf++;
       ver = ver << 4;
       HEX2INT(*pSf, ver);
       pSf++;

       /* Position pointer to printer state subfield. */
       switch (ver)
       {
          case 0:
          case 1:
          case 2:
             pSf+=12;
             break;
          case 3:
             pSf+=14;
             break;
          case 4:
             pSf+=18;
             break;
          default:
             printf("WARNING: unknown S-field version=%d\n", ver);
             pSf+=12;
             break;
       }

       /* Extract VStatus.*/
       status = 0;
       HEX2INT(*pSf, status);
       pSf++;
       status = status << 4;
       HEX2INT(*pSf, status);
    }
    printf("status:%d\n", status);

bugout:
    return status;
}

狀態碼相應關系:

   VSTATUS_IDLE = 0,
   VSTATUS_BUSY = 1,
   VSTATUS_PRNT = 2,      /* io printing */
   VSTATUS_OFFF = 3,      /* turning off */
   VSTATUS_RPRT = 4,      /* report printing */
   VSTATUS_CNCL = 5,      /* canceling */
   VSTATUS_IOST = 6,      /* io stall */
   VSTATUS_DRYW = 7,      /* dry time wait */
   VSTATUS_PENC = 8,      /* pen change */
   VSTATUS_OOPA = 9,      /* out of paper */
   VSTATUS_BNEJ = 10,      /* banner eject needed */
   VSTATUS_BNMZ = 11,      /* banner mismatch */
   VSTATUS_PHMZ = 12,      /* photo mismatch */
   VSTATUS_DPMZ = 13,      /* duplex mismatch */
   VSTATUS_PAJM = 14,      /* media jam */
   VSTATUS_CARS = 15,      /* carriage stall */
   VSTATUS_PAPS = 16,      /* paper stall */
   VSTATUS_PENF = 17,      /* pen failure */
   VSTATUS_ERRO = 18,      /* hard error */
   VSTATUS_PWDN = 19,      /* power down */
   VSTATUS_FPTS = 20,      /* front panel test */
   VSTATUS_CLNO = 21       /* clean out tray missing */

    文章至此該結束了。


其他:

Linux打印驅動知識點

1.關於DeviceId各段意義見《ieee_1284

2.關於LEDM見《DISCOVERING PC-CONNECTED DEVICES

3.Syslog見《syslog-example》經過實踐輸出到了/var/log/syslog.

4.關於Eclipse CD高版本號的Memory View不能顯示相應的Text.換成Helios Service Release 2使用New Rendrings->Traditional.

5.關於PJL(打印機控制語言)《Printer Job Language Technical Reference Manual

6.終於選擇了基於deviceId的方法來實現。所以臨時不再用hpmud的方法了。可是已經代碼已經實現了printDate和基於ledm獲取打印機的狀態信息。

#include <stdio.h>
#include <string.h>
#include <hpmud.h>
#include <malloc.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdarg.h>


//#define DEBUG

#ifdef  DEBUG
#define debug(fmt,args...)  printf (fmt ,##args)
#define debugX(level,fmt,args...) if (DEBUG>=level) debug(fmt,##args);
#else
#define debug(fmt,args...)
#define debugX(level,fmt,args...)
#endif  /* DEBUG */

// hp printer device
static HPMUD_DEVICE hd;

#define MALLOC(type, n)  (type*)malloc(n*sizeof(type))

// HPMUD_I_MAX
#define HPMUD_I_MAX  18
static int channels[HPMUD_I_MAX] = {0};

typedef HPMUD_CHANNEL(open_channel)(void);
typedef int(read_func)(int bytes_requested, char* reply, int timeout);

enum BACKEND_RESULT
{
  BACKEND_OK = 0,
  BACKEND_FAILED = 1,           /* use error-policy */
  BACKEND_HOLD = 3,             /* hold job */
  BACKEND_STOP = 4,             /* stop queue */
  BACKEND_CANCEL = 5            /* cancel job */
};

struct pjl_attributes
{
   int pjl_device;   /* 0=disabled, 1=enabled */
   int current_status;
   int eoj_pages;        /* end-of-job pages */
   int abort;         /* 0=no, 1=yes */
   int done;          /* 0=no, 1=yes */
   HPMUD_DEVICE dd;
   HPMUD_CHANNEL cd;
   pthread_t tid;
   pthread_mutex_t mutex;
   pthread_cond_t done_cond;
};

#define _STRINGIZE(x) #x
#define STRINGIZE(x) _STRINGIZE(x)

#define BUG(args...) bug(__FILE__ " " STRINGIZE(__LINE__) ": " args)

#ifdef HP_DEBUG
   #define DBG(args...) syslog(LOG_INFO, __FILE__ " " STRINGIZE(__LINE__) ": " args)
   #define DBG_DUMP(data, size) sysdump((data), (size))
   #define DBG_SZ(args...) syslog(LOG_INFO, args)
#else
   #define DBG(args...)
   #define DBG_DUMP(data, size)
   #define DBG_SZ(args...)
#endif

#define RETRY_TIMEOUT 30  /* seconds */
#define EXCEPTION_TIMEOUT 45 /* seconds */

#define NFAULT_BIT  0x08
#define PERROR_BIT  0x20

#define OOP             (NFAULT_BIT | PERROR_BIT)
#define JAMMED          (PERROR_BIT)
#define ERROR_TRAP      (0)

#define STATUS_MASK (NFAULT_BIT | PERROR_BIT)

#define DEVICE_IS_OOP(reg)  ((reg & STATUS_MASK) == OOP)
#define DEVICE_PAPER_JAMMED(reg)  ((reg & STATUS_MASK) == JAMMED)
#define DEVICE_IO_TRAP(reg)       ((reg & STATUS_MASK) == ERROR_TRAP)

#define HEX2INT(x, i) if (x >= '0' && x <= '9')      i |= x - '0'; \
                       else if (x >= 'A' && x <= 'F') i |= 0xA + x - 'A'; \
                       else if (x >= 'a' && x <= 'f') i |= 0xA + x - 'a'

/* Definitions for hpLogLevel in cupsd.conf. */
#define BASIC_LOG          1
#define SAVE_PCL_FILE      2
#define SAVE_INPUT_RASTERS 4
#define SEND_TO_PRINTER_ALSO    8

/* Actual vstatus codes are mapped to 1000+vstatus for DeviceError messages. */
typedef enum
{
   VSTATUS_IDLE = 1000,
   VSTATUS_BUSY,
   VSTATUS_PRNT,      /* io printing */
   VSTATUS_OFFF,      /* turning off */
   VSTATUS_RPRT,      /* report printing */
   VSTATUS_CNCL,      /* canceling */
   VSTATUS_IOST,      /* io stall */
   VSTATUS_DRYW,      /* dry time wait */
   VSTATUS_PENC,      /* pen change */
   VSTATUS_OOPA,      /* out of paper */
   VSTATUS_BNEJ,      /* banner eject needed */
   VSTATUS_BNMZ,      /* banner mismatch */
   VSTATUS_PHMZ,      /* photo mismatch */
   VSTATUS_DPMZ,      /* duplex mismatch */
   VSTATUS_PAJM,      /* media jam */
   VSTATUS_CARS,      /* carriage stall */
   VSTATUS_PAPS,      /* paper stall */
   VSTATUS_PENF,      /* pen failure */
   VSTATUS_ERRO,      /* hard error */
   VSTATUS_PWDN,      /* power down */
   VSTATUS_FPTS,      /* front panel test */
   VSTATUS_CLNO       /* clean out tray missing */
} VSTATUS;

#define EVENT_START_JOB 500
#define EVENT_END_JOB 501

//const char pjl_status_cmd[] = "\e%-12345X@PJL INFO STATUS \r\n\e%-12345X";
static const char pjl_ustatus_cmd[] = "\e%-12345X@PJL USTATUS DEVICE = ON \r\n@PJL USTATUS JOB = ON \r\n@PJL JOB \r\n\e%-12345X";
static const char pjl_job_end_cmd[] = "\e%-12345X@PJL EOJ \r\n\e%-12345X";
static const char pjl_ustatus_off_cmd[] = "\e%-12345X@PJL USTATUSOFF \r\n\e%-12345X";

static int bug(const char *fmt, ...)
{
   char buf[256];
   va_list args;
   int n;

   va_start(args, fmt);

   if ((n = vsnprintf(buf, 256, fmt, args)) == -1)
      buf[255] = 0;     /* output was truncated */

   fprintf(stderr, "%s", buf);
   //syslog(LOG_ERR, "%s", buf);

   fflush(stderr);
   va_end(args);
   return n;
}

/**
 * 因為使用的數組來實現python中的字典
 * 所以要將字符串轉化為相應數字
 * openChannle和closeChannel中會用到
 */
static int get_service_name_num(char* service_name)
{
    int ser_name_id = 0;

    if(!strncmp(HPMUD_S_PRINT_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 1;
    else if(!strncmp(HPMUD_S_PML_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 2;
    else if(!strncmp(HPMUD_S_SCAN_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 3;
    else if(!strncmp(HPMUD_S_FAX_SEND_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 4;
    else if(!strncmp(HPMUD_S_CONFIG_UPLOAD_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 5;
    else if(!strncmp(HPMUD_S_CONFIG_DOWNLOAD_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 6;
    else if(!strncmp(HPMUD_S_MEMORY_CARD_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 7;
    else if(!strncmp(HPMUD_S_EWS_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 8;
    else if(!strncmp(HPMUD_S_EWS_LEDM_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 9;
    else if(!strncmp(HPMUD_S_SOAP_SCAN, service_name, strlen(service_name)))
        ser_name_id = 10;
    else if(!strncmp(HPMUD_S_SOAP_FAX, service_name, strlen(service_name)))
        ser_name_id = 11;
    else if(!strncmp(HPMUD_S_DEVMGMT_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 12;
    else if(!strncmp(HPMUD_S_MARVELL_SCAN_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 13;
    else if(!strncmp(HPMUD_S_MARVELL_FAX_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 14;
    else if(!strncmp(HPMUD_S_LEDM_SCAN, service_name, strlen(service_name)))
        ser_name_id = 15;
    else if(!strncmp(HPMUD_S_WIFI_CHANNEL, service_name, strlen(service_name)))
        ser_name_id = 16;
    else
        ser_name_id = 0;

    return ser_name_id;
}

static int __closeChannel(char* service_name)
{
    int result_code = 0;
    int ser_name_id = get_service_name_num(service_name);
    //if not self.mq['io-mode'] == IO_MODE_UNI and
    //  if self.io_state == IO_STATE_HP_OPEN:

//        service_name = service_name.upper();
    if(channels[ser_name_id] != 0)
    {
        printf("Closing %s channel...\n", service_name);
        result_code = hpmud_close_channel(hd, channels[ser_name_id]);
        if (result_code != HPMUD_R_OK)
        {
            fprintf(stderr, "error hpmud_close_channel (code=%d)\n", result_code);
            return 1;
        }
        else
        {
            channels[ser_name_id] = 0;
        }
    }

    return 0;
}

/*
 * open channel
 */
static int __openChannel(char* service_name)
{
    int result_code = 0;
    int channel_id  = 0;
    HPMUD_CHANNEL ret = 0;
    int ser_name_id = get_service_name_num(service_name);

    if (channels[ser_name_id] == 0)
    {
        printf("Opening %s channel...\n", service_name);

        result_code = hpmud_open_channel(hd, service_name, &channel_id);
        if (result_code != HPMUD_R_OK)
        {
            fprintf(stderr, "error opening channel (code=%d)\n", result_code);
            exit(1);
        }
        else
        {
            channels[ser_name_id] = channel_id;
            debug("channel-id=%d\n", channel_id);
            ret = channel_id;
        }
    }
    else
    {
        printf("already open!\n");
        ret = channels[ser_name_id];
    }
    return ret;
}

/*
 * read channel
 */
static int __readChannel(open_channel opener, int bytes_to_read, char* reply, int allow_short_read, int timeout)
{
    bytes_to_read = 1024;
    char data[1024] = {0};
    int ret = 0;
    int num_bytes = 0;
    int len = 0;
    char *p = reply;
    HPMUD_CHANNEL channel_id = opener();

    while (1)
    {
        ret = hpmud_read_channel(hd, channel_id, data, 1024, timeout, &num_bytes);
        debug("Result code=%d\n", ret);
        len = strlen(data);
        if(ret == HPMUD_R_IO_TIMEOUT)
        {
            debug("I/O timeout\n");
            break;
        }
        if(ret != HPMUD_R_OK)
        {
            debug("Channel read error\n");
            break;
        }

        //debug("read_buf:%s\n", data);

        if(!len)
        {
            debug("End of data\n");
            break;
        }

        memcpy(p, data, len);

        if (num_bytes == bytes_to_read)
        {
            debug("Full read complete.\n");
            break;
        }

        if (allow_short_read && (num_bytes < bytes_to_read))
        {
            debug("Allowed short read of %d of %d bytes complete.\n", num_bytes, bytes_to_read);
            break;
        }
    }

    debug("Saved %d total bytes to stream.\n", num_bytes);
    return num_bytes;
}

/**
 * write channel
 */
static int __writeChannel(open_channel opener, const char* data, int total_bytes_to_write)
{
    HPMUD_DEVICE  device_id  = hd;
    HPMUD_CHANNEL channel_id = opener();
    int          result_code = 0;
    int        bytes_written = 0;
    const char*             buffer = data;
    int            bytes_out = 0;
    int      max_message_len = 16384;
    int              timeout = 45;
    int       bytes_to_write = total_bytes_to_write;

    while(bytes_to_write > 0)
    {
        result_code = hpmud_write_channel(device_id, channel_id, buffer, bytes_to_write < max_message_len ? bytes_to_write : max_message_len, timeout, &bytes_written);

        printf("Result code=%d\n", result_code);
        if (result_code != HPMUD_R_OK)
        {
            fprintf(stderr, "Channel write error\n");
            return -1;
        }

        buffer += max_message_len;
        bytes_out += bytes_written;
        bytes_to_write -= bytes_written;
    }

    if (total_bytes_to_write != bytes_out)
    {
        printf("total_bytes_to_write =%d =\\= bytes_out=%d\n", total_bytes_to_write, bytes_out);
        return -1;
    }

    //printf("%s end\n", __func__);
    return bytes_out;
}

static HPMUD_CHANNEL openEWS_LEDM()
{
    return __openChannel(HPMUD_S_EWS_LEDM_CHANNEL);
}

static HPMUD_CHANNEL openPrint()
{
    return __openChannel(HPMUD_S_PRINT_CHANNEL);
}

static int closePrint(void)
{
    return __closeChannel(HPMUD_S_PRINT_CHANNEL);
}

static int closeEWS_LEDM(void)
{
    return __closeChannel(HPMUD_S_EWS_LEDM_CHANNEL);
}

static int readEWS_LEDM(int bytes_requested, char* reply, int timeout)
{
    open_channel* opener = openEWS_LEDM;
    return __readChannel(opener, bytes_requested, reply, 1, timeout);
}

static int writePrint(const char* data, int len)
{
    open_channel* opener = openPrint;
    int ret = EXIT_FAILURE;
    int result_code = 0;
    //TODO:delect hpmud_write_channel
    /* Enable unsolicited status. */
    //ret = hpmud_write_channel(hd, channel_id, pjl_ustatus_cmd, sizeof(pjl_ustatus_cmd)-1, 5, &bytes_written);

    result_code = __writeChannel(opener, data, len);
    if(result_code != len)
    {
    	printf("ret != len(%s:%u,%s)\n", __FILE__, __LINE__, __func__);
    	goto bugout;
    }
    /* Look for job end status. */

    //ret = hpmud_write_channel(hd, channel_id, pjl_ustatus_off_cmd, sizeof(pjl_ustatus_off_cmd)-1, 5, &bytes_written);
    result_code = __writeChannel(opener, pjl_job_end_cmd, sizeof(pjl_job_end_cmd)-1);
    if(result_code != (sizeof(pjl_job_end_cmd)-1))
    {
    	printf("ret != len(%s:%u,%s)\n", __FILE__, __LINE__, __func__);
    	goto bugout;
    }

    ret = EXIT_SUCCESS;
bugout:
    return ret;
}

static int writeEWS_LEDM(const char* data, int len)
{
    open_channel* opener = openEWS_LEDM;
    return __writeChannel(opener, data, len);
}


static void readLEDMData(read_func* func, char *reply)
{
    int timeout = 6;
    const char* END_OF_DATA="0\r\n\r\n";
    int bytes_read = 0;
    int bytes_requested = 1024;
    char temp_buf[1024] = {0};  //大小要一致
    int chunkedFlag = 1;
    char *offset = reply;
    if(reply == NULL)
    {
        fprintf(stderr, "MALLC FAILURE!\n");
        return;
    }

    bytes_read = func(bytes_requested, reply, timeout);
    offset += bytes_read;
    //debug("%s:%s\n", __func__, reply);

    // 默認chunked分塊.
    chunkedFlag = 1;

    //result = strtok(reply, "\n");

    //debug("result=%s\n", result);

    while (bytes_read > 0)
    {
        bytes_read = readEWS_LEDM(bytes_requested, (char*)temp_buf, timeout);

        //reply.write(temp_buf.getvalue());
        memcpy(offset, temp_buf, bytes_read);
        debug("%s:%s\n", __func__, offset);
        offset += bytes_read;

        if(!chunkedFlag) // Unchunked data
        {
            // do nothing!
        }
        else // Chunked data end
        {
            //END_OF_DATA == temp_buf.getvalue();
            if(!strncmp(temp_buf, END_OF_DATA, sizeof(END_OF_DATA)))
                break;
        }
    }

    printf("%s:%s\n", __func__, reply);

}

static int open_hp(const char* url, char* reply)
{
    char data[512] = {0};
    debug("open_hp(%s)\n", url);

//    match_obj = http_pat_url.search(url)
//    loc = url.split("=")[url.count("=")]

    openEWS_LEDM();

    sprintf(data, "GET %s HTTP/1.1\r\nAccept: text/plain\r\nHost:localhost\r\nUser-Agent:hplip\r\n\r\n", url);
    writeEWS_LEDM(data, strlen(data));

    //while dev.readEWS_LEDM(512, reply, timeout=3):
        //pass

    read_func* func = readEWS_LEDM;

    readLEDMData(func, reply);

    //reply.seek(0);

    //return reply.getvalue();
    return 0;

}
void getEWSUrl_LEDM(const char* url, char* reply)
{
//    int self, url, stream, footer;
//    url2 = "%s&loc=%s" % (self.device_uri.replace('hpfax:', 'hp:'), url);
//    data = self;
//    opener = LocalOpenerEWS_LEDM({});
//    opener.open_hp(url2, data);
    open_hp(url, reply);
    closeEWS_LEDM();
}

void printData()
{
    char *buf = (char*)MALLOC(unsigned char, 107968);
    if(buf == NULL)
    {
        fprintf(stderr, "MALLC FAILURE!\n");
        return;
    }

    int fd = open("/home/kangear/bin.bin", O_RDONLY);
    if(fd == -1)
    {
        fprintf(stderr, "open file error!\n");
        return;
    }

    int num = read(fd, buf, 107968);
    if(num == -1)
    {
        fprintf(stderr, "read file error!\n");
        return;
    }

    writePrint(buf, num);

    close(fd);
    free(buf);
    buf = NULL;
}

/**
 * device discovery
 * if there is hp device return EXIT_SUCCESS, but EXIT_FAILURE.
 */
static int device_discovery()
{
   char buf[HPMUD_LINE_SIZE*64];
   int cnt=0, bytes_read, r=EXIT_FAILURE;
   enum HPMUD_RESULT stat;

   stat = hpmud_probe_devices(HPMUD_BUS_ALL, buf, sizeof(buf), &cnt, &bytes_read);

   if (stat != HPMUD_R_OK)
      goto bugout;

   if (cnt == 0)
   {
#ifdef HAVE_CUPS11
      fprintf(stdout, "direct hp:/no_device_found \"Unknown\" \"hp no_device_found\"\n");
#else
      fprintf(stdout, "direct hp \"Unknown\" \"HP Printer (HPLIP)\"\n");
#endif
      goto bugout;
   }
   else
      fprintf(stdout, "%s", buf);

   r = EXIT_SUCCESS;

bugout:
   return r;
}

static int open_device(const char* device_uri)
{
    //int io_mode = 0;
    HPMUD_DEVICE device_id = -1;
    int result_code = 0, r = EXIT_FAILURE;

    enum HPMUD_IO_MODE io_mode = HPMUD_RAW_MODE;

    debug("I/O mode=%d\n", io_mode);

    // 打開設備獲得
    result_code = hpmud_open_device(device_uri, io_mode, &device_id);
    if (result_code != HPMUD_R_OK)
    {
        fprintf(stderr, "error opening device (code=%d)\n", result_code);
        goto bugout;
    }

    hd = device_id; //TODO:delect.
    r = EXIT_SUCCESS;

bugout:
    return r;
}


/* Map printer status to IPP printer-state-reasons (see RFC-2911). */
static int map_ipp_printer_state_reason(int status, const char **state_msg)
{

   if (status >= 1000 && status <= 1999)
   {
      /* inkjet vstatus */
      switch (status)
      {
         case VSTATUS_IDLE:
         case VSTATUS_PRNT:
            *state_msg = "none";
            break;
         case VSTATUS_OOPA:
            *state_msg = "media-empty-error";
            break;
         case(VSTATUS_PAJM):
            *state_msg = "media-jam-error";
            break;
         default:
            *state_msg = "other";
            break;
      }
   }
   else if (status >= 10000 && status <= 55999)
   {
      /* laserjet pjl status */
      if (status >= 10000 && status <= 10999)
         *state_msg = "none";
      else if (status >= 41000 && status <= 41999)
         *state_msg = "media-empty-error";
      else if ((status >= 42000 && status <= 42999) || (status >= 44000 && status <= 44999) || (status == 40022))
         *state_msg = "media-jam-error";
      else if (status == 40021)
         *state_msg = "cover-open-error";
      else if (status == 40600)
         *state_msg = "toner-empty-error";
      else
         *state_msg = "other";      /* 40017 - cartridge E-LABEL is unreadable (ie: ljp1005) */
   }
   else
   {
      /* Assume hpmud error */
      *state_msg = "other";
   }

   return 0;
}

/*
 * get_printer_status
 *
 * inputs:
 *   dd - device descriptor
 *   pa - see pjl_attributes definition
 *
 * outputs:
 *   return - printer status, 1000 to 1999 = inkjet vstatus, 5000 to 5999 = hpmud error, 10000 to 55999 = pjl status code
 *
 */
static int get_printer_status(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, struct pjl_attributes *pa)
{
   char id[1024];
   char *pSf;
   int status, ver, len;
   enum HPMUD_RESULT r;

   if (pa->pjl_device)
   {
      pthread_mutex_lock(&pa->mutex);
      status = pa->current_status;
      pthread_mutex_unlock(&pa->mutex);
   }
   else
   {
      status = VSTATUS_IDLE; /* set default */
      r = hpmud_get_device_id(dd, id, sizeof(id), &len);
//      if (!(r == HPMUD_R_OK || r == HPMUD_R_DEVICE_BUSY))
      if (r != HPMUD_R_OK)
      {
         status = 5000+r;      /* no deviceid, return some error */
         goto bugout;
      }

      /* Check for valid S-field in device id string. */
      if ((pSf = strstr(id, ";S:")) == NULL)
      {
         /* No S-field, use status register instead of device id. */
         unsigned int bit_status;
         r = hpmud_get_device_status(dd, &bit_status);
//         if (!(r == HPMUD_R_OK || r == HPMUD_R_DEVICE_BUSY))
         if (r != HPMUD_R_OK)
         {
            status = 5000+r;      /* no 8-bit status, return some error */
            goto bugout;
         }

         if (DEVICE_IS_OOP(bit_status))
            status = VSTATUS_OOPA;
         else if (DEVICE_PAPER_JAMMED(bit_status))
            status = VSTATUS_PAJM;
         else if (DEVICE_IO_TRAP(bit_status))
            status = VSTATUS_CARS;
      }
      else
      {
         /* Valid S-field, get version number. */
         pSf+=3;
         ver = 0;
         HEX2INT(*pSf, ver);
         pSf++;
         ver = ver << 4;
         HEX2INT(*pSf, ver);
         pSf++;

         /* Position pointer to printer state subfield. */
         switch (ver)
         {
            case 0:
            case 1:
            case 2:
               pSf+=12;
               break;
            case 3:
               pSf+=14;
               break;
            case 4:
               pSf+=18;
               break;
            default:
               BUG("WARNING: unknown S-field version=%d\n", ver);
               pSf+=12;
               break;
         }

         /* Extract VStatus.*/
         status = 0;
         HEX2INT(*pSf, status);
         pSf++;
         status = status << 4;
         HEX2INT(*pSf, status);
         status += 1000;
      }
   }

bugout:
   return status;
}


/* Check printer status, if a valid error state, loop until error condition is cleared. */
static int loop_test(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, struct pjl_attributes *pa,
        const char *dev, const char *printer, const char *username, const char *jobid, const char *title)
{
   int status, stat;
   const char *pstate, *old_state=NULL;

   while (1)
   {
      status = get_printer_status(dd, cd, pa);
      map_ipp_printer_state_reason(status, &pstate);

      /* Check for user intervention errors. */
      if (strstr(pstate, "error"))
      {
         if (pstate != old_state)
         {
            if (old_state)
            {
               /* Clear old error. */
//               device_event(dev, printer, status, username, jobid, title);
               fprintf(stderr, "STATE: -%s\n", old_state);
            }

            /* Display error. */
            //device_event(dev, printer, status, username, jobid, title);
            fprintf(stderr, "STATE: +%s\n", pstate);
            old_state = pstate;
         }
         BUG("ERROR: %d %s; will retry in %d seconds...\n", status, pstate, RETRY_TIMEOUT);
         sleep(RETRY_TIMEOUT);
         continue;
      }

      /* Clear any old state. */
      if (old_state)
         fprintf(stderr, "STATE: -%s\n", old_state);

      /* Check for system errors. */
      if (status >= 5000 && status <= 5999)
      {
         /* Display error. */
         //device_event(dev, printer, status, username, jobid, title);
         BUG("ERROR: %d device communication error!\n", status);
         stat = 1;
      }
      else
         stat = 0;

      break;   /* done */
   }

   return stat;
}

int get_device_id(HPMUD_DEVICE hd)
{
    char id[1024] = {0};
    char *pSf;
    int status, ver, len;
    enum HPMUD_RESULT r;
    int ret = EXIT_FAILURE;
    r = hpmud_get_device_id(hd, id,sizeof(id), &len);
    printf("device_id:%s\n", id);
    if (r != HPMUD_R_OK)
    {
       /* no deviceid, return some error */
       goto bugout;
    }
    /* Check for valid S-field in device id string. */
    if ((pSf = strstr(id, ";S:")) == NULL)
    {
       /* No S-field, use status register instead of device id. */
       unsigned int bit_status;
       r = hpmud_get_device_status(hd, &bit_status);
//         if (!(r == HPMUD_R_OK || r == HPMUD_R_DEVICE_BUSY))
       if (r != HPMUD_R_OK)
       {
          status = 5000+r;      /* no 8-bit status, return some error */
          goto bugout;
       }

//       if (DEVICE_IS_OOP(bit_status))
//          status = VSTATUS_OOPA;
//       else if (DEVICE_PAPER_JAMMED(bit_status))
//          status = VSTATUS_PAJM;
//       else if (DEVICE_IO_TRAP(bit_status))
//          status = VSTATUS_CARS;
    }
    else
    {
       /* Valid S-field, get version number. */
       pSf+=3;
       ver = 0;
       HEX2INT(*pSf, ver);
       pSf++;
       ver = ver << 4;
       HEX2INT(*pSf, ver);
       pSf++;

       /* Position pointer to printer state subfield. */
       switch (ver)
       {
          case 0:
          case 1:
          case 2:
             pSf+=12;
             break;
          case 3:
             pSf+=14;
             break;
          case 4:
             pSf+=18;
             break;
          default:
             printf("WARNING: unknown S-field version=%d\n", ver);
             pSf+=12;
             break;
       }

       /* Extract VStatus.*/
       status = 0;
       HEX2INT(*pSf, status);
       pSf++;
       status = status << 4;
       HEX2INT(*pSf, status);
       status += 1000;
    }
    printf("status:%d\n", status);
    ret = EXIT_SUCCESS;
bugout:
    return ret;
}

static int get_printer_status_code(const char* device_id)
{
	const char* id = device_id;
    char *pSf;
    int ver;
    int status = 0;

    /* Check for valid S-field in device id string. */
    if ((pSf = strstr(id, ";S:")) == NULL)
    {
    	/* No S-field, use status register instead of device id. */
    	/* do nothing */
    	goto bugout;
    }
    else
    {
       /* Valid S-field, get version number. */
       pSf+=3;
       ver = 0;
       HEX2INT(*pSf, ver);
       pSf++;
       ver = ver << 4;
       HEX2INT(*pSf, ver);
       pSf++;

       /* Position pointer to printer state subfield. */
       switch (ver)
       {
          case 0:
          case 1:
          case 2:
             pSf+=12;
             break;
          case 3:
             pSf+=14;
             break;
          case 4:
             pSf+=18;
             break;
          default:
             printf("WARNING: unknown S-field version=%d\n", ver);
             pSf+=12;
             break;
       }

       /* Extract VStatus.*/
       status = 0;
       HEX2INT(*pSf, status);
       pSf++;
       status = status << 4;
       HEX2INT(*pSf, status);
       status += 1000;
    }
    printf("status:%d\n", status);

bugout:
    return status;
}

static int get_cups_uri(char* cups_uri)
{
    char buf[HPMUD_LINE_SIZE*64];
    int cnt=0, bytes_read, r=EXIT_FAILURE;
    enum HPMUD_RESULT stat;

    stat = hpmud_probe_devices(HPMUD_BUS_ALL, buf, sizeof(buf), &cnt, &bytes_read);

    if (stat != HPMUD_R_OK)
      goto bugout;

    if (cnt == 0)
        fprintf(stdout, "direct hp \"Unknown\" \"HP Printer (HPLIP)\"\n");
    else
        sprintf(cups_uri, buf, strlen(buf));

    r = EXIT_SUCCESS;

bugout:
    return r;
}


static int make_device_uri(char* dev_uri)
{
    int ret = EXIT_FAILURE;
    enum HPMUD_RESULT stat;
    char cups_uri[HPMUD_LINE_SIZE*64];
    char* p = NULL;

    stat = get_cups_uri(cups_uri);

    if (stat != EXIT_SUCCESS)
      goto bugout;

    p = strtok(cups_uri, " ");
    if(p == NULL)
        goto bugout;

    p = strtok(NULL, " ");
    if(p == NULL)
        goto bugout;

    // copy only one device uri and others ignore.
    sprintf(dev_uri, p, strlen(p));

    debug("dev_uri:%s\n", dev_uri);

    ret = EXIT_SUCCESS;
bugout:
    return ret;
}

int main(void)
{
    int ret = EXIT_FAILURE;
    //const char *device_uri = "hp:/usb/Deskjet_1010_series?serial=CN39I18M1805S8";
    //const char *device_uri = "hp:/usb/Deskjet_1000_J110_series?serial=CN2C818N3605YD";
    char dev_uri[HPMUD_LINE_SIZE] = {0};

    // step 1.
    if(device_discovery() != EXIT_SUCCESS)
    {
        debug("device discovery failed!\n");
        goto bugout;
    }
    printf("device discovery success!\n");

    // step 2:解析一個device uri.
    if(make_device_uri(dev_uri) !=  EXIT_SUCCESS)
    {
        debug("make device uri failed!\n");
        goto bugout;
    }

    // step 3:open hp device
    if(open_device(dev_uri) !=  EXIT_SUCCESS)
    {
        debug("open_device device failed!\n");
        goto bugout;
    }

    // step 4: get device id.
    get_device_id(hd);

    //printData();

    //readAttributeFromXml_EWS

    //StatusType10();

    char* reply = (char*)MALLOC(char, 500*1024);

    //獲取狀態XML
    getEWSUrl_LEDM("/DevMgmt/ProductStatusDyn.xml", reply);  // 獲取狀態
    //getEWSUrl_LEDM("/DevMgmt/ConsumableConfigDyn.xml", reply); // 獲取耗材配置

    free(reply);
    reply = NULL;

    if (hd >= 0)
       hpmud_close_device(hd);

    ret = EXIT_SUCCESS;

bugout:
    return ret;
}

打印機異常狀態:

N

Windows狀態

0

正常

STATUS_PRINTER_IDLE(1000)

1

無法與打印機通信

x

2

出紙盒已關閉

STATUS_PRINTER_OUTPUT_TRAY_CLOSED(1035)

3

門己打開

STATUS_PRINTER_PEN_CHANGE(1008)

4

缺紙

STATUS_PRINTER_OUT_OF_PAPER(1009)

5

卡紙

STATUS_PRINTER_PAPER_STALL(1016)

6

墨盒故障-黑色

STATUS_PRINTER_PEN_FAILURE(1017)

7

墨盒故障-三色

-

8

墨盒故障-黑色-三色

-

9

墨盒丟失

10

單墨盒模式-缺黑色

STATUS_PRINTER_IDLE(1000)

11

單墨盒模式-缺彩色

STATUS_PRINTER_IDLE(1000)

12

無墨  黑色

x

13

無墨  彩色

x

14

無墨  黑色-彩色

x

15

已經安裝HP保護墨盒

x

16

檢測到使用過的或仿制墨盒

x

 

...

 

注:x代表暫無條件獲取, -代表和上條同樣。

假設出現6號狀態,建議提示提示語墨盒故障或墨盒丟失

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM