[comp.unix.wizards] need help porting SysV driver to Berkeley

fred@rover.UUCP (Fred Christiansen) (08/28/87)

a co-worker is attempting to port a driver from System V/68 (SysV)
to a Sun (Berkeley).  he brought this question to me, which i can't
answer.  can someone help?

if a user does a read(), like:
	n = read( fd, buf, sizeof( buf ) );
then inside a SysV kernel, u.u_base is the address of `buf', while
u.u_count is `sizeof( buf )'.  after the copyout(), i decrement
u.u_count by the number of bytes copied.  then, by someone between
the driver read return and the read() return, someone subtracts
u.u_count from `sizeof( buf )' so that the return value of read()
is the number of bytes read.

the co-worker has read Sun's device driver writer's guide, but
does not understand (so cannot explain to me) the use of `struct uio'
and `struct iovec'.  under SunOS, read()'s are returning 0.
what needs to get set so the real number of bytes read gets returned.

thanks!
-- 
Fred Christiansen ("Canajun, eh?")     | seismo!noao!mcdsun!nud!fred (ARPA gate)
Motorola Microcomputer Div., Tempe, AZ |       utzoo!mnetor!mot!fred
"The greatest thing a father can do for|              ihnp4!mot!fred
his children is to love their mother." |  hplabs!motsj1!ellygroup ...

guy%gorodish@Sun.COM (Guy Harris) (08/29/87)

> if a user does a read(), like:
> 	n = read( fd, buf, sizeof( buf ) );
> then inside a SysV kernel, u.u_base is the address of `buf', while
> u.u_count is `sizeof( buf )'.

4.2BSD got rid of this crud (Global Variables Considered Harmful), and instead
passes the equivalent information in the "uio" structure pointed to by the
appropriate argument to the device driver (or other) routine.

If your driver used to use "iomove", to make it work in a system that handles
this in a 4.2BSD style it should be changed to use "uiomove".  "iomove" handles
updating "u.u_count"; "uiomove" handles updating "uio_resid" for the "uio"
structure pointed to by the argument.  The "uio_resid" member of that "uio"
structure contains the byte count for the "read" or "write" (or the total byte
count for scatter/gather operations), and should be decremented as data is
transferred.  The value when the operation is complete is what the "read",
"write", etc.  code uses to determine how much data is actually transferred.

The "iomove" call

	iomove(addr, count, flag)

turns into

	error = uiomove(addr, count, uioflag, uio)

"uio" is the "uio" structure pointer passed to the driver.  "uioflag" is a flag
(an "enum", actually) indicating the type of I/O operation.  If "flag" was
B_READ, "uioflag" should be UIO_READ; if "flag" was B_WRITE, "uioflag" should
be UIO_WRITE.

The return value from "uiomove" is either 0 if there were no errors, or a UNIX
"errno" code if there was an error; many routines in 4.2BSD were changed to
return error codes rather than setting "u.u_error" (device driver routines,
for example, are expected to return 0 or an error code, rather than returning
no value and setting "u.u_error" to the error code if an error occurred).

If the driver *didn't* use "iomove", but did the operations itself, the
conversion can get a bit complicated *if* they intend to support the "readv"
and "writev" system calls, which require the full "iovec" baggage.  They would
basically have to manually step through the "iovec" vector themselves.  If they
don't intend to support those calls, they can just cheat and use the first
member of the "iovec" structure pointed to by the "uio_iov" member of the "uio"
structure (which, for "read" or "write", is the *only* member).

Essentially, "uio_resid" is the moral equivalent of "u.u_count", "uio_offset"
is the moral equivalent of "u.u_offset", and in SunOS 3.2 and later (but not in
4.[23]BSD) "uio_fmode" is the moral equivalent of "u.u_fmode".

"uio_seg" (renamed "uio_segflg" in 4.3BSD, and most likely in a future SunOS
release), is the moral equivalent of "u.u_segflg", if the driver cares about
it.  The values are the same: 0 for user data space, 1 for system space, and
(in 4.3BSD) 2 for user I space (if you have separate I&D space, which we
don't).  However, you should use the #defines in "uio.h" instead to make your
code more readable.

"u.u_base" does not have an equivalent in the "uio" structure, since the I/O
operation could conceivably consist of several transfers to several
non-contiguous areas.  Each area is described by an "iovec" structure in the
array whose first member is pointed to by the "uio_iov" member; "uio_iovcnt" is
the number of members in that array.  Each "iovec" structure has an "iov_base"
member, which is the "u.u_base" *for that area*, and an "iov_len" member which
is the length of that area.  (When the driver routine is called,
"uio_resid"s value is equal to the sum of the values of the "iov_len" members
of all the "iovec" structures.)

So the problem is probably that the driver isn't updating the "uio_resid"
member of the "uio" structure passed to it.  It should subtract the number of
bytes it reads or writes from that value.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com