Re: SG_BIG_BUFF, glibc 2.1 weirdness ...

abel deuring (a.deuring@satzbau-gmbh.de)
Mon, 20 Sep 1999 22:39:35 +0200

Hi all!

At the end of the mail are some modifications to sanei_scsi.c to
get a "better cooperation" with the new Linux SG driver.
While this driver has been introduced in kernel version 2.2.6,
SG version 2.1.34 or newer is required in order to
take advantage of these modifications (SG 2.1.34 is part of the
kernels 2.2.10 .. 2.2.12) Compilation and usage of the modifiled
sanei_scsi.c with older SG drivers and kernels is of course
possible as before, but the new functionality will not be available.
On the other hand, there should not be any problem to use the SG
driver 2.1.34 or 2.1.35 with kernel version 2.2.6 or newer.
(Don't ask me about older kernels...) You can download the SG
driver at http://www.torque.net/sg.

1. It is possible to set the buffer size for SCSI commands with
the environment variable SANE_SG_BUFFERSIZE. The default
buffersize can be changed with the configure option
--enable-scsibuffersize=nnn. The latter may be useful if one
wants to change the "default default" buffersize of 128 kB
for saned. (Thanks to Andy for suggesting the configure option.)

Defining a too big buffer size is often not such a good idea,
because the kernel may need to swap other data in order to
allocate the SG buffer. This can lead to a performance
degradation.

If the frontend fails for a large value in
SANE_SG_BUFFERSIZE, set the environment variable
SANE_DEBUG_SANEI_SCSI=1, and check, if you get the message
"sanei_scsi_open: could not allocate SG buffer memory.
wanted: 10000000 got: 2500096". (As you can see from this
example, several megabytes are really too much :)

In this case, you should decrease the value for SANE_SG_BUFFERSIZE.

2. If you have the SG driver 2.1.35 installed, a backend can use
the command queueing offered by the SG driver. This affects
only the HP, Mustek, Microtek (not Microtek2) and Sharp
backends. The usage of this "lower level" queueing avoids under
certain conditions (higher scan resolution, additional
"traffic" on the SCSI bus) many carriage stops for the
Sharp JX250. Perhaps the improved queueing avoids some carriage
stops for other scanners too.

Technical details:

The main compatibility problem between the new SG driver and
sanei_scsi.c is that the new SG driver does not guarantee to allocate
a buffer of the size specified with the SG_SET_RESERVED_SIZE
ioctl call. It can theoretically even happen that there is no
buffer at all (see
http://www.torque.net/sg/p/scsi-generic_long.txt for details).

This means especially that there is no guarantee that two file
handles will have the same buffer size: The SG driver allocates
separate buffers for each file handle. Therefore, the static
variable sanei_scsi_max_request_size becomes less useful.

Andreas Beck made two suggestions to solve these problem:

1. to define a second function

sanei_scsi_open_extended(const char *dev, int *fdp,
SANEI_SCSI_Sense_Handler handler,
void *handler_arg, int *buffersize)

This function tries to allocate a SG buffer of size *buffersize;
the actual buffer size allocated by the SG driver is written into
*buffersize on return. Please note that the SG driver does not
guarantee to allocated even one page (4 kB on most platforms) of
memory. Thus it is up to the backend to decide if the buffer is
large enough.

2. to define sanei_scsi_open as a "wrapper function" for
sanei_scsi_open-extended. sanei_scsi_open can then ensure that
sanei_scsi_max_request_size is not increased if a second file
handle is opened.

While I implemented Andy's suggestion for a wrapper function, I
decided against the logic of setting an upper limit for
sanei_scsi_max_request_size in a second open. Several
backends start a separate reader process, and if a call to
sanei_scsi_open decreasing sanei_scsi_max_request_size happens,
while a reader process is running, the reader process would use
the old value. (There are even more situations, where a backend
might crash, if sanei_scsi_max_request_size changes.)

Therefore, sanei_scsi_open ensures that the SG driver allocates
exactly as much memory as requested, else it fails with
SANE_STATUS_NO_MEM.

Further, I changed sanei_scsi_req_flush_all. If a frontend
attaches to two scanners, the old implementation would flush the
commands sent to both scanners. Thus, I wrote another function
sanei_scsi_req_flush_all_extended(int fd), which flushes the
commands for only one file handle.

sanei_scsi_req_flush_all is now a wrapper for the new function,
and flushes the commands for a file handle, if there is only one
opened file handle. If two or more file handles are open, the
function bails out in an assert.

The modifications to the queueing implementation do not affect
the behaviour of sanei_scsi_req_enter / sanei_scsi_req_wait,
so I don't explain them. Look into he source code if you are curious.

I know that especially the introduction of sanei_scsi_open_extended
can have a relatively big impact on the backends. The Sharp backend
for example needs some cleanup regarding the time, at which it
allocates a buffer. (For my tests of the new functions, I
used some workaround. And I found an "asymmetry" in the backend
regarding the functions which call sanei_scsi_open and
sanei_scsi_close...)

But the improved capabilities of the new SG driver are worth the
changes, I think. And, frankly speaking, I don't think that
global variables like sanei_scsi_max_request_size are the best
invention since sliced bread, Let's get rid of it.

Thanks to Douglas Gilbert and Andy Beck for answering many
questions and giving many suggestions.

Abel

--- sanei_scsi.c-orig Sat Jul 3 16:59:46 1999
+++ sanei_scsi.c Mon Sep 20 20:58:20 1999
@@ -194,6 +194,76 @@
#endif

int sanei_scsi_max_request_size = MAX_DATA;
+#if USE == LINUX_INTERFACE
+/* the following #defines follow Douglas Gilbert's sample code
+ to maintain run time compatibility with the old and the
+ new SG driver for Linux
+*/
+#ifndef SG_SET_COMMAND_Q
+#define SG_SET_COMMAND_Q 0x2271
+#endif
+#ifndef SG_SET_RESERVED_SIZE
+#define SG_SET_RESERVED_SIZE 0x2275
+#endif
+#ifndef SG_GET_RESERVED_SIZE
+#define SG_GET_RESERVED_SIZE 0x2272
+#endif
+#ifndef SG_GET_SCSI_ID
+#define SG_GET_SCSI_ID 0x2276
+#endif
+#ifndef SG_GET_VERSION_NUM
+#define SG_GET_VERSION_NUM 0x2282
+#endif
+
+#ifndef SCSIBUFFERSIZE
+#define SCSIBUFFERSIZE (128 * 1024)
+#endif
+
+/* the struct returned by the SG ioctl call SG_GET_SCSI_ID changed
+ from version 2.1.34 to 2.1.35, and we need the informations from
+ the field s_queue_depth, which was introduced in 2.1.35.
+ To get this file compiling also with older versions of sg.h, the
+ struct is re-defined here.
+*/
+typedef struct xsg_scsi_id {
+ int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2
etc */
+ int channel;
+ int scsi_id; /* scsi id of target device */
+ int lun;
+ int scsi_type; /* TYPE_... defined in scsi/scsi.h */
+ short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
+ short d_queue_depth;/* device (or adapter) maximum queue length */
+ int unused1; /* probably find a good use, set 0 for now */
+ int unused2; /* ditto */
+} SG_scsi_id;
+
+typedef struct req
+ {
+ struct req *next;
+ int fd;
+ u_int running:1, done:1;
+ SANE_Status status;
+ size_t *dst_len;
+ void *dst;
+ struct
+ {
+ struct sg_header hdr;
+ /* Make sure this is the last element, the real size is
+ SG_BIG_BUFF and machine dependant */
+ u_int8_t data[1];
+ }
+ cdb;
+ }
+req;
+
+typedef struct Fdparms
+ {
+ int sg_queue_used, sg_queue_max;
+ req *sane_qhead, *sane_qtail, *sane_free_list;
+ }
+fdparms;
+
+#endif

#if USE == FREEBSD_CAM_INTERFACE
# define CAM_MAXDEVS 128
@@ -590,12 +660,25 @@

#endif /* USE_OS2_INTERFACE */

+static int num_alloced = 0;
+
+#if USE == LINUX_INTERFACE
+
+SANE_Status
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+
+#else
+
SANE_Status
sanei_scsi_open (const char *dev, int *fdp,
SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+
+#endif
+
{
u_int bus = 0, target = 0, lun = 0, fake_fd = 0;
- static int num_alloced = 0;
char *real_dev = 0;
void *pdata = 0;
int fd;
@@ -621,6 +704,7 @@
sanei_scsi_max_request_size = atoi (buf);
DBG (1, "sanei_scsi_open: sanei_scsi_max_request_size=%d bytes\n",
sanei_scsi_max_request_size);
+ close(fd);
}
}
#endif
@@ -913,6 +997,109 @@
}
}
#endif /* SGIOCSTL */
+#if USE == LINUX_INTERFACE
+ {
+ SG_scsi_id sid;
+ int ioctl_val, sg_version;
+ int real_buffersize;
+ fdparms *fdpa = 0;
+
+ pdata = fdpa = malloc(sizeof(fdparms));
+ if (!pdata)
+ {
+ close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ memset(fdpa, 0, sizeof(fdparms));
+ /* default: allow only one command to be sent to the SG driver
+ */
+ fdpa->sg_queue_max = 1;
+
+ /* Try to read the SG version. If the ioctl call is successful,
+ we have the new SG driver, and we can increase the buffer size
+ using another ioctl call.
+ If we have SG version 2.1.35 or above, we can additionally
enable
+ command queueing.
+ */
+ if (0 == ioctl(fd, SG_GET_VERSION_NUM, &sg_version))
+ {
+ DBG(1, "sanei_scsi_open: SG driver version: %i\n", sg_version);
+
+ /* try to reserve a SG buffer of the size specified by
*buffersize
+ */
+ ioctl(fd, SG_SET_RESERVED_SIZE, buffersize);
+
+ /* the set call may not be able to allocate as much memory
+ as requested, thus we read the actual buffer size.
+
+ NOTE: sanei_scsi_max_request_size is a global variable
+ used for all devices/file handles, while version 2.0 and
+ above of the SG driver allocate buffer memory for each
+ opened file separately. Therefore, we have a possible
+ inconsistency, if more than one file is opened and
+ if the SG_GET_RESERVED_SIZE return different buffer sizes
+ for different file handles. (See Douglas Gilbert's
+ description of the SG driver for details:
+ http://www.torque.net/sg/p/scsi-generic_long.txt)
+
+ For this reason, sanei_scsi_open does not allow to open
+ two or more file handles simultaneously.
+ */
+ if (0 == ioctl(fd, SG_GET_RESERVED_SIZE, &real_buffersize))
+ {
+ /* if we got more memory than requested, we stick with
+ with the requested value, in order to allow
+ sanei_scsi_open to check the buffer size exactly.
+ */
+ if (real_buffersize > *buffersize)
+ {
+ sanei_scsi_max_request_size = *buffersize;
+ }
+ else
+ {
+ sanei_scsi_max_request_size = real_buffersize;
+ *buffersize = real_buffersize;
+ }
+ }
+ else
+ {
+ DBG(1, "sanei_scsi_open: cannot read SG buffer size -
%s\n",
+ strerror(errno));
+ close(fd);
+ return SANE_STATUS_NO_MEM;
+ }
+ DBG(1, "sanei_scsi_open_extended: using %i bytes as SCSI
buffer\n",
+ sanei_scsi_max_request_size);
+
+ if (sg_version >= 20135)
+ {
+ DBG(1, "trying to enable low level command queueing\n");
+
+ if (0 == ioctl(fd, SG_GET_SCSI_ID, &sid))
+ {
+ DBG(1, "sanei_scsi_open: Host adapter queue depth:
%i\n",
+ sid.d_queue_depth);
+
+ ioctl_val = 1;
+ if(0 == ioctl(fd, SG_SET_COMMAND_Q, &ioctl_val))
+ {
+ fdpa->sg_queue_max = sid.d_queue_depth;
+ if (fdpa->sg_queue_max <= 0)
+ fdpa->sg_queue_max = 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* we have the old SG driver: */
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ }
+ if (fdpa->sg_queue_max > 1)
+ DBG(1, "sanei_scsi_open: low level command queueing enabled\n");
+ }
+#endif /* LINUX_INTERFACE */
#endif /* !DECUNIX_INTERFACE */

if (fd >= num_alloced)
@@ -958,9 +1145,78 @@
return SANE_STATUS_GOOD;
}

+#if USE == LINUX_INTERFACE
+/* The "wrapper" for the old open call */
+SANE_Status
+sanei_scsi_open (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler, void *handler_arg)
+{
+ int i, j = 0;
+ int wanted_buffersize = SCSIBUFFERSIZE, real_buffersize;
+ SANE_Status res;
+ char *cc, *cc1;
+
+ cc = getenv("SANE_SG_BUFFERSIZE");
+ if (cc)
+ {
+ i = strtol(cc, &cc1, 10);
+ if (cc != cc1 && i >= 32768)
+ wanted_buffersize = i;
+ }
+
+ real_buffersize = wanted_buffersize;
+ res = sanei_scsi_open_extended(dev, fdp, handler, handler_arg,
+ &real_buffersize);
+
+ /* make sure that we got as much memory as we wanted, otherwise
+ the backend might be confused
+ */
+ if (real_buffersize != wanted_buffersize)
+ {
+ DBG(1, "sanei_scsi_open: could not allocate SG buffer memory "
+ "wanted: %i got: %i\n", wanted_buffersize, real_buffersize);
+ sanei_scsi_close(*fdp);
+ return SANE_STATUS_NO_MEM;
+ }
+
+ return res;
+}
+#else
+/* dummy for the proposed new open call */
+sanei_scsi_open_extended (const char *dev, int *fdp,
+ SANEI_SCSI_Sense_Handler handler,
+ void *handler_arg, int *buffersize)
+{
+ SANE_Status res;
+ res = sanei_scsi_open(dev, fdp, handler, handler_arg);
+ if (sanei_scsi_max_request_size < *buffersize)
+ *buffersize = sanei_scsi_max_request_size;
+ return res;
+}
+#endif
+
void
sanei_scsi_close (int fd)
{
+#if USE == LINUX_INTERFACE
+ if (fd_info[fd].pdata)
+ {
+ req *req, *next_req;
+
+ /* make sure that there are no pending SCSI calls */
+ sanei_scsi_req_flush_all_extended(fd);
+
+ req = ((fdparms*) fd_info[fd].pdata)->sane_free_list;
+ while (req)
+ {
+ next_req = req->next;
+ free(req);
+ req = next_req;
+ }
+ free(fd_info[fd].pdata);
+ }
+#endif
+
fd_info[fd].in_use = 0;
fd_info[fd].sense_handler = 0;
fd_info[fd].sense_handler_arg = 0;
@@ -1225,69 +1481,133 @@
} \
while (0)

-static struct req
- {
- struct req *next;
- int fd;
- u_int running:1, done:1;
- SANE_Status status;
- size_t *dst_len;
- void *dst;
- struct
- {
- struct sg_header hdr;
- /* Make sure this is the last element, the real size is
- SG_BIG_BUFF and machine dependant */
- u_int8_t data[1];
- }
- cdb;
- }
-*qhead, *qtail, *free_list;
-
static void
issue (struct req *req)
{
ssize_t nwritten;
+ fdparms *fdp;
+ struct req *rp;
+ int retries;

- if (!req || req->running)
+ if (!req)
return;
-
+ fdp = (fdparms*) fd_info[req->fd].pdata;
DBG (4, "sanei_scsi.issue: %p\n", req);

- ATOMIC (req->running = 1;
- nwritten = write (req->fd, &req->cdb, req->cdb.hdr.pack_len));
-
- if (nwritten != req->cdb.hdr.pack_len)
- {
- DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
- strerror (errno));
- req->done = 1;
- if (errno == ENOMEM)
- {
- DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
- "Check file PROBLEMS.\n");
- req->status = SANE_STATUS_NO_MEM;
- }
+ rp = fdp->sane_qhead;
+ while (rp && rp->running)
+ rp = rp->next;
+
+ while (rp && fdp->sg_queue_used < fdp->sg_queue_max)
+ {
+ retries = 20;
+ while (retries)
+ {
+ ATOMIC (rp->running = 1;
+ nwritten = write (rp->fd, &rp->cdb,
rp->cdb.hdr.pack_len);
+ if (nwritten != rp->cdb.hdr.pack_len)
+ {
+ /* ENOMEM can easily happen, if both command
queueing
+ inside the SG driver and large buffers are
used.
+ Therefore, if ENOMEM does not occur for the
first
+ command in the queue, we can simply try to
issue
+ it later again.
+ */
+ if ( errno == EAGAIN
+ || (errno == ENOMEM && rp !=
fdp->sane_qhead))
+ {
+ /* don't try to send the data again, but
+ wait for the next call to issue()
+ */
+ rp->running = 0;
+ }
+ }
+ );
+ if (rp == fdp->sane_qhead && errno == EAGAIN)
+ {
+ retries--;
+ usleep(10000);
+ }
+ else
+ retries = 0;
+ }
+
+ if (nwritten != rp->cdb.hdr.pack_len)
+ {
+ if (rp->running)
+ {
+ DBG (1, "sanei_scsi.issue: bad write (errno=%s)\n",
+ strerror (errno));
+ rp->done = 1;
+ if (errno == ENOMEM)
+ {
+ DBG (1, "sanei_scsi.issue: SG_BIG_BUF inconsistency? "
+ "Check file PROBLEMS.\n");
+ rp->status = SANE_STATUS_NO_MEM;
+ }
+ else
+ rp->status = SANE_STATUS_IO_ERROR;
+ }
+ else
+ {
+ if (errno == ENOMEM)
+ DBG(1, "issue: ENOMEM - cannot queue SCSI command. "
+ "Trying again later.\n");
+ else
+ DBG(1, "issue: cannot queue SCSI command. "
+ "Trying again later.\n");
+ }
+ break; /* in case of an error don't try to queue more
commands */
+ }
else
- req->status = SANE_STATUS_IO_ERROR;
+ fdp->sg_queue_used++;
+ rp = rp->next;
}
}

void
-sanei_scsi_req_flush_all (void)
+sanei_scsi_req_flush_all_extended (int fd)
{
+ fdparms *fdp;
struct req *req, *next_req;

- for (req = qhead; req; req = next_req)
+ fdp = (fdparms*) fd_info[fd].pdata;
+ for (req = fdp->sane_qhead; req; req = next_req)
{
if (req->running && !req->done)
- read (req->fd, &req->cdb, req->cdb.hdr.reply_len);
+ {
+ read (fd, &req->cdb, req->cdb.hdr.reply_len);
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;
+ }
next_req = req->next;

- req->next = free_list;
- free_list = req;
+ req->next = fdp->sane_free_list;
+ fdp->sane_free_list = req;
}
- qhead = qtail = 0;
+ fdp->sane_qhead = fdp->sane_qtail = 0;
+}
+
+void
+sanei_scsi_req_flush_all ()
+{
+ int fd, i, j = 0;
+
+ /* sanei_scsi_open allows only one open file handle, so we
+ can simply look for the first entry where in_use is set
+ */
+ +
+ fd = num_alloced;
+ for (i = 0; i < num_alloced; i++)
+ if (fd_info[i].in_use)
+ {
+ j++;
+ fd = i;
+ }
+
+ assert(j < 2);
+
+ if (fd < num_alloced)
+ sanei_scsi_req_flush_all_extended(fd);
}

SANE_Status
@@ -1296,11 +1616,14 @@
{
struct req *req;
size_t size;
+ fdparms *fdp;
+
+ fdp = (fdparms*) fd_info[fd].pdata;

- if (free_list)
+ if (fdp->sane_free_list)
{
- req = free_list;
- free_list = req->next;
+ req = fdp->sane_free_list;
+ fdp->sane_free_list = req->next;
req->next = 0;
}
else
@@ -1328,17 +1651,23 @@
memcpy (&req->cdb.data, src, src_size);

req->next = 0;
- ATOMIC (if (qtail)
+ ATOMIC (if (fdp->sane_qtail)
{
- qtail->next = req;
- qtail = req;
+ fdp->sane_qtail->next = req;
+ fdp->sane_qtail = req;
}
else
- qhead = qtail = req);
+ fdp->sane_qhead = fdp->sane_qtail = req);

DBG (4, "scsi_req_enter: entered %p\n", req);

*idp = req;
+ issue(req);
+
+ DBG(10, "scsi_req_enter: queue_used: %i, queue_max: %i\n",
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_used,
+ ((fdparms*) fd_info[fd].pdata)->sg_queue_max);
+
return SANE_STATUS_GOOD;
}

@@ -1349,7 +1678,8 @@
struct req *req = id;
ssize_t nread = 0;

- assert (req == qhead); /* we don't support out-of-order completion */
+ /* we don't support out-of-order completion */
+ assert (req == ((fdparms*)fd_info[req->fd].pdata)->sane_qhead);

DBG (4, "sanei_scsi_req_wait: waiting for %p\n", req);

@@ -1372,6 +1702,9 @@
ATOMIC (nread = read (req->fd, &req->cdb,
req->cdb.hdr.reply_len);
req->done = 1);

+ if (fd_info[req->fd].pdata)
+ ((fdparms*) fd_info[req->fd].pdata)->sg_queue_used--;
+
/* Now issue next command asap, if any. We can't do this
earlier since the Linux kernel has space for just one big
buffer. */
@@ -1423,11 +1756,12 @@
}

/* dequeue and release processed request: */
- ATOMIC (qhead = qhead->next;
- if (!qhead)
- qtail = 0;
- req->next = free_list;
- free_list = req);
+ ATOMIC (((fdparms*) fd_info[req->fd].pdata)->sane_qhead
+ = ((fdparms*) fd_info[req->fd].pdata)->sane_qhead->next;
+ if (!((fdparms*) fd_info[req->fd].pdata)->sane_qhead)
+ ((fdparms*) fd_info[req->fd].pdata)->sane_qtail = 0;
+ req->next = ((fdparms*) fd_info[req->fd].pdata)->sane_free_list;
+ ((fdparms*) fd_info[req->fd].pdata)->sane_free_list = req);
return status;
}

--- sanei_scsi.h-orig Sat Sep 4 16:30:01 1999
+++ sanei_scsi.h Mon Sep 20 21:05:35 1999
@@ -48,6 +48,23 @@
SANEI_SCSI_Sense_Handler sense_handler,
void *sense_arg);

+/* The extended open call allows a backend to ask for a specific
+ buffer size. sanei_scsi_open tries to allocate a buffer of the
+ size given by *buffersize upon entry to this function. If
+ sanei_scsi_open_extended returns successfully, *buffersize
+ contains the available buffer size. This value may be both
+ smaller or larger than the value requested by the backend;
+ it can even be zero. The backend must decide, if it got enough
+ buffer memory to work.
+
+ Note that the value of *buffersize may differ for different
+ files.
+*/
+extern SANE_Status sanei_scsi_open_extended (
+ const char * device_name, int * fd,
+ SANEI_SCSI_Sense_Handler sense_handler,
+ void *sense_arg, int *buffersize);
+
/* One or more scsi commands can be enqueued by calling req_enter().
SRC is the pointer to the SCSI command and associated write data
and SRC_SIZE is the length of the command and data. DST is a
@@ -76,8 +93,13 @@
const void * src, size_t src_size,
void * dst, size_t * dst_size);

-/* Flush all pending SCSI commands. */
+/* Flush all pending SCSI commands. This function work only,
+ if zero or one SCSI file handles are open.
+*/
extern void sanei_scsi_req_flush_all (void);
+
+/* Flush all SCSI commands pending for one handle */
+extern void sanei_scsi_req_flush_all_extended (int fd);

extern void sanei_scsi_close (int fd);

--- configure.in-orig Sun Apr 4 00:44:56 1999
+++ configure.in Mon Sep 20 20:41:44 1999
@@ -192,10 +192,19 @@
else
DLL_PRELOAD=""
fi
+
+AC_ARG_ENABLE(scsibuffersize,
+ [ --enable-scsibuffersize=N
+ specify the default size of the buffer for SCSI
commands],
+ [set_scsibuffersize="$enableval"], [set_scsibuffersize=131072])
+CFLAGS="$CFLAGS -DSCSIBUFFERSIZE=$set_scsibuffersize"
+echo "scsi buffersize: $set_scsibuffersize"
+
AC_SUBST(V_MAJOR)
AC_SUBST(V_MINOR)
AC_SUBST(V_REV)
AC_SUBST(DLL_PRELOAD)
+

AC_OUTPUT([Makefile lib/Makefile sanei/Makefile frontend/Makefile
japi/Makefile backend/Makefile include/Makefile doc/Makefile

--
Source code, list archive, and docs: http://www.mostang.com/sane/
To unsubscribe: echo unsubscribe sane-devel | mail majordomo@mostang.com