Interface of the driver

M. Warner Losh imp at bsdimp.com
Wed Nov 2 20:38:28 PST 2005


In message: <20051103025038.CAF618401E at ws1-5.us4.outblaze.com>
            "Yong Ma" <mayong at mail.com> writes:

: My driver for the cryptographic accelerator is almost finished, the
: next job will be the development of user interfaces.  I've got some
: problems now:  Is the ioctl() function the only way in which the user
: implications interact with the driver(mine is a KLD module)?

Yes and no.  It is the typical way that most drivers choose to
interact with userland.  In this model, you have to add a device entry
(see make_dev(9)) wher you give it a 'cdevsw' structure.  This is the
stylized way of doing function potiners for entry points (virtual
functions in C++ use a similar mechanism) to the driver.  You create a
device, the user opens it and then issues requests.  In your case
ioctl (although I suspect read and write too).

: If so,
: how can I pass some arguments(such as my private key or the file
: name that will be encrypted)?

There are a number of macros which help you to pass arguments.  The
ioctl function itself takes a 'function' and a 'arguement'.  Since the
'function' also encodes the size and direction of the ioctl, it can
copy the arguments into the kenrel for you and copy them out when you
are done.

: Can I call some function(with
: arguments) defined in the driver in my user implications, and how?

Most drivers that define an ioctl interface have a fooio.h header.
fooio.h will include <sys/ioccom.h> and then device their command
numbers.  For example, mtio.h defines mag tape ioctls:

/* mag tape io control commands */
#define	MTIOCTOP	_IOW('m', 1, struct mtop)	/* do a mag tape op */
#define	MTIOCGET	_IOR('m', 2, struct mtget)	/* get tape status */
/* these two do not appear to be used anywhere */
#define MTIOCIEOT	_IO('m', 3)			/* ignore EOT error */

The first one 'writes' a magtape operation to the kernel.  This is
contained in a struct mtop.  The struct mtop is defined in mtio.h, but
I've omitted it here.  The driver gets the MTIOCTOP operations, does
some casting and then extracts data from the passed in structure.  The
kernel code looks something like:

static int
saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
{
	int err;
...
	err = 0;
	switch (cmd) {
...
	case MTIOCTOP:
	{
		struct mtop *mt = (struct mtop *) arg;
		err = sa_process_mt(mt);
		break;
	}
	default:
		err = ENOTTY;
		break;
	}
...
	return (err);
}


The sa_process_mt routine would be something the driver wrote to
interpret the struct mtop from this call.  Similarly for other ioctls.

_IOW() is for defining ioctls that write data into the kernel only.
Most 'set' ioctls are IOW.  _IOR are used to extract data from the
kernel.  Most 'get' ioctls are IOR.  _IO ioctls are ioctls that pass
no data to the kernel and do some predefined thing (like set the
ignore eof flag above).  _IOWR are used to pass data both ways, into
and out of the kernel.  These are used for more complicated 'get'
routines or to communicate data back out of the kernel.

In userland, the user would just include the fooio.h file and use it
like they would for any other ioctl:

struct foo_state
{
	...
};

#define FOOIOCGETSTATE	_IOR('f', 1, struct foo_state);
...
    struct foo_state state;
    int foo;

    fd = open("/dev/foo0", O_RDWR);
    if (fd == -1)
	err(1, "open");
    if (ioctl(fd, FOOIOCGETSTATE, &state) == -1)
	err(1, "IOOIOCFETSTATE");
    /* Do something with the state */
    close(fd);
...

hope this helps..

Warner


More information about the freebsd-drivers mailing list