Discussion:
[sane-devel] SANE V2
Enno Fennema
2002-12-04 10:08:31 UTC
Permalink
I would like to make some comments on the SANE 2.06 draft standard.

They represent my personal preferences and proposals and are offerered
for discussion but rather than prefixing each statement with 'I
think...' 'I propose...' etc.
They are presented as statements of fact, which they are not.
Some rationale or comment is included within << and >>

1)
One preliminary question: Who does turn the switch when and decide to go
to version 2.0 ?


2) SANE_Handle
A SANE_Handle points to a very important structure, viz.

typedef struct SANE_HANDLE {
SANE_Device *device;
SANE_Status (*init) (SANE_Int * version_code, SANE_Auth_Callback
authorize);
void (*exit) (void);
SANE_Status (*get_devices) (SANE_Device **device_list, SANE_Bool
local_only);

SANE_Status (*open) (SANE_String_Const devicename);
void (*close)(void);

const SANE_Option_Descriptor* (*sane_get_option_descriptor)(SANE_Int
option);
SANE_Status (*control_option) (SANE_Int option, SANE_Action action,
void *value, SANE_Int * info);
SANE_Status (*get_parameters)(SANE_Parameters * params);
SANE_Status (*start) (void);
SANE_Status (*read) (SANE_Byte * data, SANE_Int max_length, SANE_Int
* length);
void (*cancel) (void);
SANE_Status (*set_io_mode) (SANE_Bool non_blocking);
SANE_Status (*get_select_fd) (SANE_Int * fd);
};

SANE_HANDLE *be;

<<
All function calls can then take the the form
be->start()
and the handle need no longer be given as the first parameter.

This also obviates the fiddling of backend related names and looking
them up after dynamically loading a backend.
Now only lookup the backend name, which returns the address of the
handle through which all backend functions can be accessed.

Usually a backend deals with one single device or a few strongly related
devices, eg. different models of scanners from one manufacturer.
For a meta-backend the definition of 'related' can be quite wide, in the
extreme case 'all devices for which specific backends are available on
this system'.

The sane_open() function creates a nexus between a frontend and a
device. The backend dealing with device must note and maintain that
nexus until sane_close().

Each backend has exactly one struct SANE_HANDLE with the backend name as
name. This allows the frontend or meta backend to know how to get the
address of the handle of a backend with dlsym().

If a device has been open()ed h->device points to that SANE_Device,
otherwise h->device == NULL.
The backend either through the device pointer or by other internal means
must know whether a device model is currently open and what model it is.

The handle is the sole link between a frontend and the currently
selected backend.
whether meta- or physical.
2) SANE_Device

typedef struct SANE_Device {
struct SANE_Device *next;
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
SANE_String devname; /* from sane.conf eg. /dev/usbscanner0 */
} SANE_Device;


<<
next pointer: If sane_get_devices() returns a list of devices rather
than an array it is easier for a meta-backend to string all those lists
together.

When enquiring into models managed by a particular backend
sane_get_devices need only return the pointer to the first device
managed by it, assuming that in the backend they are also linked into a
list.

devname: How to open the device with an operating system dependent way.
See also comments on config file.

I have no serious problems with all the other fields proposed already
but am worried that you may be going overboard with detail like which
floor the printer is on and e-mail addresses.

Most people use only very few scanners and they know which one is which.
4) sanei
Probably stands for SANE Internal ???.
The available functions, even though their use appears to be optional,
deserve some documentation.

In my view sane_strstatus() is a sanei_ rather than a sane_ function.

5) Option descriptor
Include a one-character shortname for common options to be used by eg.
get_opt_long().

Is there any point in including a SANE_Value (union word, fixed, char*)
in an option descriptor
containing the current value? Not quite sure myself/not very important.


6) Configuration files
Agree a format for a standard /etc/sane.conf file.
Could by commandline option use other path, etc.

Backends need at least a method to derive the name of the device as used
by the operating system but could benefit from some configuration
information, calibration settings etc.

My preferred format is somewhat like Windows ini files.

The file consists of a number sections consisting of a number of lines.

A '#' symbol, not within quotes, and everything on a line beyond it is
ignored, as are totally blank lines.

A section start with a line containing only the section name in square
brackets and ends at the next section or at end of file.

Each line consists of a number of words.
Words are essentially characters separated by white space.
White space is a space, a tab, a newline or carriage return.
Characters, including whitespace, enclosed in double quotes are treated
as one word.

The first word on each line is the type of configuration data. Standard
lines could be:

scanner <name> <backend> <opsysname>

eg. scanner "Canon N656U" canonusb /dev/usbscanner0

and

alias myscanner "Canon N656U"

A backend (and a frontend if it so wishes) may read all sections and
ignore those it does not need/understand.
The only obligatory section is [Backends] followed by one line for each
available backend.

Each backend can decide on its own configuration data within its own
sections always named
[be-name model-name]

A configuration file would look something like:

[Backends]
...
canonusb
...
def myscanner canonusb N656U

[canonusb]
model Canoscan N656U
...

[Canoscan N656U]
vendor 0x02A9
product 0x2206
sysname /dev/usbscanner0
saturationRed 2.44
saturationGreen 1.37
saturationBlue 1.08
...


Would appreciate comments on the points raised.

Enno Fennema
***@dataweb.nl
Oliver Rauch
2002-12-04 17:14:22 UTC
Permalink
Post by Enno Fennema
2) SANE_Handle
A SANE_Handle points to a very important structure, viz.
I do not see an advantage of this and I do not see a reason to do any changes
here. The way sane1 does handle it does work.
The sane standard is defined as a functional approach. It is not an object
oriented approach, it does not make sense to mix both aprroaches.
Post by Enno Fennema
2) SANE_Device
typedef struct SANE_Device {
struct SANE_Device *next;
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
SANE_String devname; /* from sane.conf eg. /dev/usbscanner0 */
} SANE_Device;
<<
next pointer: If sane_get_devices() returns a list of devices rather
than an array it is easier for a meta-backend to string all those lists
together.
When enquiring into models managed by a particular backend
sane_get_devices need only return the pointer to the first device
managed by it, assuming that in the backend they are also linked into a
list.
devname: How to open the device with an operating system dependent way.
See also comments on config file.
I have no serious problems with all the other fields proposed already
but am worried that you may be going overboard with detail like which
floor the printer is on and e-mail addresses.
Most people use only very few scanners and they know which one is which.
These information are really important. May be 90% do not need the
field device_location because they use the scanner locally, but when
the scanner is available on the network then this makes sense.

Email of backend author and URL of backen website are also important
(for me, beause I get several backend related mails because xsane can
not display a contact address for the backend in the moment).
Post by Enno Fennema
5) Option descriptor
Include a one-character shortname for common options to be used by eg.
get_opt_long().
This is something that does not fit into the sane standard, it has to be
managed by the frontend.
Post by Enno Fennema
Is there any point in including a SANE_Value (union word, fixed, char*)
in an option descriptor
containing the current value? Not quite sure myself/not very important.
6) Configuration files
Agree a format for a standard /etc/sane.conf file.
I do not think it makes sense to create one large
conf.file that includes all backends.
This makes it harder to include external backends
and has no advantage, but it increases the risk
that something breaks.
But it makes sense to use more common options.

Oliver
--
http://www.xsane.org
http://www.mostang.com/sane
http://www.rauch-domain.de
mailto:***@Rauch-Domain.DE
Enno Fennema
2002-12-06 09:43:33 UTC
Permalink
Thanks for reply on 1. I have no very strong feelings on points 3) to 5)
but have to come back on 2).

A few years ago James W. McAllister, a Scottish philosopher at the time
working at Leiden University, wrote a book called "Beauty and Revolution
in Science". He noted that scientists in choosing between competing
theories are also guided by aesthetic criteria.

Beauty and elegance are not infallible guides to the perfect algorithm
but such considerations have occasionally stopped me from going astray.

The renaming activities around backends, name concatenation in the front
end together with a proliferation of generated stub files is neither
beautiful nor elegant.

I hope you will take some time to try and understand why Linux and
nearly all modules so heavily use the approach you deprecate. That
something works should not be an argument to reject something that may
work better.

Regards,
Enno Fennema
Henning Meier-Geinitz
2002-12-06 23:11:33 UTC
Permalink
Hi,
Post by Enno Fennema
A few years ago James W. McAllister, a Scottish philosopher at the time
working at Leiden University, wrote a book called "Beauty and Revolution
in Science". He noted that scientists in choosing between competing
theories are also guided by aesthetic criteria.
Beauty and elegance are not infallible guides to the perfect algorithm
but such considerations have occasionally stopped me from going astray.
The renaming activities around backends, name concatenation in the front
end together with a proliferation of generated stub files is neither
beautiful nor elegant.
That's true. It's a consequence of providing several ways to connect
to backends:

1) Link to a backend statically
2) Link to a shared backend library
3) link statically to a meta backend that preloads all backends
statically
4) link to a shared meta backend library that preloads all backends
statically
5) link statically to a meta backend that dynamically loads all backends
6) link to a shared meta backend library that dynamically loads all backends

I'm still not convinced that that works but it obviously does.
Post by Enno Fennema
I hope you will take some time to try and understand why Linux and
nearly all modules so heavily use the approach you deprecate. That
something works should not be an argument to reject something that may
work better.
That something not works is not an argument to reject something that
does work better.

The "may" is the important word :-)

But I'm not the right person to talk about interface design. I may
find holes in existing ones, but designing new ones should be done by
others.

Apropos holes:

You would need a backend object that can contain multiple device
objects (because one backend supports multiple devices at the same
time). Those may contain an image object which consists of frames
channels. Not sure, if this is easier.

Bye,
Henning
Henning Meier-Geinitz
2002-12-04 22:15:57 UTC
Permalink
Hi,
Post by Enno Fennema
I would like to make some comments on the SANE 2.06 draft standard.
Thanks for your comments. I hope I can put some more time into SANE2
during the next days.
Post by Enno Fennema
They represent my personal preferences and proposals and are offerered
for discussion but rather than prefixing each statement with 'I
think...' 'I propose...' etc.
Same here.
Post by Enno Fennema
1)
One preliminary question: Who does turn the switch when and decide to go
to version 2.0 ?
Well, "we" do :-) But I don't think there will be a hard switch. My
idea is to finish the standard at least to a degree, where all the
wishlist items are included and all major bugs are fixed. Then coding
for the core elements can begin. This will be a completely different
source tree, no code is just copied from sane1 to avoid keeping old
cruft. The first thing to do is probably to alow some testing with
scanimage and a test backend. Then, if there are maintainers, the
backends are ported.

There is no hard switch because it's planned to write a "bridge"
meta backend that allows SANE2-frontends to communicate with SANE1
backends. So SANE1 will be around some time and may be updated, e.g.
if severe errors are found.
Post by Enno Fennema
2) SANE_Handle
A SANE_Handle points to a very important structure, viz.
As Oliver, I don't like this approach. It completely changes the way
SANE works and I'm not sure if the reduction in parameters really buys
us anything.
Post by Enno Fennema
This also obviates the fiddling of backend related names and looking
them up after dynamically loading a backend.
Now only lookup the backend name, which returns the address of the
handle through which all backend functions can be accessed.
That's an advantage, but well, the existing code works.
Post by Enno Fennema
Each backend has exactly one struct SANE_HANDLE with the backend name as
name. This allows the frontend or meta backend to know how to get the
address of the handle of a backend with dlsym().
As far as I know, the standard aloows to open more than one device
simultaneously (or am I wrong?) This isn't used currently by any
frontend however.
Post by Enno Fennema
2) SANE_Device
typedef struct SANE_Device {
struct SANE_Device *next;
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
SANE_String devname; /* from sane.conf eg. /dev/usbscanner0 */
} SANE_Device;
<<
next pointer: If sane_get_devices() returns a list of devices rather
than an array it is easier for a meta-backend to string all those lists
together.
When enquiring into models managed by a particular backend
sane_get_devices need only return the pointer to the first device
managed by it, assuming that in the backend they are also linked into a
list.
Sounds reasonable. Are there any compatibility issues with SANE1?
Post by Enno Fennema
devname: How to open the device with an operating system dependent way.
See also comments on config file.
Why should the frontend know about the real device name? The frontend
mustn't touch the device anyway.
Post by Enno Fennema
I have no serious problems with all the other fields proposed already
but am worried that you may be going overboard with detail like which
floor the printer is on and e-mail addresses.
It's ok to have the email address and webpage because that's an easy
way to contact the maintainer if anything goes wrong.

I would name it "email_backend_maintainer" because that's the one who
wants to get email. The orginal author may have been lost since years...

I also would only recommend the addition of a webpage/ftp location
("should", not "must") jsut because not every simple backend has one.

As the device location and comment strings are optional, I don't think
they cause any harm.

We should define that all strings that are not uses are set to "".
Post by Enno Fennema
Most people use only very few scanners and they know which one is which.
True, but then they just won't use the feature. No need to remove it.
Post by Enno Fennema
4) sanei
Probably stands for SANE Internal ???.
Yes.
Post by Enno Fennema
The available functions, even though their use appears to be optional,
deserve some documentation.
http://sanei.meier-geinitz.de/

The SANE standard defines the interface between frontends and
backends. While it explains implementation details sometimes, it
should not comment on such helper functions. In my opinion the
standard already provides too much backend/frontend implementation
stuff that isn't strictly necessary and doesn't touch the interface at
all. I'll comment on the details later this week.
Post by Enno Fennema
In my view sane_strstatus() is a sanei_ rather than a sane_ function.
It's an interface between the frontend and the backend. The frontend
asks the backend about the textual meaning of an error.
The function is also used in the backends but the idea is to use it in
frontends.
Post by Enno Fennema
5) Option descriptor
Include a one-character shortname for common options to be used by eg.
get_opt_long().
That's an implementation detail. Most frontends don't even know what
short options are. And further more, the backend can't know which
short options already in use by the frontend. The same is true with
long options, but the risk of clashes is lower.
Post by Enno Fennema
Is there any point in including a SANE_Value (union word, fixed, char*)
in an option descriptor
containing the current value? Not quite sure myself/not very important.
I don't think so. That's why we have sane_control_option().
Post by Enno Fennema
6) Configuration files
Agree a format for a standard /etc/sane.conf file.
Only over my dead body :-)

One of the problems with such files is that updating only one or
adding backends is complicated. And one mistake in the format can
break all other backends.

Also I don't like editing long config files. We have about 50 backends
that all would need sections in this file.

Keep in mind that ideally configuration files shouldn't not be used.
That means, that the backend should autodetect any device. Well, the
world is not perfect, but that's the goal.
Post by Enno Fennema
Could by commandline option use other path, etc.
SANE_CONFIG_DIR=/opt/etc/sane.d/ already works.
Post by Enno Fennema
Backends need at least a method to derive the name of the device as used
by the operating system but could benefit from some configuration
information, calibration settings etc.
You mean frontends? Backends should know hat they are doing. There
shouldn't be a hidden path to communicate between backends and/or
frontends. This will cause trouble...
Post by Enno Fennema
My preferred format is somewhat like Windows ini files.
Uh! :-)
Post by Enno Fennema
Each line consists of a number of words.
Words are essentially characters separated by white space.
White space is a space, a tab, a newline or carriage return.
Characters, including whitespace, enclosed in double quotes are treated
as one word.
These rules are already used in current config files.
Post by Enno Fennema
The first word on each line is the type of configuration data. Standard
scanner <name> <backend> <opsysname>
eg. scanner "Canon N656U" canonusb /dev/usbscanner0
/dev/usbscanner0 and other hardwired device files should be avoided
anyway. They are only used when autodetection doesn't work.

You are right concerning config file rules, however. It's an
implementation detail, so it won'z go in the standard but e.g. to
backend-writing.txt. Maybe also sane_config can have more code. E.g.
ignoring everything that starts with a "#" so we don't need to do this
check in every backend.

Some things that could be standardized:

[usb]
--> all following devices are usb (same for SCSI etc)

usb vendor device
scsi VENDOR MODEL TYPE BUS CHANNEL ID LUN
--> already define in man pages. Should be used everywhere.

device-vendor string
device-model string
device-location string
device-comment string
(or similar)
--> to overwrite the backend's default values, e.g. for rebadged scanners

device /some/device/name
--> to define a hardwired device name if autodetection doesn't work

port 0x432
--> to set a port number for parport scanners if autodetection doesn't
work

Bye,
Henning
Matthew Duggan
2002-12-05 23:00:12 UTC
Permalink
Hi,
Post by Henning Meier-Geinitz
Post by Enno Fennema
In my view sane_strstatus() is a sanei_ rather than a sane_
function.
Post by Henning Meier-Geinitz
It's an interface between the frontend and the backend. The frontend
asks the backend about the textual meaning of an error.
The function is also used in the backends but the idea is to use it in
frontends.
Although this may have been the original intent, it's not how the
function has been used in *any* of the backends - probably due to the
vague wording of the original. I think the information deserves
clarification. As a backend author I had been looking for a function to
allow me to return more detailed error messages (rather than just "I/O
Error"), but it didn't seem to exist. Perhaps to make the intended use
more clear, it needs to say something like:

--------------------
sane_strstatus

This function can be used to translate a SANE status code into a
printable string. The returned string is a single line of text that
forms a complete sentence, but without the trailing period (full-stop).
The function is guaranteed to never return NULL. The returned pointer is
valid at least until the next call to this function is performed. This
function is optional for backends to implement, and a default
implementation is avaialble for use in both backends and frontends. If
a backend implements this function, it should provide more detailed
information about the current status of the device than the default
implementation. This information may be device-dependent, and should
assist users in diagnosing the real problem, e.g. "lp0 is on fire".

const SANE_String_Const sane_strstatus (SANE_Status status);

-------------------

Ok, so that example doesn't neccessarily assist users in diagnosing the
problem, but you get the idea.

- Matthew
Henning Meier-Geinitz
2002-12-06 15:53:11 UTC
Permalink
Hi,
Post by Enno Fennema
Post by Henning Meier-Geinitz
Post by Enno Fennema
In my view sane_strstatus() is a sanei_ rather than a sane_
function.
Post by Henning Meier-Geinitz
It's an interface between the frontend and the backend. The frontend
asks the backend about the textual meaning of an error.
The function is also used in the backends but the idea is to use it in
frontends.
Although this may have been the original intent, it's not how the
function has been used in *any* of the backends - probably due to the
vague wording of the original.
I wasn't there when the original standard was written so I can only
guess the intention by the words written there and by the
implementation. Look at ChangeLog-1.0.0 for some info. E.g.
sane_strstatus was explicitely moved from sanei to backend.

As far as I understand, this function should really return just the
textual meaning of the SANE error codes as defined in 4.2.7. sane.h
does not contain texts for the error values so this is a simple method
for frontends to ask what they should print on the screen without
maintaining a list themselves. And it is used by the frontends this
way.

It's also used by the backends themselves for DBG messages, but that's
ok.

The interesting thing is that the most of the mssages in
sane_strstatus.c are not complete sentences so all backends are not
sane-compliant :-)

And the declation in the standard is wrong!

const SANE_String_Const sane_strstatus (SANE_Status status);

The const is too much! Looks like nobody noticed in 6 years.
Post by Enno Fennema
I think the information deserves
clarification. As a backend author I had been looking for a function to
allow me to return more detailed error messages (rather than just "I/O
Error"), but it didn't seem to exist.
You were right, it doesn't exist. But it will exist in SANE2.
Post by Enno Fennema
This function can be used to translate a SANE status code into a
printable string. The returned string is a single line of text that
forms a complete sentence, but without the trailing period (full-stop).
The function is guaranteed to never return NULL. The returned pointer is
valid at least until the next call to this function is performed. This
function is optional for backends to implement,
Huh? What should they do when they don't implement it? They must
define the symbol but can't return NULL. So maybe retunr ""?
Post by Enno Fennema
and a default
implementation is avaialble for use in both backends and frontends.
Don't mix the SANE standard/interface with implementation details in
the current SANE distribution.

Ok, while we are at this topic: The idea was to provide a second error
function that gives more details of the latest error.

So the frontend would call e.g. sane_start and gets back
SANE_STATUS_INVAL. It could call sane_strstatus(SANE_STATUS_INVAL) and
for further detils calls sane_verbose_error(handle). So it could print:

Invalid argument: The top-x coordinate was bigger then the bottom-x
coordinate.

The first one will be the general error type while the second one is
the more detailed one.

Proposal:

4.3.16 sane_verbose_error

This function returns a verbose description of the latest error. The
returned string consists of one or more complete sentences. It is the
responsibility of the frontend to break the string into
managable-length lines. Newline characters in this string should be
interpreted as paragraph breaks. The function is guaranteed to never
return NULL. The returned pointer is valid at least until the next
call to this function is performed.

SANE_String_Const sane_verbose_error(Sane_Handle h)

Bye,
Henning
Matthew Duggan
2002-12-07 02:19:31 UTC
Permalink
Hi,

Henning Meier-Geinitz <***@meier-geinitz.de> wrote:
...
Post by Henning Meier-Geinitz
4.3.16 sane_verbose_error
This function returns a verbose description of the latest error. The
returned string consists of one or more complete sentences. It is the
responsibility of the frontend to break the string into
managable-length lines. Newline characters in this string should be
interpreted as paragraph breaks. The function is guaranteed to never
return NULL. The returned pointer is valid at least until the next
call to this function is performed.
SANE_String_Const sane_verbose_error(Sane_Handle h)
I like this, and it's pretty much what I was looking for. I only have
one criticism from a backend writing point of view: What happens if the
only return has been SANE_STATUS_GOOD? Maybe just:

-----
This function returns a verbose description of the latest error, or an
empty string if no error has occured.

-----

Otherwise, perfect!

- Matthew
Henning Meier-Geinitz
2002-12-07 22:34:56 UTC
Permalink
Hi,
Post by Henning Meier-Geinitz
4.3.16 sane_verbose_error
This function returns a verbose description of the latest error. The
returned string consists of one or more complete sentences. It is the
responsibility of the frontend to break the string into
managable-length lines. Newline characters in this string should be
interpreted as paragraph breaks. The function is guaranteed to never
return NULL. The returned pointer is valid at least until the next
call to this function is performed.
SANE_String_Const sane_verbose_error(Sane_Handle h)
"This function provides a verbose description of the error code
returned by the latest call to a SANE function. The returned ...

The error description must be set to "" if the latest SANE function
did return SANE_STATUS_GOOD."

So the intention is: the message stays the same until any other
SANE function returns an non-error status code. Everything not
returning a status would be ignored.

The issue of thread-safety was raised. I've never used threads and I
also don't know if thread-safety is necessary for this function but if
the following declarion avoids trouble, it's ok, too:

void
sane_verbose_error(Sane_Handle h, SANE_String *description, SANE_Int max_len);


I'm not exactly happy with this function. Mostly because it doesn't
provide the error at the point when the error occurs but sometime later.

A little brainstorming:

What about using the proposed message call-back instead of this
function?

So the backend would call the callback immediatly, when a fatal error
occurs. Fatal means, that it will return anything other than
SANE_STATUS_GOOD. This would work for command-line programs but may be
messy for graphical frontends.

E.g. sane_open ("/dev/scanner") is called, the open system call fails,
because of "file not found" and the abyckend calls the message
callback with this error message. The callback returns immediately and
the backend returns SANE_STATUS_INVAL. Now the frontend would pop up
another error message about the STATUS_INVAL. Quite chaotic.

Another way would be to extend the SANE_Status values. E.g. the top 16
bits are for backend-local error. So both the standard SANE error code
and something that has more information could be stored.
sane_strstatus could be used to print it.

However, if you want to use messages like "can't open /dev/something"
that means dynamic numbering. Not too nice, also.

Bye,
Henning
Diego Zuccato
2002-12-08 13:14:35 UTC
Permalink
Post by Henning Meier-Geinitz
So the backend would call the callback immediatly, when a fatal error
occurs. Fatal means, that it will return anything other than
SANE_STATUS_GOOD. This would work for command-line programs but may be
messy for graphical frontends.
Errr... Since you're defining a new API, then why not return a ptr to
typedef struct {
int errornum;
const char *errmsg;
} SANE_error;
or momething similar ?
This way, the failing function sets both the numerical error code AND
the (verbose) error message.
You don't like structs? :-)
Then a success might be indicated by a NULL ptr, while an error is a
string like "NNN verbose message follows error code" (like many net
progs already do).
Want numerical error code? atoi(retval) and you get it. Is atoi() too
slow? Then use
#define ERRCODE(rv) (rv?(rv[0]*10+rv[1])*10+rv[2]:0)

So you could even have the same error code for different error messages.
Really useful when debugging a module: the error code tells the failure
type, and the rest of the message helps tracing it down. The pointer
should point to a list of static strings, so it remains valid as long as
it's needed.

Just my $.01 :-)

BYtE,
Diego.
Henning Meier-Geinitz
2002-12-08 13:02:45 UTC
Permalink
Hi,
Post by Diego Zuccato
Post by Henning Meier-Geinitz
So the backend would call the callback immediatly, when a fatal error
occurs. Fatal means, that it will return anything other than
SANE_STATUS_GOOD. This would work for command-line programs but may be
messy for graphical frontends.
Errr... Since you're defining a new API, then why not return a ptr to
typedef struct {
int errornum;
const char *errmsg;
} SANE_error;
or momething similar ?
And this would be returned by any SANE API function that currently
returns SANE_Status? Nice, because it links the error number and the
message. No need for an extra call to a function.
Post by Diego Zuccato
This way, the failing function sets both the numerical error code AND
the (verbose) error message.
However: How long is errmsg valid? Until the next call to any SANE
function? That's bad because the frontend may get in trouble when
using threads. Until sane_exit()? Then the memory usage gets bigger
and bigger.
Post by Diego Zuccato
You don't like structs? :-)
Lots of structs are already in SANE :-)
Post by Diego Zuccato
Then a success might be indicated by a NULL ptr, while an error is a
string like "NNN verbose message follows error code" (like many net
progs already do).
Want numerical error code? atoi(retval) and you get it. Is atoi() too
slow? Then use
#define ERRCODE(rv) (rv?(rv[0]*10+rv[1])*10+rv[2]:0)
So you could even have the same error code for different error messages.
Really useful when debugging a module: the error code tells the failure
type, and the rest of the message helps tracing it down. The pointer
should point to a list of static strings, so it remains valid as long as
it's needed.
Well, a list of fixed error messages wouln't be a problem at all. We
could jsut add some more detailed error codes to the existing SANE
codes.

The really interesting error messages are the dynamic ones: "Couldn't
open %s". And %s can be anything and can change multiple times between
sane_init and sane_exit.


Yet another way would be to use an additional callback:

void
sane_error_message_callback (SANE_String error_msg);

Call it in your SANE API function if an error occured that will lead
to a return value of anything other than SANE_STATUS_GOOD. error_msg is
only valid during the runtime of sane_error_message_callback. The
calback only explains the error in more detail, the actual error is
flagged by the return value of the SANAPI function.

Not too nice, either.

If only C would copy strings when returning them as the return value
of functions :-)

Bye,
Henning
Diego Zuccato
2002-12-09 00:13:01 UTC
Permalink
Post by Henning Meier-Geinitz
And this would be returned by any SANE API function that currently
returns SANE_Status? Nice, because it links the error number and the
message. No need for an extra call to a function.
Yep. That's why I like this method. :-)
Post by Henning Meier-Geinitz
However: How long is errmsg valid? Until the next call to any SANE
function? That's bad because the frontend may get in trouble when
using threads. Until sane_exit()? Then the memory usage gets bigger
and bigger.
Well, if it points to a static string (it's const char*) the only limit
for the string is available memory :-)
The struct should be allocated (from heap or from a pool) by the
function called and then freed by the caller. And that's what I don't
like in strdup() like function, but sometimes it's necessary. Another
solution, rarely used, consists of passing a ptr to the struct, so the
called function can set it up as needed and the "return value" is just a
flag ("all right" or "look at the struct").
Post by Henning Meier-Geinitz
The really interesting error messages are the dynamic ones: "Couldn't
open %s". And %s can be anything and can change multiple times between
sane_init and sane_exit.
Err... I didn't think about these. :-(
Well, in that case, use the strdup() technique: the called function
allocates the needed memory that must be freed by the caller. I heard
(well, seen in some code) that that could be a problem under winkozz,
since if a dll allocates memory, that memory must be freed from the dll
itself. Just add a free() wrapper in the dll code... May be SANE_free()
:-)
Callbacks always give me nightmares (I'm developing a db async access
infrastructure that interacts with gtk... brr!)
Post by Henning Meier-Geinitz
If only C would copy strings when returning them as the return value
of functions :-)
Simple :
return strdup(errmsg);
instead of :
return errmsg;

PS: for multithreaded programs, it's possible to setup a thread-specific
data area... See pthread.h .

BYtE,
Diego.
Matto Marjanovic
2002-12-09 21:39:54 UTC
Permalink
Date: Mon, 09 Dec 2002 00:13:01 +0000
Post by Henning Meier-Geinitz
And this would be returned by any SANE API function that currently
returns SANE_Status? Nice, because it links the error number and the
message. No need for an extra call to a function.
Yep. That's why I like this method. :-)
(For those who've lost track: "this method" is to use struct, containing
both the status code and a verbose string pointer, as the return value of
all the sane_() functions.)

Um, except now a string has to be marshalled over the network every time
a sane function is called, and reconstructed on the client side even if
it isn't used.
Callbacks always give me nightmares (I'm developing a db async access
infrastructure that interacts with gtk... brr!)
A true asynchronous callback cannot be used with the network protocol.
However, it can work in this case:
o Frontend calls sane_func(), hands its callback to network backend.
o Net backend signals remote net frontend "execute sane_func()".
o Remote network frontend calls sane_func(), hands its callback to backend.
o Backend calls network callback to put a string somewhere on remote side.
o Remote network frontend sends that string back along with the response
to "sane_func()" request.
o Network backend calls the client frontend's callback with string,
then finally returns from "sane_func()" request.

But this has pretty much all the same problems and lack of benefits as
the above method.
Post by Henning Meier-Geinitz
The really interesting error messages are the dynamic ones: "Couldn't
open %s". And %s can be anything and can change multiple times between
sane_init and sane_exit.
Err... I didn't think about these. :-(
Well, in that case, use the strdup() technique: the called function
allocates the needed memory that must be freed by the caller. I heard
(well, seen in some code) that that could be a problem under winkozz,
since if a dll allocates memory, that memory must be freed from the dll
itself. Just add a free() wrapper in the dll code... May be SANE_free()
:-)
What a pain....
Now I have to call some version of free() (either SANE's or just libc's)
everytime I call a sane_function()!!!


I much prefer Henning's original idea, as follows:

Threading is not a very big issue for SANE.
Henning Meier-Geinitz
2002-12-10 10:06:33 UTC
Permalink
Hi,
Post by Matto Marjanovic
(For those who've lost track: "this method" is to use struct, containing
both the status code and a verbose string pointer, as the return value of
all the sane_() functions.)
Um, except now a string has to be marshalled over the network every time
a sane function is called, and reconstructed on the client side even if
it isn't used.
And after every call to an SANE function, it must be checked for NULL
and otherwise freed. No, I don't think that's the way to go.
Post by Matto Marjanovic
Post by Diego Zuccato
Callbacks always give me nightmares (I'm developing a db async access
infrastructure that interacts with gtk... brr!)
A true asynchronous callback cannot be used with the network protocol.
o Frontend calls sane_func(), hands its callback to network backend.
o Net backend signals remote net frontend "execute sane_func()".
o Remote network frontend calls sane_func(), hands its callback to backend.
o Backend calls network callback to put a string somewhere on remote side.
o Remote network frontend sends that string back along with the response
to "sane_func()" request.
If I understand correctly what happens in the case of auth_callback,
the remote sane_func isn't finished yet, the callback is still running.
Post by Matto Marjanovic
o Network backend calls the client frontend's callback with string,
then finally returns from "sane_func()" request.
First the net backend calls the RPC for sane_func again, saned returns
from the clalbach, the remote frontend returns from sane_func() and
give the result back all the way to the local frontend.
Post by Matto Marjanovic
But this has pretty much all the same problems and lack of benefits as
the above method.
And it's pretty much complictated and means that we need to add
another parameter for each RPC that may return an error status.
Post by Matto Marjanovic
Threading is not a very big issue for SANE.
Matto Marjanovic
2002-12-10 18:03:00 UTC
Permalink
Gooooood morning,
Post by Henning Meier-Geinitz
If I understand correctly what happens in the case of auth_callback,
the remote sane_func isn't finished yet, the callback is still running.
Post by Matto Marjanovic
o Network backend calls the client frontend's callback with string,
then finally returns from "sane_func()" request.
First the net backend calls the RPC for sane_func again, saned returns
from the clalbach, the remote frontend returns from sane_func() and
give the result back all the way to the local frontend.
Yeah, this/that occurred to me later last night.
o When the device backend invokes the callback, saned can return an early
response to the client backend "I'm not done with the last operation,
but please invoke a callback for me."
o The client net backend invokes the real callback, and then sends the
return status to saned in another RPC call "Here is the result of the
callback; now finish what you were doing.".
o Saned returns the result to the device backend, and everything keeps
on going as before (i.e. eventually the backend returns from whatever
sane_func(), and then saned sends a closing response to the client).

Is this how it is implemented? (I took a quick look at the net backend,
and discovered that a quick look is not enough.) Neither the current SANE
standard nor the v2 draft say anything about callbacks.

The big question I have is: How is an asynchronous sane_cancel() invoked???


[getting back to verbose errors...]
...
Post by Henning Meier-Geinitz
As far as I understood the comment about thread-safety (from David
Mosberger-Tang) is about interface definition, not implementation.
...
Post by Henning Meier-Geinitz
void
sane_verbose_error(Sane_Handle h, SANE_String * verbose_message,
SANE_Int lenght)
The the frontend can decide what to do with the string, e.g. print it
later.
I don't see how it helps, and I think it does hurt:

a) This doesn't have any impact on thread safety: this function still cannot
be called while another sane_func(h, ...) is executing --- that other
function might be in the middle of rewriting the backend's "verbose
message buffer".
Furthermore, without the requirement that this function is called
synchronously, it becomes ambiguous which error it refers to.

b) Now the frontend has to preallocate a buffer to receive the message,
which will probably only be read once. How big should that buffer be?
How will the frontend know if the message has been truncated because
its buffer is too small?

In the original version, if the frontend wants to keep the string around
after the next sane_func(h, ...), it can just strdup() it.


Hmm... you know what, we've overlooked something.
What about a verbose error message for those functions which don't require
a device handle?
(back to this in a moment)

...
Post by Henning Meier-Geinitz
As far as I understood the comment about thread-safety (from David
Mosberger-Tang) is about interface definition, not implementation.
So even if the current implementation doesn't allow simuktaneous
operation of SANE function, a later one may do. And we are in trouble
then. All the other function definitions seem to be thread-safe.
Ok, this is worthy of some discussion.

I don't see any current guarantees of thread-safety anywhere in SANE.
On the day that we amend the standard to say "these functions may be
called simultaneously", we will all be screwed anyway, because none of
the backends will have been written with thread-safety in mind.

We need to explicitly define which operations can be called at which times.

Here is some brainstorming:

First, let's consider the device-dependent functions:

SANE_Status sane_control_option(H, )
SANE_Status sane_get_parameters(H, )
SANE_Status sane_start(H, )
SANE_Status sane_read(H, )

void sane_cancel(H, )
SANE_Option_Descriptor * sane_get_option_descriptor(H, )

void sane_verbose_error(H, )
void sane_close(H)

These all act on a particular device handle, H. It should be
perfectly safe to call these functions simultaneously and reentrantly
for different handles, but not necessarily for the same handle.

The first four are all synchronous operations --- it doesn't make
any sense to call any of them simultaneously, for a given handle H.
(E.g. You can't get the scan parameters while you are in the
middle of setting an option, and vice-versa.)

sane_cancel() is signal-safe, and might as well be thread-safe, too.
One should be allowed to call sane_cancel() at any time.

sane_get_option_descriptor() returns a pointer to a descriptor, and
that pointer is guaranteed to be valid as long as the device is
open. So, technically, this function could be thread-safe and
signal-safe, too, since it is just returning pointers which are
presumably allocated once during sane_open().
However, the values of those option_descriptors --- the stuff that
is *pointed to* --- is not guaranteed to be valid for the duration
of a call to sane_control_option(). Setting an option may change
the ranges/etc of other options.
So, the option_descriptors themselves must be accessed synchronously,
more or less, for a given handle H.

sane_verbose_error(), as we've discussed, reflects the return status
of the last synchronous operation, hence it must also be called
synchronously (for a given handle H).

sane_close() invalidates the device, so it also must be called
synchronously. It is probably be a good idea to declare that it
is illegal to call sane_cancel(H, ) during a call to sane_close(H, ).


Ok, now the device-independent functions:

SANE_Status sane_init()
void sane_exit()

SANE_String_Const sane_strstatus()

SANE_Status sane_get_devices()
SANE_Status sane_open()

sane_init() and sane_exit() are definitely synchronous -- one has to be
called before anything else, and one has to be called after everything
else.

sane_strstatus() should be completely thread and signal safe. It is
not even a backend function, technically, and just returns a string
constant.

sane_get_devices() appears to be synchronous, given this language in
its description (4.3.3):
"The returned list is guaranteed to remain unchanged and
valid until (a) another call to this function or (b) a call
to sane_exit() is performed."
Thus, a second thread calling this function could silently invalidate
the list being accessed by the first thread.

sane_open() looks like it *could* be made re-entrant; there doesn't
seem to be anything about the code-flow the prevents it. But this
may require some mutex mechanism in the backend.
However, sane_open() should be declared thread-safe with respect
to the device-dependent functions above.

I don't see why sane_open() would ever need to be called more than
once simultaneously. But I can imagine multi-threading being used
(in a network daemon, for example) such that there was one thread
assigned to each open device, and a master thread in charge of
handling new device requests. The master thread would be the only
thread that ever needed to call sane_open(), once at a time.


Hmmm... so, back to sane_verbose_error(H, ):

sane_init(), sane_open(), and sane_get_devices() all return a
SANE_Status, and for the first two, at least, one might want to see
a more verbose message/explanation from the backend.

If these functions are all required to be called synchronously, then
we could simply say that

sane_verbose_error(NULL, ...)

returns the "most recent global verbose message".

That's what I got so far. Ideas?


-matt "get it in writing" m.
Henning Meier-Geinitz
2002-12-10 23:20:58 UTC
Permalink
Hi,
Post by Matto Marjanovic
Post by Henning Meier-Geinitz
If I understand correctly what happens in the case of auth_callback,
the remote sane_func isn't finished yet, the callback is still running.
Post by Matto Marjanovic
o Network backend calls the client frontend's callback with string,
then finally returns from "sane_func()" request.
First the net backend calls the RPC for sane_func again, saned returns
from the clalbach, the remote frontend returns from sane_func() and
give the result back all the way to the local frontend.
Yeah, this/that occurred to me later last night.
o When the device backend invokes the callback, saned can return an early
response to the client backend "I'm not done with the last operation,
but please invoke a callback for me."
Exactly, there is a parameter that is returned by saned to the net
backend in every function where a callback can be called. If this
string is !=0, the function isn't finished but the callback must be
called.
Post by Matto Marjanovic
o The client net backend invokes the real callback, and then sends the
return status to saned in another RPC call "Here is the result of the
callback; now finish what you were doing.".
o Saned returns the result to the device backend, and everything keeps
on going as before (i.e. eventually the backend returns from whatever
sane_func(), and then saned sends a closing response to the client).
Is this how it is implemented? (I took a quick look at the net backend,
and discovered that a quick look is not enough.)
I think you described the implementation correctly.
Post by Matto Marjanovic
Neither the current SANE standard nor the v2 draft say anything about
callbacks.
SANE_NET_AUTHORIZE *is* described in the network part of the standard.
However, the current implementation doesn't match the standard. At
least, that's my understanding. The standard says, that the operation
has to be retried after SANE_NET_AUTHORIZE. I would have thought that
the whole RPC would have to be called again (e.g. SANE_NET_OPEN), but
the implementation shows that only the response is gotten again.

So I would call this a big bug in either the implementation or the
standard. The standard concerning networking doesn't go into taht much
details, anyway.
Post by Matto Marjanovic
The big question I have is: How is an asynchronous sane_cancel() invoked???
I haven't tried but the code looks like: It isn't. You can't do it.
Cancelling during sane_read seems to work because the data transport
is on a different port. However cancelling sane_start won't work.
Post by Matto Marjanovic
[getting back to verbose errors...]
...
Post by Henning Meier-Geinitz
As far as I understood the comment about thread-safety (from David
Mosberger-Tang) is about interface definition, not implementation.
...
Post by Henning Meier-Geinitz
void
sane_verbose_error(Sane_Handle h, SANE_String * verbose_message,
SANE_Int lenght)
The the frontend can decide what to do with the string, e.g. print it
later.
I'm really no expert in thread-safety at all. In fact, I don't think I
have ever written a threaded program. So I'm just referring to what
David said.
Post by Matto Marjanovic
this function still cannot
be called while another sane_func(h, ...) is executing --- that other
function might be in the middle of rewriting the backend's "verbose
message buffer".
It could lock the buffer.
Post by Matto Marjanovic
Furthermore, without the requirement that this function is called
synchronously, it becomes ambiguous which error it refers to.
That's true.
Post by Matto Marjanovic
b) Now the frontend has to preallocate a buffer to receive the message,
which will probably only be read once. How big should that buffer be?
How will the frontend know if the message has been truncated because
its buffer is too small?
In the original version, if the frontend wants to keep the string around
after the next sane_func(h, ...), it can just strdup() it.
Ok. I don't object.
Post by Matto Marjanovic
Hmm... you know what, we've overlooked something.
What about a verbose error message for those functions which don't require
a device handle?
True, the handle won't work.
Post by Matto Marjanovic
Post by Henning Meier-Geinitz
As far as I understood the comment about thread-safety (from David
Mosberger-Tang) is about interface definition, not implementation.
So even if the current implementation doesn't allow simuktaneous
operation of SANE function, a later one may do. And we are in trouble
then. All the other function definitions seem to be thread-safe.
Ok, this is worthy of some discussion.
[Long and interesting discussion on which operations can be called at
which times snipped]

Well, my conclusion is: don't do it. I mean: don't call the functions
simultaneously. It just too much chaos in my opinion.
If you want to support more then one device in one backend just fork.
Post by Matto Marjanovic
sane_init(), sane_open(), and sane_get_devices() all return a
SANE_Status, and for the first two, at least, one might want to see
a more verbose message/explanation from the backend.
If these functions are all required to be called synchronously, then
we could simply say that
sane_verbose_error(NULL, ...)
returns the "most recent global verbose message".
That's a possibility. But it makes it a bit more complicated.

But with the above stated opinion, we could also just use
sane_verbose_error() (no argument) for everything.

Maybe someone else has better ideas :-)

Bye,
Henning

Martin Kho
2002-12-05 00:11:06 UTC
Permalink
[cut]
Post by Enno Fennema
6) Configuration files
Agree a format for a standard /etc/sane.conf file.
Could by commandline option use other path, etc.
Backends need at least a method to derive the name of the device as used
by the operating system but could benefit from some configuration
information, calibration settings etc.
My preferred format is somewhat like Windows ini files.
[cut]

Hi,

Well, a standard format suggest that all people think the same way. That's
not true and that's exactly why I dislike Windows. The pluriformity of
Unix/Linux with all their different configs keeps me in touch with what I'm
doing. I don't want to be a dumbo, who follows Bill's wishes.

By the way I'm only interested in the stuff that gets my scanner working, so
I don't want to be bored by parameters used in other backends.

Bye

Martin
Dave Close
2002-12-07 05:46:13 UTC
Permalink
Post by Matthew Duggan
Post by Henning Meier-Geinitz
SANE_String_Const sane_verbose_error(Sane_Handle h)
I like this, and it's pretty much what I was looking for. I only have
one criticism from a backend writing point of view: What happens if the
-----
This function returns a verbose description of the latest error, or an
empty string if no error has occured.
How do you all propose to handle multiple calls to sane_verbose_error()
without any other intervening SANE function call? Does calling this
function "reset" in some sense the understanding of what was the latest
error, or does it return the same result repeatedly - or should the
standard specify?

This thought occurs because I understand the proposal to mean than any
function which returns some SANE_STATUS_* resets the "latest" error
state. How about SANE functions which don't return a SANE_STATUS_*,
like sane_verbose_error()? In some sense, they do return a status, even
if only by returning a null string.
--
Dave Close, Compata, Costa Mesa CA +1 714 434 7359
***@compata.com ***@alumni.caltech.edu
"Everything that can be invented has been invented."
-- Charles H. Duell, Commissioner, US Patent Office, 1899
Loading...