[sane-devel] Re: Sane on Ultra Sparc

abel deuring a.deuring@satzbau-gmbh.de
Fri, 10 Jan 2003 17:38:12 +0100

"T. Ribbrock" wrote:
> On Fri, Jan 10, 2003 at 01:46:55PM +0100, T. Ribbrock wrote:
> [...]
> > Hm. I am by no means an expert in any way, but I'd naively assume that
> > this type of problem must have been encountered (and maybe solved) by
> > other programs as well? I wonder how things like cdrecord or suchlike
> > work on these platforms. Mind you, I'm just brainstorming a bit... :-}
> [...]

Hmmm. Are you sure that cdrecord works as a 32 bit application on a 64
bit Linux kernel using the SG interface?

> While I'm at it (the brainstorming, I mean) - I had a look at the
> cdrecord source - besides the fact the Joerg Schilling doesn't seem to
> happy with Linux' sg driver :-} I noticed that he's got some code in
> his scsi-linux with regard to misalignment, especially for non-ix86 -
> could that be something? Shot in the dark, I know...

Well, some issues regarding misalignment might emerge, when you try to
build 64 bit-aligned 64-but-variables in a 32-bit environment.

But the point, where the SG driver complains, is different: Sane uses
the SG3 interface. This means to define a variable of type strct
sg_io_hdr, defined in sg.h, to fill in the proper value and to issue a
write() call for an SG device with this variable.

struct sg_io_hdr is defined as:

typedef struct sg_io_hdr
    int interface_id;           /* [i] 'S' for SCSI generic (required)
    int dxfer_direction;        /* [i] data transfer direction  */
    unsigned char cmd_len;      /* [i] SCSI command length ( <= 16
bytes) */
    unsigned char mx_sb_len;    /* [i] max length to write to sbp */
    unsigned short iovec_count; /* [i] 0 implies no scatter gather */
    unsigned int dxfer_len;     /* [i] byte count of data transfer */
    void * dxferp;              /* [i], [*io] points to data transfer
                                              or scatter gather list */
    unsigned char * cmdp;       /* [i], [*i] points to command to
perform */
    unsigned char * sbp;        /* [i], [*o] points to sense_buffer
memory */
    unsigned int timeout;       /* [i] MAX_UINT->no timeout (unit:
millisec) */
    unsigned int flags;         /* [i] 0 -> default, see SG_FLAG... */
    int pack_id;                /* [i->o] unused internally (normally)
    void * usr_ptr;             /* [i->o] unused internally */
    unsigned char status;       /* [o] scsi status */
    unsigned char masked_status;/* [o] shifted, masked scsi status */
    unsigned char msg_status;   /* [o] messaging level data (optional)
    unsigned char sb_len_wr;    /* [o] byte count actually written to
sbp */
    unsigned short host_status; /* [o] errors from host adapter */
    unsigned short driver_status;/* [o] errors from software driver */
    int resid;                  /* [o] dxfer_len - actual_transferred */
    unsigned int duration;      /* [o] time taken by cmd (unit:
millisec) */
    unsigned int info;          /* [o] auxiliary information */
} sg_io_hdr_t;

So, basically bunch of pointers, integers and chars.

What sanei_scsi.c does is this (things like queue manegement, sg driver
version detection and other stuff stripped off):

struct sg_io_hdr sg_hdr;
int fd; /* file handle of an SG device file */
int res;
/' init sg_hdr */

res = write(fd, &sg_hdr, sizeof(sg_hdr));

if (res < 0) {/* bail out */}

One of the first things done by the SG driver is to check, if the 3rd
parameter of the write call, i.e., the write size, equals
sizeof(sg_io_hdr). If this is not the case, errno is set to EINVAL (22
for SPARC Linux). And exactly this happens, because sizeof(sg_io_hdr) is
different for 32 bit and 64 bit programs.

So, what you can do:

1. compile Sane as a 64-bit application. In this case, sizeof(sg_io_hdr)
should be the same both in the application and in the kernel. (OK, I
understand that this might waste some disk space for duplicates of libc
and friends.)

2. Don't use struct sg_io_hdr as defined in sg.h, but build your own
struct, which is 64-bit-aware. I don't have any experience running 32
bit apps on top a 64 bit kernel, so I can't give any precise recipes.
But let's assume that you can use something like a "long pointer", i.e.
"long void * usr_ptr;"

Then you can define a replacement for struct sg_io_hdr, which might be
accepted by the SG driver. This probably also involves to check, if ints
are 32 bit or 64 bit values, and if padding bytes must be inserted for
example between "unsigned short iovec_count;" and "unsigned int
dxfer_len;" in order to maintain proper 64 bit alignment. When you got
all this resolved, you can try to call the SG driver from Sane again.
For test purposes, you can add a few printk statements to the SG driver
and some DBG statements to sanei_scsi.c in order to check, if the values
are properly passed. 

Another problem with might be, how the 32bit application address space
is mapped into the kernel's 64 bit address space. Look at the kernel
functions __copy_from_user and __copy_to_user to figure out, if the
pointers might need any special treatment.

3. Use the old SG interface. To do that, figure out, if the kernel works
with 32 bit ints or with 64 bit ints. 

3.1 If the kernel works with 32 bit ints, simply change all "#ifdef
SG_IO" in sanei_scsi.c to "#ifdef XSG_IO" or so and compile Sane again.
Sane will then use the old SG interface, which should be a bit easier to

3.2 If the kernel works with 64 bit ints, figure out, how you can 
define a 32-bit replacement for struct sg_header (also defined in sg.h),
so that this new struct defines somethingm which, when thrown into a
write() call, will be accepted by the SG driver as a proper 64-bit
struct sg_header. This will probably mainly mean to replace "int" with
"long int". But remember that you might need to insert some padding
bytes in order to achive proper 64-alignments. If everything looks fine,
proceed as in 3.1.

I'd prefer (1) or (2), because the SG3 interface is much cleaner than
the old interface, but it might be a bit easier, because the the old
interface did paass any pointers to the driver.