Linux下hp打印机驱动hplip分析

本文详细分析了Linux下HP打印机驱动hplip的工作流程,包括D-Bus初始化、服务器端hp-systray启动、客户端与服务器端交互、LEDM通信协议以及hpmud的使用。通过hplip,程序能够与打印机进行通信,获取状态信息,并通过D-Bus进行事件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

               

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-Bus服务器端回调函数。

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的服务器端。D-bus服务器端启动完毕。

2.D-bus服务器hp-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种与打印机通信方式,分别是:

  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在收到这个消息后,会怎么处理,且看以后分析。以上是向服务器端请求事件,服务器端收到事件后会调用handleSessionSignal回调。

handleSessionSignal -> handleStatusReply -> updateDevice。


4.客户端与服务器端交互

        客户端hp-toolbox,服务器端hp-systray.他们分别启动后,如何进行交互是一个重点,基于服务器端是有自己的消息通知和界面显示的,不过只是一般的事件信息。hp-toolbox可以主动获取打印机信息。

        服务器端会主动向打印机设备获取状态信息,客户端获取的要是服务器保存好的状态信息,这个基本属于 生产者-消费者 之间的关系。

        

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:AlertDet
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值