kopia lustrzana https://gitlab.com/sane-project/website
812 wiersze
28 KiB
HTML
812 wiersze
28 KiB
HTML
<!-- received="Mon Sep 20 13:30:22 1999 PDT" -->
|
||
<!-- sent="Mon, 20 Sep 1999 22:39:35 +0200" -->
|
||
<!-- name="abel deuring" -->
|
||
<!-- email="a.deuring@satzbau-gmbh.de" -->
|
||
<!-- subject="Re: SG_BIG_BUFF, glibc 2.1 weirdness ..." -->
|
||
<!-- id="" -->
|
||
<!-- inreplyto="SG_BIG_BUFF, glibc 2.1 weirdness ..." -->
|
||
<title>sane-devel: Re: SG_BIG_BUFF, glibc 2.1 weirdness ...</title>
|
||
<h1>Re: SG_BIG_BUFF, glibc 2.1 weirdness ...</h1>
|
||
<b>abel deuring</b> (<a href="mailto:a.deuring@satzbau-gmbh.de"><i>a.deuring@satzbau-gmbh.de</i></a>)<br>
|
||
<i>Mon, 20 Sep 1999 22:39:35 +0200</i>
|
||
<p>
|
||
<ul>
|
||
<li> <b>Messages sorted by:</b> <a href="date.html#127">[ date ]</a><a href="index.html#127">[ thread ]</a><a href="subject.html#127">[ subject ]</a><a href="author.html#127">[ author ]</a>
|
||
<!-- next="start" -->
|
||
<li> <b>Next message:</b> <a href="0128.html">Hugo.van.der.Kooij@caiw.nl: "Re: Error device I/O"</a>
|
||
<li> <b>Previous message:</b> <a href="0126.html">Oliver Rauch: "Re: Error device I/O"</a>
|
||
<!-- nextthread="start" -->
|
||
<!-- reply="end" -->
|
||
</ul>
|
||
<!-- body="start" -->
|
||
Hi all!<br>
|
||
<p>
|
||
At the end of the mail are some modifications to sanei_scsi.c to<br>
|
||
get a "better cooperation" with the new Linux SG driver.<br>
|
||
While this driver has been introduced in kernel version 2.2.6,<br>
|
||
SG version 2.1.34 or newer is required in order to<br>
|
||
take advantage of these modifications (SG 2.1.34 is part of the<br>
|
||
kernels 2.2.10 .. 2.2.12) Compilation and usage of the modifiled<br>
|
||
sanei_scsi.c with older SG drivers and kernels is of course<br>
|
||
possible as before, but the new functionality will not be available.<br>
|
||
On the other hand, there should not be any problem to use the SG<br>
|
||
driver 2.1.34 or 2.1.35 with kernel version 2.2.6 or newer.<br>
|
||
(Don't ask me about older kernels...) You can download the SG<br>
|
||
driver at <a href="http://www.torque.net/sg">http://www.torque.net/sg</a>.<br>
|
||
<p>
|
||
<p>
|
||
<p>
|
||
1. It is possible to set the buffer size for SCSI commands with<br>
|
||
the environment variable SANE_SG_BUFFERSIZE. The default <br>
|
||
buffersize can be changed with the configure option <br>
|
||
--enable-scsibuffersize=nnn. The latter may be useful if one <br>
|
||
wants to change the "default default" buffersize of 128 kB <br>
|
||
for saned. (Thanks to Andy for suggesting the configure option.)<br>
|
||
<p>
|
||
Defining a too big buffer size is often not such a good idea,<br>
|
||
because the kernel may need to swap other data in order to<br>
|
||
allocate the SG buffer. This can lead to a performance<br>
|
||
degradation.<br>
|
||
<p>
|
||
If the frontend fails for a large value in<br>
|
||
SANE_SG_BUFFERSIZE, set the environment variable<br>
|
||
SANE_DEBUG_SANEI_SCSI=1, and check, if you get the message<br>
|
||
"sanei_scsi_open: could not allocate SG buffer memory. <br>
|
||
wanted: 10000000 got: 2500096". (As you can see from this <br>
|
||
example, several megabytes are really too much :)<br>
|
||
<p>
|
||
In this case, you should decrease the value for SANE_SG_BUFFERSIZE.<br>
|
||
<p>
|
||
2. If you have the SG driver 2.1.35 installed, a backend can use<br>
|
||
the command queueing offered by the SG driver. This affects<br>
|
||
only the HP, Mustek, Microtek (not Microtek2) and Sharp<br>
|
||
backends. The usage of this "lower level" queueing avoids under<br>
|
||
certain conditions (higher scan resolution, additional<br>
|
||
"traffic" on the SCSI bus) many carriage stops for the<br>
|
||
Sharp JX250. Perhaps the improved queueing avoids some carriage <br>
|
||
stops for other scanners too.<br>
|
||
<p>
|
||
<p>
|
||
Technical details:<br>
|
||
<p>
|
||
The main compatibility problem between the new SG driver and<br>
|
||
sanei_scsi.c is that the new SG driver does not guarantee to allocate<br>
|
||
a buffer of the size specified with the SG_SET_RESERVED_SIZE<br>
|
||
ioctl call. It can theoretically even happen that there is no<br>
|
||
buffer at all (see<br>
|
||
<a href="http://www.torque.net/sg/p/scsi-generic_long.txt">http://www.torque.net/sg/p/scsi-generic_long.txt</a> for details).<br>
|
||
<p>
|
||
This means especially that there is no guarantee that two file<br>
|
||
handles will have the same buffer size: The SG driver allocates<br>
|
||
separate buffers for each file handle. Therefore, the static<br>
|
||
variable sanei_scsi_max_request_size becomes less useful.<br>
|
||
<p>
|
||
<p>
|
||
Andreas Beck made two suggestions to solve these problem:<br>
|
||
<p>
|
||
1. to define a second function<br>
|
||
<p>
|
||
sanei_scsi_open_extended(const char *dev, int *fdp,<br>
|
||
SANEI_SCSI_Sense_Handler handler,<br>
|
||
void *handler_arg, int *buffersize)<br>
|
||
<p>
|
||
This function tries to allocate a SG buffer of size *buffersize;<br>
|
||
the actual buffer size allocated by the SG driver is written into<br>
|
||
*buffersize on return. Please note that the SG driver does not<br>
|
||
guarantee to allocated even one page (4 kB on most platforms) of<br>
|
||
memory. Thus it is up to the backend to decide if the buffer is<br>
|
||
large enough.<br>
|
||
<p>
|
||
2. to define sanei_scsi_open as a "wrapper function" for<br>
|
||
sanei_scsi_open-extended. sanei_scsi_open can then ensure that<br>
|
||
sanei_scsi_max_request_size is not increased if a second file<br>
|
||
handle is opened.<br>
|
||
<p>
|
||
While I implemented Andy's suggestion for a wrapper function, I<br>
|
||
decided against the logic of setting an upper limit for<br>
|
||
sanei_scsi_max_request_size in a second open. Several<br>
|
||
backends start a separate reader process, and if a call to<br>
|
||
sanei_scsi_open decreasing sanei_scsi_max_request_size happens,<br>
|
||
while a reader process is running, the reader process would use<br>
|
||
the old value. (There are even more situations, where a backend <br>
|
||
might crash, if sanei_scsi_max_request_size changes.)<br>
|
||
<p>
|
||
Therefore, sanei_scsi_open ensures that the SG driver allocates <br>
|
||
exactly as much memory as requested, else it fails with <br>
|
||
SANE_STATUS_NO_MEM.<br>
|
||
<p>
|
||
<p>
|
||
Further, I changed sanei_scsi_req_flush_all. If a frontend<br>
|
||
attaches to two scanners, the old implementation would flush the<br>
|
||
commands sent to both scanners. Thus, I wrote another function<br>
|
||
sanei_scsi_req_flush_all_extended(int fd), which flushes the<br>
|
||
commands for only one file handle.<br>
|
||
<p>
|
||
sanei_scsi_req_flush_all is now a wrapper for the new function,<br>
|
||
and flushes the commands for a file handle, if there is only one<br>
|
||
opened file handle. If two or more file handles are open, the<br>
|
||
function bails out in an assert.<br>
|
||
<p>
|
||
<p>
|
||
The modifications to the queueing implementation do not affect<br>
|
||
the behaviour of sanei_scsi_req_enter / sanei_scsi_req_wait,<br>
|
||
so I don't explain them. Look into he source code if you are curious.<br>
|
||
<p>
|
||
<p>
|
||
I know that especially the introduction of sanei_scsi_open_extended<br>
|
||
can have a relatively big impact on the backends. The Sharp backend<br>
|
||
for example needs some cleanup regarding the time, at which it<br>
|
||
allocates a buffer. (For my tests of the new functions, I<br>
|
||
used some workaround. And I found an "asymmetry" in the backend<br>
|
||
regarding the functions which call sanei_scsi_open and<br>
|
||
sanei_scsi_close...) <br>
|
||
<p>
|
||
But the improved capabilities of the new SG driver are worth the<br>
|
||
changes, I think. And, frankly speaking, I don't think that<br>
|
||
global variables like sanei_scsi_max_request_size are the best<br>
|
||
invention since sliced bread, Let's get rid of it.<br>
|
||
<p>
|
||
<p>
|
||
Thanks to Douglas Gilbert and Andy Beck for answering many<br>
|
||
questions and giving many suggestions.<br>
|
||
<p>
|
||
Abel<br>
|
||
<p>
|
||
--- sanei_scsi.c-orig Sat Jul 3 16:59:46 1999<br>
|
||
+++ sanei_scsi.c Mon Sep 20 20:58:20 1999<br>
|
||
@@ -194,6 +194,76 @@<br>
|
||
#endif<br>
|
||
<br>
|
||
int sanei_scsi_max_request_size = MAX_DATA;<br>
|
||
+#if USE == LINUX_INTERFACE<br>
|
||
+/* the following #defines follow Douglas Gilbert's sample code<br>
|
||
+ to maintain run time compatibility with the old and the<br>
|
||
+ new SG driver for Linux<br>
|
||
+*/<br>
|
||
+#ifndef SG_SET_COMMAND_Q<br>
|
||
+#define SG_SET_COMMAND_Q 0x2271<br>
|
||
+#endif<br>
|
||
+#ifndef SG_SET_RESERVED_SIZE<br>
|
||
+#define SG_SET_RESERVED_SIZE 0x2275<br>
|
||
+#endif <br>
|
||
+#ifndef SG_GET_RESERVED_SIZE<br>
|
||
+#define SG_GET_RESERVED_SIZE 0x2272<br>
|
||
+#endif <br>
|
||
+#ifndef SG_GET_SCSI_ID<br>
|
||
+#define SG_GET_SCSI_ID 0x2276<br>
|
||
+#endif <br>
|
||
+#ifndef SG_GET_VERSION_NUM<br>
|
||
+#define SG_GET_VERSION_NUM 0x2282<br>
|
||
+#endif<br>
|
||
+<br>
|
||
+#ifndef SCSIBUFFERSIZE<br>
|
||
+#define SCSIBUFFERSIZE (128 * 1024)<br>
|
||
+#endif<br>
|
||
+<br>
|
||
+/* the struct returned by the SG ioctl call SG_GET_SCSI_ID changed<br>
|
||
+ from version 2.1.34 to 2.1.35, and we need the informations from<br>
|
||
+ the field s_queue_depth, which was introduced in 2.1.35.<br>
|
||
+ To get this file compiling also with older versions of sg.h, the <br>
|
||
+ struct is re-defined here.<br>
|
||
+*/<br>
|
||
+typedef struct xsg_scsi_id {<br>
|
||
+ int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2<br>
|
||
etc */<br>
|
||
+ int channel;<br>
|
||
+ int scsi_id; /* scsi id of target device */<br>
|
||
+ int lun;<br>
|
||
+ int scsi_type; /* TYPE_... defined in scsi/scsi.h */<br>
|
||
+ short h_cmd_per_lun;/* host (adapter) maximum commands per lun */<br>
|
||
+ short d_queue_depth;/* device (or adapter) maximum queue length */<br>
|
||
+ int unused1; /* probably find a good use, set 0 for now */<br>
|
||
+ int unused2; /* ditto */<br>
|
||
+} SG_scsi_id;<br>
|
||
+<br>
|
||
+typedef struct req<br>
|
||
+ {<br>
|
||
+ struct req *next;<br>
|
||
+ int fd;<br>
|
||
+ u_int running:1, done:1;<br>
|
||
+ SANE_Status status;<br>
|
||
+ size_t *dst_len;<br>
|
||
+ void *dst;<br>
|
||
+ struct<br>
|
||
+ {<br>
|
||
+ struct sg_header hdr;<br>
|
||
+ /* Make sure this is the last element, the real size is<br>
|
||
+ SG_BIG_BUFF and machine dependant */<br>
|
||
+ u_int8_t data[1];<br>
|
||
+ }<br>
|
||
+ cdb;<br>
|
||
+ }<br>
|
||
+req;<br>
|
||
+<br>
|
||
+typedef struct Fdparms <br>
|
||
+ {<br>
|
||
+ int sg_queue_used, sg_queue_max;<br>
|
||
+ req *sane_qhead, *sane_qtail, *sane_free_list;<br>
|
||
+ }<br>
|
||
+fdparms;<br>
|
||
+<br>
|
||
+#endif<br>
|
||
<br>
|
||
#if USE == FREEBSD_CAM_INTERFACE<br>
|
||
# define CAM_MAXDEVS 128<br>
|
||
@@ -590,12 +660,25 @@<br>
|
||
<br>
|
||
#endif /* USE_OS2_INTERFACE */<br>
|
||
<br>
|
||
+static int num_alloced = 0;<br>
|
||
+<br>
|
||
+#if USE == LINUX_INTERFACE<br>
|
||
+<br>
|
||
+SANE_Status<br>
|
||
+sanei_scsi_open_extended (const char *dev, int *fdp,<br>
|
||
+ SANEI_SCSI_Sense_Handler handler, <br>
|
||
+ void *handler_arg, int *buffersize)<br>
|
||
+<br>
|
||
+#else<br>
|
||
+<br>
|
||
SANE_Status<br>
|
||
sanei_scsi_open (const char *dev, int *fdp,<br>
|
||
SANEI_SCSI_Sense_Handler handler, void *handler_arg)<br>
|
||
+<br>
|
||
+#endif<br>
|
||
+<br>
|
||
{<br>
|
||
u_int bus = 0, target = 0, lun = 0, fake_fd = 0;<br>
|
||
- static int num_alloced = 0;<br>
|
||
char *real_dev = 0;<br>
|
||
void *pdata = 0;<br>
|
||
int fd;<br>
|
||
@@ -621,6 +704,7 @@<br>
|
||
sanei_scsi_max_request_size = atoi (buf);<br>
|
||
DBG (1, "sanei_scsi_open: sanei_scsi_max_request_size=%d bytes\n",<br>
|
||
sanei_scsi_max_request_size);<br>
|
||
+ close(fd);<br>
|
||
}<br>
|
||
}<br>
|
||
#endif<br>
|
||
@@ -913,6 +997,109 @@<br>
|
||
}<br>
|
||
}<br>
|
||
#endif /* SGIOCSTL */<br>
|
||
+#if USE == LINUX_INTERFACE<br>
|
||
+ {<br>
|
||
+ SG_scsi_id sid;<br>
|
||
+ int ioctl_val, sg_version;<br>
|
||
+ int real_buffersize;<br>
|
||
+ fdparms *fdpa = 0;<br>
|
||
+ <br>
|
||
+ pdata = fdpa = malloc(sizeof(fdparms));<br>
|
||
+ if (!pdata)<br>
|
||
+ {<br>
|
||
+ close(fd);<br>
|
||
+ return SANE_STATUS_NO_MEM;<br>
|
||
+ }<br>
|
||
+ memset(fdpa, 0, sizeof(fdparms));<br>
|
||
+ /* default: allow only one command to be sent to the SG driver <br>
|
||
+ */<br>
|
||
+ fdpa->sg_queue_max = 1;<br>
|
||
+<br>
|
||
+ /* Try to read the SG version. If the ioctl call is successful, <br>
|
||
+ we have the new SG driver, and we can increase the buffer size<br>
|
||
+ using another ioctl call. <br>
|
||
+ If we have SG version 2.1.35 or above, we can additionally<br>
|
||
enable<br>
|
||
+ command queueing.<br>
|
||
+ */<br>
|
||
+ if (0 == ioctl(fd, SG_GET_VERSION_NUM, &sg_version))<br>
|
||
+ {<br>
|
||
+ DBG(1, "sanei_scsi_open: SG driver version: %i\n", sg_version);<br>
|
||
+<br>
|
||
+ /* try to reserve a SG buffer of the size specified by<br>
|
||
*buffersize<br>
|
||
+ */<br>
|
||
+ ioctl(fd, SG_SET_RESERVED_SIZE, buffersize);<br>
|
||
+<br>
|
||
+ /* the set call may not be able to allocate as much memory<br>
|
||
+ as requested, thus we read the actual buffer size.<br>
|
||
+<br>
|
||
+ NOTE: sanei_scsi_max_request_size is a global variable<br>
|
||
+ used for all devices/file handles, while version 2.0 and <br>
|
||
+ above of the SG driver allocate buffer memory for each <br>
|
||
+ opened file separately. Therefore, we have a possible <br>
|
||
+ inconsistency, if more than one file is opened and<br>
|
||
+ if the SG_GET_RESERVED_SIZE return different buffer sizes<br>
|
||
+ for different file handles. (See Douglas Gilbert's<br>
|
||
+ description of the SG driver for details:<br>
|
||
+ <a href="http://www.torque.net/sg/p/scsi-generic_long.txt">http://www.torque.net/sg/p/scsi-generic_long.txt</a>)<br>
|
||
+ <br>
|
||
+ For this reason, sanei_scsi_open does not allow to open <br>
|
||
+ two or more file handles simultaneously.<br>
|
||
+ */<br>
|
||
+ if (0 == ioctl(fd, SG_GET_RESERVED_SIZE, &real_buffersize))<br>
|
||
+ {<br>
|
||
+ /* if we got more memory than requested, we stick with<br>
|
||
+ with the requested value, in order to allow<br>
|
||
+ sanei_scsi_open to check the buffer size exactly.<br>
|
||
+ */<br>
|
||
+ if (real_buffersize > *buffersize)<br>
|
||
+ {<br>
|
||
+ sanei_scsi_max_request_size = *buffersize;<br>
|
||
+ }<br>
|
||
+ else<br>
|
||
+ {<br>
|
||
+ sanei_scsi_max_request_size = real_buffersize;<br>
|
||
+ *buffersize = real_buffersize;<br>
|
||
+ }<br>
|
||
+ }<br>
|
||
+ else <br>
|
||
+ {<br>
|
||
+ DBG(1, "sanei_scsi_open: cannot read SG buffer size -<br>
|
||
%s\n", <br>
|
||
+ strerror(errno));<br>
|
||
+ close(fd);<br>
|
||
+ return SANE_STATUS_NO_MEM;<br>
|
||
+ }<br>
|
||
+ DBG(1, "sanei_scsi_open_extended: using %i bytes as SCSI<br>
|
||
buffer\n", <br>
|
||
+ sanei_scsi_max_request_size);<br>
|
||
+<br>
|
||
+ if (sg_version >= 20135)<br>
|
||
+ {<br>
|
||
+ DBG(1, "trying to enable low level command queueing\n");<br>
|
||
+ <br>
|
||
+ if (0 == ioctl(fd, SG_GET_SCSI_ID, &sid)) <br>
|
||
+ { <br>
|
||
+ DBG(1, "sanei_scsi_open: Host adapter queue depth:<br>
|
||
%i\n",<br>
|
||
+ sid.d_queue_depth);<br>
|
||
+ <br>
|
||
+ ioctl_val = 1;<br>
|
||
+ if(0 == ioctl(fd, SG_SET_COMMAND_Q, &ioctl_val)) <br>
|
||
+ {<br>
|
||
+ fdpa->sg_queue_max = sid.d_queue_depth;<br>
|
||
+ if (fdpa->sg_queue_max <= 0)<br>
|
||
+ fdpa->sg_queue_max = 1;<br>
|
||
+ }<br>
|
||
+ }<br>
|
||
+ }<br>
|
||
+ }<br>
|
||
+ else<br>
|
||
+ {<br>
|
||
+ /* we have the old SG driver: */<br>
|
||
+ if (sanei_scsi_max_request_size < *buffersize)<br>
|
||
+ *buffersize = sanei_scsi_max_request_size;<br>
|
||
+ }<br>
|
||
+ if (fdpa->sg_queue_max > 1)<br>
|
||
+ DBG(1, "sanei_scsi_open: low level command queueing enabled\n");<br>
|
||
+ }<br>
|
||
+#endif /* LINUX_INTERFACE */<br>
|
||
#endif /* !DECUNIX_INTERFACE */<br>
|
||
<br>
|
||
if (fd >= num_alloced)<br>
|
||
@@ -958,9 +1145,78 @@<br>
|
||
return SANE_STATUS_GOOD;<br>
|
||
}<br>
|
||
<br>
|
||
+#if USE == LINUX_INTERFACE<br>
|
||
+/* The "wrapper" for the old open call */<br>
|
||
+SANE_Status<br>
|
||
+sanei_scsi_open (const char *dev, int *fdp,<br>
|
||
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)<br>
|
||
+{<br>
|
||
+ int i, j = 0;<br>
|
||
+ int wanted_buffersize = SCSIBUFFERSIZE, real_buffersize;<br>
|
||
+ SANE_Status res;<br>
|
||
+ char *cc, *cc1;<br>
|
||
+<br>
|
||
+ cc = getenv("SANE_SG_BUFFERSIZE");<br>
|
||
+ if (cc)<br>
|
||
+ {<br>
|
||
+ i = strtol(cc, &cc1, 10);<br>
|
||
+ if (cc != cc1 && i >= 32768)<br>
|
||
+ wanted_buffersize = i;<br>
|
||
+ }<br>
|
||
+<br>
|
||
+ real_buffersize = wanted_buffersize;<br>
|
||
+ res = sanei_scsi_open_extended(dev, fdp, handler, handler_arg, <br>
|
||
+ &real_buffersize);<br>
|
||
+<br>
|
||
+ /* make sure that we got as much memory as we wanted, otherwise<br>
|
||
+ the backend might be confused<br>
|
||
+ */<br>
|
||
+ if (real_buffersize != wanted_buffersize)<br>
|
||
+ {<br>
|
||
+ DBG(1, "sanei_scsi_open: could not allocate SG buffer memory "<br>
|
||
+ "wanted: %i got: %i\n", wanted_buffersize, real_buffersize);<br>
|
||
+ sanei_scsi_close(*fdp);<br>
|
||
+ return SANE_STATUS_NO_MEM;<br>
|
||
+ }<br>
|
||
+<br>
|
||
+ return res;<br>
|
||
+}<br>
|
||
+#else<br>
|
||
+/* dummy for the proposed new open call */<br>
|
||
+sanei_scsi_open_extended (const char *dev, int *fdp,<br>
|
||
+ SANEI_SCSI_Sense_Handler handler, <br>
|
||
+ void *handler_arg, int *buffersize)<br>
|
||
+{<br>
|
||
+ SANE_Status res;<br>
|
||
+ res = sanei_scsi_open(dev, fdp, handler, handler_arg);<br>
|
||
+ if (sanei_scsi_max_request_size < *buffersize)<br>
|
||
+ *buffersize = sanei_scsi_max_request_size;<br>
|
||
+ return res;<br>
|
||
+}<br>
|
||
+#endif<br>
|
||
+<br>
|
||
void<br>
|
||
sanei_scsi_close (int fd)<br>
|
||
{<br>
|
||
+#if USE == LINUX_INTERFACE<br>
|
||
+ if (fd_info[fd].pdata) <br>
|
||
+ {<br>
|
||
+ req *req, *next_req;<br>
|
||
+ <br>
|
||
+ /* make sure that there are no pending SCSI calls */<br>
|
||
+ sanei_scsi_req_flush_all_extended(fd);<br>
|
||
+<br>
|
||
+ req = ((fdparms*) fd_info[fd].pdata)->sane_free_list;<br>
|
||
+ while (req) <br>
|
||
+ {<br>
|
||
+ next_req = req->next;<br>
|
||
+ free(req);<br>
|
||
+ req = next_req;<br>
|
||
+ }<br>
|
||
+ free(fd_info[fd].pdata);<br>
|
||
+ }<br>
|
||
+#endif<br>
|
||
+<br>
|
||
fd_info[fd].in_use = 0;<br>
|
||
fd_info[fd].sense_handler = 0;<br>
|
||
fd_info[fd].sense_handler_arg = 0;<br>
|
||
@@ -1225,69 +1481,133 @@<br>
|
||
} \<br>
|
||
while (0)<br>
|
||
<br>
|
||
-static struct req<br>
|
||
- {<br>
|
||
- struct req *next;<br>
|
||
- int fd;<br>
|
||
- u_int running:1, done:1;<br>
|
||
- SANE_Status status;<br>
|
||
- size_t *dst_len;<br>
|
||
- void *dst;<br>
|
||
- struct<br>
|
||
- {<br>
|
||
- struct sg_header hdr;<br>
|
||
- /* Make sure this is the last element, the real size is<br>
|
||
- SG_BIG_BUFF and machine dependant */<br>
|
||
- u_int8_t data[1];<br>
|
||
- }<br>
|
||
- cdb;<br>
|
||
- }<br>
|
||
-*qhead, *qtail, *free_list;<br>
|
||
-<br>
|
||
static void<br>
|
||
issue (struct req *req)<br>
|
||
{<br>
|
||
ssize_t nwritten;<br>
|
||
+ fdparms *fdp;<br>
|
||
+ struct req *rp;<br>
|
||
+ int retries;<br>
|
||
<br>
|
||
- if (!req || req->running)<br>
|
||
+ if (!req)<br>
|
||
return;<br>
|
||
-<br>
|
||
+ fdp = (fdparms*) fd_info[req->fd].pdata;<br>
|
||
DBG (4, "sanei_scsi.issue: %p\n", req);<br>
|
||
<br>
|
||
- ATOMIC (req->running = 1;<br>
|
||
- nwritten = write (req->fd, &req->cdb, req->cdb.hdr.pack_len));<br>
|
||
-<br>
|
||
- if (nwritten != req->cdb.hdr.pack_len)<br>
|
||
- {<br>
|
||
- DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",<br>
|
||
- strerror (errno));<br>
|
||
- req->done = 1;<br>
|
||
- if (errno == ENOMEM)<br>
|
||
- {<br>
|
||
- DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "<br>
|
||
- "Check file PROBLEMS.\n");<br>
|
||
- req->status = SANE_STATUS_NO_MEM;<br>
|
||
- }<br>
|
||
+ rp = fdp->sane_qhead;<br>
|
||
+ while (rp && rp->running)<br>
|
||
+ rp = rp->next;<br>
|
||
+<br>
|
||
+ while (rp && fdp->sg_queue_used < fdp->sg_queue_max)<br>
|
||
+ {<br>
|
||
+ retries = 20;<br>
|
||
+ while (retries)<br>
|
||
+ {<br>
|
||
+ ATOMIC (rp->running = 1;<br>
|
||
+ nwritten = write (rp->fd, &rp->cdb,<br>
|
||
rp->cdb.hdr.pack_len);<br>
|
||
+ if (nwritten != rp->cdb.hdr.pack_len)<br>
|
||
+ {<br>
|
||
+ /* ENOMEM can easily happen, if both command<br>
|
||
queueing<br>
|
||
+ inside the SG driver and large buffers are<br>
|
||
used.<br>
|
||
+ Therefore, if ENOMEM does not occur for the<br>
|
||
first <br>
|
||
+ command in the queue, we can simply try to<br>
|
||
issue<br>
|
||
+ it later again.<br>
|
||
+ */<br>
|
||
+ if ( errno == EAGAIN <br>
|
||
+ || (errno == ENOMEM && rp !=<br>
|
||
fdp->sane_qhead))<br>
|
||
+ {<br>
|
||
+ /* don't try to send the data again, but<br>
|
||
+ wait for the next call to issue()<br>
|
||
+ */<br>
|
||
+ rp->running = 0;<br>
|
||
+ }<br>
|
||
+ }<br>
|
||
+ );<br>
|
||
+ if (rp == fdp->sane_qhead && errno == EAGAIN)<br>
|
||
+ {<br>
|
||
+ retries--;<br>
|
||
+ usleep(10000);<br>
|
||
+ }<br>
|
||
+ else<br>
|
||
+ retries = 0;<br>
|
||
+ }<br>
|
||
+<br>
|
||
+ if (nwritten != rp->cdb.hdr.pack_len)<br>
|
||
+ {<br>
|
||
+ if (rp->running)<br>
|
||
+ {<br>
|
||
+ DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",<br>
|
||
+ strerror (errno));<br>
|
||
+ rp->done = 1;<br>
|
||
+ if (errno == ENOMEM)<br>
|
||
+ {<br>
|
||
+ DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "<br>
|
||
+ "Check file PROBLEMS.\n");<br>
|
||
+ rp->status = SANE_STATUS_NO_MEM;<br>
|
||
+ }<br>
|
||
+ else<br>
|
||
+ rp->status = SANE_STATUS_IO_ERROR;<br>
|
||
+ }<br>
|
||
+ else<br>
|
||
+ {<br>
|
||
+ if (errno == ENOMEM)<br>
|
||
+ DBG(1, "issue: ENOMEM - cannot queue SCSI command. "<br>
|
||
+ "Trying again later.\n");<br>
|
||
+ else<br>
|
||
+ DBG(1, "issue: cannot queue SCSI command. "<br>
|
||
+ "Trying again later.\n");<br>
|
||
+ }<br>
|
||
+ break; /* in case of an error don't try to queue more<br>
|
||
commands */<br>
|
||
+ }<br>
|
||
else<br>
|
||
- req->status = SANE_STATUS_IO_ERROR;<br>
|
||
+ fdp->sg_queue_used++;<br>
|
||
+ rp = rp->next;<br>
|
||
}<br>
|
||
}<br>
|
||
<br>
|
||
void<br>
|
||
-sanei_scsi_req_flush_all (void)<br>
|
||
+sanei_scsi_req_flush_all_extended (int fd)<br>
|
||
{<br>
|
||
+ fdparms *fdp;<br>
|
||
struct req *req, *next_req;<br>
|
||
<br>
|
||
- for (req = qhead; req; req = next_req)<br>
|
||
+ fdp = (fdparms*) fd_info[fd].pdata;<br>
|
||
+ for (req = fdp->sane_qhead; req; req = next_req)<br>
|
||
{<br>
|
||
if (req->running && !req->done)<br>
|
||
- read (req->fd, &req->cdb, req->cdb.hdr.reply_len);<br>
|
||
+ {<br>
|
||
+ read (fd, &req->cdb, req->cdb.hdr.reply_len);<br>
|
||
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;<br>
|
||
+ }<br>
|
||
next_req = req->next;<br>
|
||
<br>
|
||
- req->next = free_list;<br>
|
||
- free_list = req;<br>
|
||
+ req->next = fdp->sane_free_list;<br>
|
||
+ fdp->sane_free_list = req;<br>
|
||
}<br>
|
||
- qhead = qtail = 0;<br>
|
||
+ fdp->sane_qhead = fdp->sane_qtail = 0;<br>
|
||
+}<br>
|
||
+<br>
|
||
+void<br>
|
||
+sanei_scsi_req_flush_all ()<br>
|
||
+{<br>
|
||
+ int fd, i, j = 0;<br>
|
||
+ <br>
|
||
+ /* sanei_scsi_open allows only one open file handle, so we<br>
|
||
+ can simply look for the first entry where in_use is set<br>
|
||
+ */<br>
|
||
+ +<br>
|
||
+ fd = num_alloced;<br>
|
||
+ for (i = 0; i < num_alloced; i++)<br>
|
||
+ if (fd_info[i].in_use)<br>
|
||
+ {<br>
|
||
+ j++;<br>
|
||
+ fd = i;<br>
|
||
+ }<br>
|
||
+ <br>
|
||
+ assert(j < 2);<br>
|
||
+ <br>
|
||
+ if (fd < num_alloced)<br>
|
||
+ sanei_scsi_req_flush_all_extended(fd);<br>
|
||
}<br>
|
||
<br>
|
||
SANE_Status<br>
|
||
@@ -1296,11 +1616,14 @@<br>
|
||
{<br>
|
||
struct req *req;<br>
|
||
size_t size;<br>
|
||
+ fdparms *fdp;<br>
|
||
+<br>
|
||
+ fdp = (fdparms*) fd_info[fd].pdata;<br>
|
||
<br>
|
||
- if (free_list)<br>
|
||
+ if (fdp->sane_free_list)<br>
|
||
{<br>
|
||
- req = free_list;<br>
|
||
- free_list = req->next;<br>
|
||
+ req = fdp->sane_free_list;<br>
|
||
+ fdp->sane_free_list = req->next;<br>
|
||
req->next = 0;<br>
|
||
}<br>
|
||
else<br>
|
||
@@ -1328,17 +1651,23 @@<br>
|
||
memcpy (&req->cdb.data, src, src_size);<br>
|
||
<br>
|
||
req->next = 0;<br>
|
||
- ATOMIC (if (qtail)<br>
|
||
+ ATOMIC (if (fdp->sane_qtail)<br>
|
||
{<br>
|
||
- qtail->next = req;<br>
|
||
- qtail = req;<br>
|
||
+ fdp->sane_qtail->next = req;<br>
|
||
+ fdp->sane_qtail = req;<br>
|
||
}<br>
|
||
else<br>
|
||
- qhead = qtail = req);<br>
|
||
+ fdp->sane_qhead = fdp->sane_qtail = req);<br>
|
||
<br>
|
||
DBG (4, "scsi_req_enter: entered %p\n", req);<br>
|
||
<br>
|
||
*idp = req;<br>
|
||
+ issue(req);<br>
|
||
+<br>
|
||
+ DBG(10, "scsi_req_enter: queue_used: %i, queue_max: %i\n",<br>
|
||
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_used,<br>
|
||
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_max);<br>
|
||
+<br>
|
||
return SANE_STATUS_GOOD;<br>
|
||
}<br>
|
||
<br>
|
||
@@ -1349,7 +1678,8 @@<br>
|
||
struct req *req = id;<br>
|
||
ssize_t nread = 0;<br>
|
||
<br>
|
||
- assert (req == qhead); /* we don't support out-of-order completion */<br>
|
||
+ /* we don't support out-of-order completion */<br>
|
||
+ assert (req == ((fdparms*)fd_info[req->fd].pdata)->sane_qhead);<br>
|
||
<br>
|
||
DBG (4, "sanei_scsi_req_wait: waiting for %p\n", req);<br>
|
||
<br>
|
||
@@ -1372,6 +1702,9 @@<br>
|
||
ATOMIC (nread = read (req->fd, &req->cdb,<br>
|
||
req->cdb.hdr.reply_len);<br>
|
||
req->done = 1);<br>
|
||
<br>
|
||
+ if (fd_info[req->fd].pdata)<br>
|
||
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;<br>
|
||
+<br>
|
||
/* Now issue next command asap, if any. We can't do this<br>
|
||
earlier since the Linux kernel has space for just one big<br>
|
||
buffer. */<br>
|
||
@@ -1423,11 +1756,12 @@<br>
|
||
}<br>
|
||
<br>
|
||
/* dequeue and release processed request: */<br>
|
||
- ATOMIC (qhead = qhead->next;<br>
|
||
- if (!qhead)<br>
|
||
- qtail = 0;<br>
|
||
- req->next = free_list;<br>
|
||
- free_list = req);<br>
|
||
+ ATOMIC (((fdparms*) fd_info[req->fd].pdata)->sane_qhead <br>
|
||
+ = ((fdparms*) fd_info[req->fd].pdata)->sane_qhead->next;<br>
|
||
+ if (!((fdparms*) fd_info[req->fd].pdata)->sane_qhead)<br>
|
||
+ ((fdparms*) fd_info[req->fd].pdata)->sane_qtail = 0;<br>
|
||
+ req->next = ((fdparms*) fd_info[req->fd].pdata)->sane_free_list;<br>
|
||
+ ((fdparms*) fd_info[req->fd].pdata)->sane_free_list = req);<br>
|
||
return status;<br>
|
||
}<br>
|
||
<br>
|
||
<p>
|
||
--- sanei_scsi.h-orig Sat Sep 4 16:30:01 1999<br>
|
||
+++ sanei_scsi.h Mon Sep 20 21:05:35 1999<br>
|
||
@@ -48,6 +48,23 @@<br>
|
||
SANEI_SCSI_Sense_Handler sense_handler,<br>
|
||
void *sense_arg);<br>
|
||
<br>
|
||
+/* The extended open call allows a backend to ask for a specific<br>
|
||
+ buffer size. sanei_scsi_open tries to allocate a buffer of the<br>
|
||
+ size given by *buffersize upon entry to this function. If<br>
|
||
+ sanei_scsi_open_extended returns successfully, *buffersize<br>
|
||
+ contains the available buffer size. This value may be both<br>
|
||
+ smaller or larger than the value requested by the backend;<br>
|
||
+ it can even be zero. The backend must decide, if it got enough<br>
|
||
+ buffer memory to work.<br>
|
||
+ <br>
|
||
+ Note that the value of *buffersize may differ for different<br>
|
||
+ files.<br>
|
||
+*/<br>
|
||
+extern SANE_Status sanei_scsi_open_extended (<br>
|
||
+ const char * device_name, int * fd,<br>
|
||
+ SANEI_SCSI_Sense_Handler sense_handler, <br>
|
||
+ void *sense_arg, int *buffersize);<br>
|
||
+<br>
|
||
/* One or more scsi commands can be enqueued by calling req_enter().<br>
|
||
SRC is the pointer to the SCSI command and associated write data<br>
|
||
and SRC_SIZE is the length of the command and data. DST is a<br>
|
||
@@ -76,8 +93,13 @@<br>
|
||
const void * src, size_t src_size,<br>
|
||
void * dst, size_t * dst_size);<br>
|
||
<br>
|
||
-/* Flush all pending SCSI commands. */<br>
|
||
+/* Flush all pending SCSI commands. This function work only,<br>
|
||
+ if zero or one SCSI file handles are open.<br>
|
||
+*/<br>
|
||
extern void sanei_scsi_req_flush_all (void);<br>
|
||
+<br>
|
||
+/* Flush all SCSI commands pending for one handle */<br>
|
||
+extern void sanei_scsi_req_flush_all_extended (int fd);<br>
|
||
<br>
|
||
extern void sanei_scsi_close (int fd);<br>
|
||
<br>
|
||
--- configure.in-orig Sun Apr 4 00:44:56 1999<br>
|
||
+++ configure.in Mon Sep 20 20:41:44 1999<br>
|
||
@@ -192,10 +192,19 @@<br>
|
||
else<br>
|
||
DLL_PRELOAD=""<br>
|
||
fi<br>
|
||
+<br>
|
||
+AC_ARG_ENABLE(scsibuffersize, <br>
|
||
+ [ --enable-scsibuffersize=N <br>
|
||
+ specify the default size of the buffer for SCSI<br>
|
||
commands],<br>
|
||
+ [set_scsibuffersize="$enableval"], [set_scsibuffersize=131072])<br>
|
||
+CFLAGS="$CFLAGS -DSCSIBUFFERSIZE=$set_scsibuffersize"<br>
|
||
+echo "scsi buffersize: $set_scsibuffersize"<br>
|
||
+<br>
|
||
AC_SUBST(V_MAJOR)<br>
|
||
AC_SUBST(V_MINOR)<br>
|
||
AC_SUBST(V_REV)<br>
|
||
AC_SUBST(DLL_PRELOAD)<br>
|
||
+<br>
|
||
<br>
|
||
AC_OUTPUT([Makefile lib/Makefile sanei/Makefile frontend/Makefile<br>
|
||
japi/Makefile backend/Makefile include/Makefile doc/Makefile<br>
|
||
<p>
|
||
<pre>
|
||
--
|
||
Source code, list archive, and docs: <a href="http://www.mostang.com/sane/">http://www.mostang.com/sane/</a>
|
||
To unsubscribe: echo unsubscribe sane-devel | mail <a href="mailto:majordomo@mostang.com">majordomo@mostang.com</a>
|
||
</pre>
|
||
<!-- body="end" -->
|
||
<p>
|
||
<ul>
|
||
<!-- next="start" -->
|
||
<li> <b>Next message:</b> <a href="0128.html">Hugo.van.der.Kooij@caiw.nl: "Re: Error device I/O"</a>
|
||
<li> <b>Previous message:</b> <a href="0126.html">Oliver Rauch: "Re: Error device I/O"</a>
|
||
<!-- nextthread="start" -->
|
||
<!-- reply="end" -->
|
||
</ul>
|