Hello everyone,
I got an old Lexmark X215 scanner/printer combo. The device is a
rebranded Samsung and it didn't take much to convince the xerox_mfp
backend to support it. I've made a preliminary patch (attached). I'm
posting it in case anyone else is interested in getting this ancient
device to work.
In case anyone would think it's a good idea to merge this, below is a
summary of the oddities of this device and my patch.
1. It seems quite confusing to me, but both the printer and the scanner
utilize the same set of BULK usb endpoints, on a Printer class
interface. So they are skipped by sanei_usb. And even if they weren't,
the interface is usually claimed by usblp.
2. To reduce interference with usblp, I've decided to utilize it instead
(or is there a better way?) So I've added a new transport
xerox_mfp-usblp. Instead of using sanei_usb (and libusb underneath),
data is sent directly to the usblp device. On startup, the usual paths
are searched for usblp devices (with vid/pid filtering to filter out
only the Lexmark). The code is unlikely to work under a non-Linux OS
3. The scanner doesn't support CMD_OBJECT_POSITION. The windows driver
doesn't send this command. I've added code to skip this command if the
device model matches "SAMSUNG/ORION" (yes, that's the model name sent by
the firmware).
4. On inquiry, the scanner returns zero values for maximum window height
(both AFP and manual values). I'm using the max_win_len value instead.
Let me know if you have any comments.
--
Andrzej Szombierski
diff --git a/backend/Makefile.am b/backend/Makefile.am
index 48a1393a5..a0fe59cfa 100644
--- a/backend/Makefile.am
+++ b/backend/Makefile.am
@@ -1217,7 +1217,7 @@ libsane_v4l_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_v4l_la_LIBADD = $(COMMON_LIBS) libv4l.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(LIBV4L_LIBS)
EXTRA_DIST += v4l.conf.in
-libxerox_mfp_la_SOURCES = xerox_mfp.c xerox_mfp-usb.c xerox_mfp-tcp.c xerox_mfp.h
+libxerox_mfp_la_SOURCES = xerox_mfp.c xerox_mfp-usb.c xerox_mfp-tcp.c xerox_mfp-usblp.c xerox_mfp.h
libxerox_mfp_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=xerox_mfp
nodist_libsane_xerox_mfp_la_SOURCES = xerox_mfp-s.c
diff --git a/backend/xerox_mfp-usblp.c b/backend/xerox_mfp-usblp.c
new file mode 100644
index 000000000..776dbd4cd
--- /dev/null
+++ b/backend/xerox_mfp-usblp.c
@@ -0,0 +1,148 @@
+/*
+ * SANE backend for Lexmark X251 (adapted from Xerox Phaser 3200MFP)
+ * Copyright 2021 Andrzej Szombierski <[email protected]>
+ *
+ * This program is licensed under GPL + SANE exception.
+ * More info at http://www.sane-project.org/license.html
+ */
+
+#undef BACKEND_NAME
+#define BACKEND_NAME xerox_mfp
+#define DEBUG_DECLARE_ONLY
+#define DEBUG_NOT_STATIC
+#include "sane/config.h"
+#include "sane/saneopts.h"
+#include "sane/sanei_config.h"
+#include "sane/sanei_backend.h"
+#include "sane/sanei_debug.h"
+#include "xerox_mfp.h"
+#include <errno.h>
+#include <asm/ioctls.h>
+#include <unistd.h>
+
+extern int sanei_debug_xerox_mfp;
+
+int
+usblp_dev_request(struct device *dev,
+ SANE_Byte *cmd, size_t cmdlen,
+ SANE_Byte *resp, size_t *resplen)
+{
+ SANE_Status status;
+ size_t len = cmdlen;
+
+ if (cmd && cmdlen) {
+ cmdlen = write(dev->dn, cmd, cmdlen);
+
+ if (cmdlen != len) {
+ DBG(1, "%s: write: wanted %lu bytes, wrote %lu bytes\n",
+ __func__, (size_t)len, (size_t)cmdlen);
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ if (resp && resplen) {
+ *resplen = read(dev->dn, resp, *resplen);
+ if (*resplen <= 0) {
+ DBG(1, "%s: read: %s\n", __func__, strerror(errno));
+ return SANE_STATUS_IO_ERROR;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+
+SANE_Status
+usblp_dev_open(struct device *dev)
+{
+ SANE_Status status;
+
+ DBG(3, "%s: open %p: %s\n", __func__, (void *)dev, dev->sane.name);
+ dev->dn = open(dev->sane.name, O_RDWR);
+ if(dev->dn < 0)
+ return SANE_STATUS_IO_ERROR;
+
+ return SANE_STATUS_GOOD;
+}
+
+void
+usblp_dev_close(struct device *dev)
+{
+ if (!dev)
+ return;
+ DBG(3, "%s: closing dev %p\n", __func__, (void *)dev);
+
+ /* finish all operations */
+ if (dev->scanning) {
+ dev->cancel = 1;
+ /* flush READ_IMAGE data */
+ if (dev->reading)
+ sane_read(dev, NULL, 1, NULL);
+ /* send cancel if not sent before */
+ if (dev->state != SANE_STATUS_CANCELLED)
+ ret_cancel(dev, 0);
+ }
+
+ close(dev->dn);
+ dev->dn = -1;
+}
+
+static int ndevices=-1;
+struct usblp_dev {
+ char path[32];
+ int vidpid[2];
+};
+
+static struct usblp_dev devices[32];
+
+static void usblp_scan()
+{
+ if(ndevices >= 0)
+ return;
+
+ const char *path_formats[] = {
+ "/dev/usb/lp%d",
+ "/dev/usblp%d",
+ NULL
+ };
+ int i,j;
+
+ ndevices=0;
+
+ for(i=0;path_formats[i];i++) {
+ for(j=0;j<16;j++) {
+ struct usblp_dev *dev = &devices[ndevices];
+
+ sprintf(dev->path, path_formats[i], j);
+
+ int fd = open(dev->path, O_RDWR);
+ if(fd >= 0) {
+ if(ioctl(fd, _IOC(_IOC_READ, 'P', 6, 8), dev->vidpid) == 0) {
+ DBG(4, "%s: dev %s has vid/pid %x:%x\n", __func__, dev->path, dev->vidpid[0], dev->vidpid[1]);
+ ++ndevices;
+ }
+ close(fd);
+ }
+ }
+ }
+}
+/* SANE API ignores return code of this callback */
+SANE_Status
+usblp_configure_device(const char *devname, SANE_Status(*attach)(const char *dev))
+{
+ int vid=0, pid=0;
+ int i;
+ sscanf(devname, "usblp %x %x", &vid, &pid);
+ DBG(3, "%s: open %s (%x %x)\n", __func__, devname, vid, pid);
+
+ usblp_scan();
+
+ for(i=0;i<ndevices;i++) {
+ if(devices[i].vidpid[0] == vid && devices[i].vidpid[1] == pid)
+ attach(devices[i].path);
+ }
+ return SANE_STATUS_GOOD;
+}
+
+
+/* xerox_mfp-usblp.c */
diff --git a/backend/xerox_mfp.c b/backend/xerox_mfp.c
index edd6e6409..359c20bc7 100644
--- a/backend/xerox_mfp.c
+++ b/backend/xerox_mfp.c
@@ -44,10 +44,11 @@
static const SANE_Device **devlist = NULL; /* sane_get_devices array */
static struct device *devices_head = NULL; /* sane_get_devices list */
-enum { TRANSPORT_USB, TRANSPORT_TCP, TRANSPORTS_MAX };
+enum { TRANSPORT_USB, TRANSPORT_TCP, TRANSPORT_USBLP, TRANSPORTS_MAX };
transport available_transports[TRANSPORTS_MAX] = {
{ "usb", usb_dev_request, usb_dev_open, usb_dev_close, usb_configure_device },
{ "tcp", tcp_dev_request, tcp_dev_open, tcp_dev_close, tcp_configure_device },
+ { "usblp", usblp_dev_request, usblp_dev_open, usblp_dev_close, usblp_configure_device },
};
static int resolv_state(int state)
@@ -223,6 +224,11 @@ static int isSupportedDevice(struct device __sane_unused__ *dev)
#endif
}
+static int isBuggyLexmark(struct device *dev)
+{
+ return strcmp(dev->sane.model, "SAMSUNG/ORION");
+}
+
static void dbg_dump(struct device *dev)
{
int i;
@@ -710,6 +716,9 @@ static int fix_window(struct device *dev)
else
dev->max_len = dev->max_len_adf;
+ if(dev->max_len == 0)
+ dev->max_len = dev->max_win_len;
+
/* parameters */
dev->win_y_range.max = SANE_FIX((double)dev->max_len / PNT_PER_MM);
@@ -979,6 +988,8 @@ static transport *tr_from_devname(SANE_String_Const devname)
{
if (strncmp("tcp", devname, 3) == 0)
return &available_transports[TRANSPORT_TCP];
+ if (strncmp("usblp", devname, 5) == 0 || devname[0]=='/')
+ return &available_transports[TRANSPORT_USBLP];
return &available_transports[TRANSPORT_USB];
}
@@ -1446,8 +1457,10 @@ sane_start(SANE_Handle h)
(dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
return dev_stop(dev);
- if (!dev_cmd_wait(dev, CMD_OBJECT_POSITION))
- return dev_stop(dev);
+ if(!isBuggyLexmark(dev)) {
+ if (!dev_cmd_wait(dev, CMD_OBJECT_POSITION))
+ return dev_stop(dev);
+ }
if (!dev_cmd(dev, CMD_READ) ||
(dev->state && dev->state != SANE_STATUS_DEVICE_BUSY))
diff --git a/backend/xerox_mfp.conf.in b/backend/xerox_mfp.conf.in
index 4fcbeb6f5..48b777557 100644
--- a/backend/xerox_mfp.conf.in
+++ b/backend/xerox_mfp.conf.in
@@ -257,3 +257,10 @@ usb 0x413c 0x5124
#Dell 1235cn (clone of Samsung CLX-3175)
usb 0x413c 0x5310
+
+######################
+### Lexmark Models ###
+######################
+
+#Lexmark X215 is routed through the "printing" usblp interface
+usblp 0x043d 0x0085
diff --git a/backend/xerox_mfp.h b/backend/xerox_mfp.h
index d85fe1451..21164019f 100644
--- a/backend/xerox_mfp.h
+++ b/backend/xerox_mfp.h
@@ -143,6 +143,13 @@ SANE_Status usb_dev_open(struct device *dev);
void usb_dev_close(struct device *dev);
SANE_Status usb_configure_device(const char *devname, SANE_Status(*cb)(SANE_String_Const devname));
+/* USB-LP transport */
+int usblp_dev_request(struct device *dev, SANE_Byte *cmd, size_t cmdlen, SANE_Byte *resp, size_t *resplen);
+SANE_Status usblp_dev_open(struct device *dev);
+void usblp_dev_close(struct device *dev);
+SANE_Status usblp_configure_device(const char *devname, SANE_Status(*cb)(SANE_String_Const devname));
+
+
/* TCP unicast */
int tcp_dev_request(struct device *dev, SANE_Byte *cmd, size_t cmdlen, SANE_Byte *resp, size_t *resplen);
SANE_Status tcp_dev_open(struct device *dev);
--
2.17.1