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行能够看出handleSessionSignal是D-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) ......
启动了hpssd和hpdio。hpssd前者会从w3管道从读取hpdio写入的打印机状态信息。
以下单说hpdio怎样获取打印机状态当hpdio run起来的时候会做以下调用run(hpdio.py)->queryDevice(device.py)->status.StatusType10(status.py)->StatusType10Status(status.py)来获取打印机状态。
在queryDevice这个函数中出现了6种与打印机通信方式,各自是:
- Type 1/2 (s: or VSTATUS:) status
- Type 3/9 LaserJet PML
- Type 6: LJ XML
- Type 8: LJ PJL
- Type 10: LEDM
- 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调用流程:
- StatusType10FetchUrl 从打印机获取状态数据
- # Parse the product status XML 解析xml,提取状态值
- getUrl_LEDM
- LocalOpenerEWS_LEDM().openhp()
- writeEWS_LEDM
- readEWS_LEDM
- 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相同实现了多种通信方式:
- musb 基于眼下经常使用的libusb通信.(本次採用打印机正是用这样的方式)
- musb_libusb01 基于眼下较老版本号的libusb通信.
- jd 基于jetdirect 的打印机通信.
- pml 和python层相应。是一种打印语言
- pp Parallel port 并口通信方式
7.C语言实现状态获取
#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 ", ret); len = strlen(data); if(ret == HPMUD_R_IO_TIMEOUT) { debug("I/O timeout "); break; } if(ret != HPMUD_R_OK) { debug("Channel read error "); break; } //debug("read_buf:%s ", data); if(!len) { debug("End of data "); break; } memcpy(p, data, len); if (num_bytes == bytes_to_read) { debug("Full read complete. "); break; } if (allow_short_read && (num_bytes < bytes_to_read)) { debug("Allowed short read of %d of %d bytes complete. ", num_bytes, bytes_to_read); break; } } debug("Saved %d total bytes to stream. ", num_bytes); return num_bytes; } void readLEDMData() { int timeout = 6; const char* END_OF_DATA="0 "; 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! "); return; } bytes_read = __readChannel(bytes_requested, reply, 1, timeout); offset += bytes_read; //debug("%s:%s ", __func__, reply); // 默认chunked分块. chunkedFlag = 1; //result = strtok(reply, " "); //debug("result=%s ", 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 ", __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 ", __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) ", 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) ", res); return 1; } char buf[1024] = "GET /DevMgmt/ProductStatusDyn.xml HTTP/1.1 Accept: text/plain Host:localhost User-Agent:hplip "; //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) ", 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 + " "); 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 ", ver); pSf+=12; break; } /* Extract VStatus.*/ status = 0; HEX2INT(*pSf, status); pSf++; status = status << 4; HEX2INT(*pSf, status); } printf("status:%d ", 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 e%-12345X"; static const char pjl_ustatus_cmd[] = "e%-12345X@PJL USTATUS DEVICE = ON @PJL USTATUS JOB = ON @PJL JOB e%-12345X"; static const char pjl_job_end_cmd[] = "e%-12345X@PJL EOJ e%-12345X"; static const char pjl_ustatus_off_cmd[] = "e%-12345X@PJL USTATUSOFF 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... ", 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) ", 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... ", 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) ", result_code); exit(1); } else { channels[ser_name_id] = channel_id; debug("channel-id=%d ", channel_id); ret = channel_id; } } else { printf("already open! "); 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 ", ret); len = strlen(data); if(ret == HPMUD_R_IO_TIMEOUT) { debug("I/O timeout "); break; } if(ret != HPMUD_R_OK) { debug("Channel read error "); break; } //debug("read_buf:%s ", data); if(!len) { debug("End of data "); break; } memcpy(p, data, len); if (num_bytes == bytes_to_read) { debug("Full read complete. "); break; } if (allow_short_read && (num_bytes < bytes_to_read)) { debug("Allowed short read of %d of %d bytes complete. ", num_bytes, bytes_to_read); break; } } debug("Saved %d total bytes to stream. ", 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 ", result_code); if (result_code != HPMUD_R_OK) { fprintf(stderr, "Channel write error "); 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 ", total_bytes_to_write, bytes_out); return -1; } //printf("%s end ", __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) ", __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) ", __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 "; 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! "); return; } bytes_read = func(bytes_requested, reply, timeout); offset += bytes_read; //debug("%s:%s ", __func__, reply); // 默认chunked分块. chunkedFlag = 1; //result = strtok(reply, " "); //debug("result=%s ", 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 ", __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 ", __func__, reply); } static int open_hp(const char* url, char* reply) { char data[512] = {0}; debug("open_hp(%s) ", url); // match_obj = http_pat_url.search(url) // loc = url.split("=")[url.count("=")] openEWS_LEDM(); sprintf(data, "GET %s HTTP/1.1 Accept: text/plain Host:localhost User-Agent:hplip ", 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! "); return; } int fd = open("/home/kangear/bin.bin", O_RDONLY); if(fd == -1) { fprintf(stderr, "open file error! "); return; } int num = read(fd, buf, 107968); if(num == -1) { fprintf(stderr, "read file error! "); 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" "); #else fprintf(stdout, "direct hp "Unknown" "HP Printer (HPLIP)" "); #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 ", 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) ", 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 ", 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 ", old_state); } /* Display error. */ //device_event(dev, printer, status, username, jobid, title); fprintf(stderr, "STATE: +%s ", pstate); old_state = pstate; } BUG("ERROR: %d %s; will retry in %d seconds... ", status, pstate, RETRY_TIMEOUT); sleep(RETRY_TIMEOUT); continue; } /* Clear any old state. */ if (old_state) fprintf(stderr, "STATE: -%s ", 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! ", 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 ", 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 ", ver); pSf+=12; break; } /* Extract VStatus.*/ status = 0; HEX2INT(*pSf, status); pSf++; status = status << 4; HEX2INT(*pSf, status); status += 1000; } printf("status:%d ", 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 ", ver); pSf+=12; break; } /* Extract VStatus.*/ status = 0; HEX2INT(*pSf, status); pSf++; status = status << 4; HEX2INT(*pSf, status); status += 1000; } printf("status:%d ", 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)" "); 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 ", 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! "); goto bugout; } printf("device discovery success! "); // step 2:解析一个device uri. if(make_device_uri(dev_uri) != EXIT_SUCCESS) { debug("make device uri failed! "); goto bugout; } // step 3:open hp device if(open_device(dev_uri) != EXIT_SUCCESS) { debug("open_device device failed! "); 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号状态,建议提示提示语“墨盒故障或墨盒丢失”。 |