關於這個需求非常早就考慮了,一直沒敢下手,也不是不敢,是之前下過一次手可是沒有成功。一直過了幾個月騰出一些空暇來解決問題。另外說明一下,截止到眼下對於這個需求我還沒有一個全然的解決方式。這篇也僅僅是捋下思緒。
關於打印機狀態的問題我在stackoverflow上也作過提問,結果問題就被刪除了,原因有二有人說這個問題是硬件上的問題,所以不在stackoverflow所討論的范圍。還有一個也許是自己的英文着實爛的不行了,問題都解釋不清楚。
因為一直沒有死心,所以在平時無聊的時候也會掏出手機搜索一番,當時唯一的收獲是參考文檔[1]中提到的USBHostPrinterGetStatus(),這個奇妙的函數沒有寫不論什么來源,但作者將其描寫敘述成是能夠獲取打印機狀態的,我着實有那么一點興奮。可是苦於沒有來源,有點不知所措。只是我還是將其作為一個方向進行了深挖了的。順便找到了[3]-是和[1]差點兒相同的英文版本號,以及[2]這個現存的這個函數。從整體來說Linux中並沒有這個函數,不知道寫論文的那個是不是先有論文又做的實驗。這個似乎是一個裸機程序,在我找到[5]這樣專業文檔的時候,這個幾個狀態是USB通信協議中就已經規定了的。更加確定這個函數眼下深究下去是一個死路,只是以后做裸機的時候能夠又一次了解。
以上是走過的錯路,以下說下我這次走能的小路:1.從Linux中標准的USB打印機驅動着手;2.從HP Device Manager入手。前后者都小有所獲,以下逐個來分析。逐個分析前先把打印機在Windows下的各種問題的狀態列表記錄一下,話說收集這個可不是一個簡單的事,首先是鎖定打印機型號然后制造這樣的故障,使得PC上可以顯示出對於的狀態對話框。(到眼下為至12-14還並非官方的文字,由於制造該故障的條件一直不具備)
NO |
中文 |
英文 |
limit |
1 |
無法與打印機通信 |
Unable to Communicate with Printer |
E |
2 |
出紙盒已關閉 |
Output Tray Closed |
E |
3 |
門己打開 |
Door Open |
E |
4 |
缺紙 |
Out of Paper |
E |
5 |
卡紙 |
Paper Jam |
E |
6 |
墨盒故障-黑色 |
Ink Cartridge Failure |
E |
7 |
墨盒故障-三色 |
- |
E |
8 |
墨盒故障-黑色-三色 |
- |
E |
9 |
墨盒丟失 |
Ink Cartrideges Missing |
E |
10 |
單墨盒模式-缺黑色 |
Single Ink Cartridge Mode |
W |
11 |
單墨盒模式-缺彩色 |
- |
W |
12 |
無墨 黑色 |
x |
E |
13 |
無墨 彩色 |
x |
E |
14 |
無墨 黑色-彩色 |
x |
E |
15 |
已經安裝HP保護墨盒 |
HP Protected Cartridge Installed |
I |
16 |
檢測到使用過的或仿制墨盒 |
Used or Counterfeit Cartridge Detected |
I |
方向1是從usblp.c中的驅動着手
結合打印機內核驅動源代碼usblp.c以及Usb協議[5]規定打印機狀態:
驅動的詳細實現是在ioctl方法中的LPGETSTATUS命令中返回的狀態,經過實驗確實能獲得一個比較顯著的狀態Out of Paper/Paper Empty這樣一個狀態,應用層的代碼例如以下:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <dirent.h> #include <string.h> #include <signal.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/lp.h>
void getDeviceStatus(int fd) { if(fd < 0) return; int status = 0; ioctl(fd, LPGETSTATUS, &status); printf("%x\n", status); printf("Hello world\n"); }
int main() { int fd = open("/dev/usb/lp0", O_RDWR); getDeviceStatus(fd); close(fd); return 0; } |
只是遺憾的是,這個狀態碼的規律是這種:正常0x18;缺紙 0x38;其他統統是0x10。等於我如今只拿到了一個狀態。我的高興勁頭只持續了幾分鍾。
方向2從HP Device Manager的源代碼入手
例如以下圖,該軟件能夠顯示很多其它的狀態,且還是開源的hplib。從實驗得出該神器能夠獲得以上列出的每一個狀態。

推導一下這個神器的真實身份:HP Device Manager -> hp-boolbox -> hplib終於確定了hplib。hplib的架構是這種:
Hplip的源代碼中有這么一段,用到libusb.能夠獲取打印機狀態:
static int device_status(int fd, unsigned int *status) { libusb_device_handle *hd; int interface; int len, stat=1; unsigned char byte;
hd = fd_table[fd].hd; interface = fd_table[fd].interface;
if (hd == NULL) { BUG("invalid device_status state\n"); goto bugout; }
len = libusb_control_transfer(hd, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, /* bmRequestType */ LIBUSB_REQUEST_CLEAR_FEATURE, /* bRequest */ 0, /* wValue */ interface, /* wIndex */ &byte, 1, LIBUSB_CONTROL_REQ_TIMEOUT);
if (len < 0) { BUG("invalid device_status: %m\n"); goto bugout; }
*status = (unsigned int)byte; stat = 0; DBG("read actual device_status successfully fd=%d\n", fd);
bugout: return stat; } |
就這幾行代碼,但卻難住了我,還是靜下心來看看<libusb Developers Guide>。回頭再來繼續。從hplib追蹤到APDK(hp官方支持的非PC平台的打印驅動),當中包括了與打印通信以及打印機的錯誤代碼。僅僅Google出這么一個好的資料《APDK Developer’s Guide Reference Manual》。這個《hpmud》也相當好,能夠清晰的顯示出hplip源代碼結構。
$ sudo apt-get install libhpmud-dev
標准頭文件hpmud.h,基於這個開發。Hp.c就是一個獨立的基於libhpmud的程序。我在libhpmud中加入�的這個調試信息在執行hp.c的時候能夠看到,可是在執行hp-toolbox的時候根本沒有反應,又有點懷疑,它的底層沒實用libhpmud這個庫。
假設能將Door open這句話從打印機到屏幕的流程走通,這個狀態的問題基本就能夠宣告攻克了。可是這個僅僅是設想,真正實現起來並沒有那么easy。這是一個開篇。
參考文檔:
3.《USB Printer Class on an Embedded Host》
4.《How to retrieve USB printer status?》
5.《Universal Serial Bus Device Class Definition for Printing Devices》