🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: comp.sys.next.programmer
2 messages
2 total messages Started by Clifford T. Matthews Wed, 19 Aug 1992 22:09
Re: _IO, _IOR, _IOW and _IOWR functions
#1231
Author: Clifford T. Matthews
Date: Wed, 19 Aug 1992 22:09
125 lines
7463 bytes
Dear curious/bored folks,

The "routines" that the subject line alludes to are actually macros that
are used to create "magic cookies" for the ioctl system call.  By magic
cookie, I mean a unique integer identifier.  Here's a brief top-of-my-head
history of ioctls and such:

In general, UNIX allows you to do three different things with devices:
read, write, and "everything else".  Reading and writing need no further
explanation; "everything else" (done with the "ioctl" system call) varies
from device to device, but includes things like setting the input and output
speed for terminals, setting the density for tapes as well as werid stuff
like raw scsi commands (you need root to do these, which is good).

In general, UNIX has little abbreviations for the devices that it supports.
The NeXT has support for things like "sd" (SCSI drive), "od" (optical drive),
"zs" (zilog serial communications controler (terminal port)) and so on.  When
you need to do low-level accesses to these things you wind up writing code
that looks like:

	int filedescriptor;
	char buf[1024];

	filedescriptor = open("/dev/rsd0h", O_RDONLY);
	nread = read(filedescriptor, buf, sizeofbuff);
	...
	err = close(filedescriptor);
	...

In the above example "/dev/rsd0h" identifies that you want to open up
the first logical SCSI drive (drive 0).  The "h" means the special
partition that allows you to get to normally inaccessible portions of
the disk and the "r" preceeding the "sd" means you want "raw" access,
i.e. you'll be bypassing the normal UNIX block buffering cache.

You can read about raw SCSI access in the "sd" man page.  If you do, you
will see that you can do other things with disks, like eject them.
To do so, you use the DKIOCEJECT ioctl argument, as in:

	err = ioctl(filedescriptor, DKIOCEJECT, (char *) 0);

In the above example the third argument to the ioctl is zero, cast as a
pointer to char.  We do that because DKIOCEJECT doesn't need any extra
information to do it's job.  The file descriptor that is passed in is
sufficient to identify which disk to eject.  However, some ioctls need
extra information, or return extra information, or both.  We're now
ready to look at _IO, _IOR, _IOW and _IOWR.

It turns out that if you "grep DKIOCEJECT /usr/include/nextdev/*.h",
you'll see that DKIOEJECT is a #define that looks like this:

#define     DKIOCEJECT      _IO('d', 21)                    /* eject disk */

_IO is just a macro that generates a unique number that the implementors
of ioctl for disks can look at.  The 'd' is a single character that serves
to identify what kind of device the ioctl is relevant to 'd' in this case
stands for disk.  As long as people are consistent and there's no collision,
it doesn't matter what the letter is.  The second argument, 21, is just
a unique number that keeps DKIOCEJECT separate from the other 'd' ioctls.
If you "grep _IO /usr/include/nextdev/disk.h", you'll get this for output:

#define DKIOCGLABEL     _IO('d', 0)                     /* read label */
#define DKIOCSLABEL     _IO('d', 1)                     /* write label */
#define DKIOCGBITMAP    _IO('d', 2)                     /* read bitmap */
#define DKIOCSBITMAP    _IO('d', 3)                     /* write bitmap */
#define DKIOCREQ        _IOWR('d', 4, struct disk_req)  /* cmd request */
#define DKIOCINFO       _IOR('d', 5, struct drive_info) /* get drive info */
#define DKIOCZSTATS     _IO('d',7)                      /* zero statistics */
#define DKIOCGSTATS     _IO('d', 8)                     /* get statistics */
#define DKIOCRESET      _IO('d', 9)                     /* reset disk */
#define DKIOCGFLAGS     _IOR('d', 11, int)              /* get driver flags */
#define DKIOCSFLAGS     _IOW('d', 12, int)              /* set driver flags */
#define DKIOCSDCWIRE    _IOW('d', 14, struct sdc_wire)  /* sdc wire memory */
#define DKIOCSDCLOCK    _IO('d', 15)                    /* sdc lock */
#define DKIOCSDCUNLOCK  _IO('d', 16)                    /* sdc unlock */
#define DKIOCGFREEVOL   _IOR('d', 17, int)              /* get free volume # */
#define DKIOCGBBT       _IO('d', 18)                    /* read bad blk tbl */
#define DKIOCSBBT       _IO('d', 19)                    /* write bad blk tbl */
#define DKIOCMNOTIFY    _IOW('d', 20, int)              /* message on insert */
#define DKIOCEJECT      _IO('d', 21)                    /* eject disk */
#define DKIOCPANELPRT   _IOW('d', 22, int)              /* register Panel 
#define DKIOCSFORMAT    _IOW('d', 23, int)              /* set 'Formatted' flag 
#define DKIOCGFORMAT    _IOR('d', 23, int)              /* get 'Formatted' flag 

See, someone just arbitrarily assigned numbers for the various special things
that you may want to do to disks.  Not all of the #defines are _IO, there's
also _IOR, _IOW and _IOWR.  Those used when the ioctl needs to read information
from the device, write information to the device or both, respectively.
With those three variants of _IO, a little more is being done then creating
a unique number; the nunmber of bytes that need to be copied between
"user space" (i.e. your program) and "kernel space" (i.e. the UNIX operating
system) is also rolled into the "magic cookie".  Specifically those three
variants use sizeof(arg3) to determine how many bytes are going to need to
be transferred.  So in reality, the magic cookie consists of four things:

	The type of device we're operating with/on
	A unique number identifying the type of operation we're doing
	a pair of direction flags no direction, read, write, both
		and
	the number of bytes that will be moved.

So now the question is what can you do with _IO and friends?  Basically Nothing.
There is no point to rolling your own "magic cookies", because the device
drivers only know about the ioctls that are available through their (slightly)
less cryptic names (i.e. "DKIOCEJECT" vs. "_IO('d', 21)").  However, there
are rare times when you may want to know what kind of "ioctl"s an application
is using.  In that case, you can find the #defines for "_IO" and friends and
figure out what the various magic cookies for the various ioctl calls, and
then use gdb on the mystery program and take a look at the args on the stack
when ioctl is called.

As always, if a routine requires super-user privs to function, you should be
very wary of using it; when you're doing low-level accesses to disks and
what not, you can really do a lot of damage in a short time.

BTW, I enjoy learning about things via the net, but I really like to see
references rather than information; I try to do unto others ..., but in
this case I'm not really sure what a relevant reference would be, perhaps
a book about programming the kernel, or the design/history of UNIX.  The
trouble is, I was hacking UNIX kernels before those books were published,
so I'm not adequately familar with the books to tell you whether or not
they go into such little details.

	--Cliff[ord T. Matthews]
	ctm@ardi.com
Re: _IO, _IOR, _IOW and _IOWR functions
#1243
Author: Brian Willoughby
Date: Thu, 20 Aug 1992 06:52
71 lines
3327 bytes
Now that the curious seem to have turned the discussion to ioctl() calls, I  
would like to take the opportunity to re-post a question of mine about directly  
accessing the Intel 82077 Floppy Disk Controller chip.

For compatibility with a 10-sector per track 3.5" floppy format, I need to send  
a READ command directly to the controller chip with DMA enabled in order to  
read a sector written with an address tag of sector 0.  Reading multiple  
sectors with one call would be nice as well.

I have learned a great deal from the /NextDeveloper/Examples/Floppy code and  
have successfully been able to read sectors 1 through 9 with a higher level  
function {namely ioctl(fd, FDIOCRRW, &rawio)}, but apparently I cannot read  
sector 0 without going to the lowest level because FDIOCRRW is block based and  
assumes that each track starts with sector 1 when it determines the cylinder.

Here is what I have coded so far.  do_ioc() is taken from fdform.c with  
improved error reporting.  I tried sectors 0 and 1, combined with tracks 0 and  
1, for transfers of 1, 9 and 10 sectors without any luck.

| #include <sys/file.h>
| #include <sys/ioctl.h>
| #include <nextdev/fd_extern.h>
| #include <nextdev/disk.h>
| #include <nextdev/dma.h>
| 
| 	bzero(&ioreq, sizeof(struct fd_ioreq));
| 	ioreq.density = FD_DENS_1;
| 	ioreq.timeout = 5000;
| 	ioreq.command = FDCMD_CMD_XFR;
| 	pcmd->mfm = 1;
| 	pcmd->opcode = (read_flag ? FCCMD_READ : FCCMD_WRITE);
| 	pcmd->hds = head;
| 	pcmd->drive_sel = 0;
| 	pcmd->cylinder = cylinder;
| 	pcmd->head = head;
| 	pcmd->sector = sector;
| 	pcmd->sector_size = pfi->sectsize_info.n;
| 	pcmd->eot = EOT;
| 	pcmd->gap_length= pfi->sectsize_info.rw_gap_length;
| 	pcmd->dtl = 0xff;
| 	ioreq.num_cmd_bytes = SIZEOF_RW_CMD;
| 	ioreq.addrs = (caddr_t)addrs;
| 	ioreq.byte_count = DMA_ENDALIGN(int, sector_count * SECT_SIZE);
| 	ioreq.num_stat_bytes = SIZEOF_RW_STAT;
| 	ioreq.flags = (read_flag ? FD_IOF_DMA_RD : FD_IOF_DMA_WR);
| 	rtn = do_ioc(fd, &ioreq);

And here are the results interpreted by my error printing routines (if you can  
answer my question, you might be able to understand these based upon knowledge  
of the Intel controller):

| Accessing floppy disk volume 0
| ...Expected stat byte count = 0x7
|    received stat byte count = 0x0
| ...Unexpected status: a
| FDR_BADPHASE:   controller changed phase unexpectedly
| ...Read/Write Failed
| Status Register 0:
| Head Address 0
| Drive Select 0

Has anyone successully used the ioctl(fd, FDIOCREQ, fdiop) call to read and  
write a floppy?  Even code which uses FDIOCREQ to access a standard 9-sector  
disk would be useful.

Is NeXT listening?  You may be the only resource that can solve this...
-- 
Brian Willoughby	Software Design Engineer, BSEE
BrianW@SoundS.WA.com	SoundSoftware
NeXTmail welcome
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads