Interface of the driver

John Baldwin jhb at freebsd.org
Thu Nov 3 06:25:05 PST 2005


On Thursday 03 November 2005 06:05 am, Yong Ma wrote:
> ----- Original Message -----
> From: "M. Warner Losh" <imp at bsdimp.com>
> To: mayong at mail.com
> Subject: Re: Interface of the driver
> Date: Wed, 02 Nov 2005 21:36:24 -0700 (MST)
>
> > 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
>
> Thank you very much,it is just what I need!
> I did as you said but came across some problems:
> My test application is as follows(ioctltest.c):
>
>       1 #include <sys/ioccom.h>
>       2 #include <string.h>
>       3 #include <fcntl.h>
>       4 #include <unistd.h>
>       5 #include <err.h>
>       6
>       7 #define ENCRYPTFILE _IOW("s", 3, struct ioctlinfo)
>       8
>       9 struct ioctlinfo{
>      10     unsigned long addr;
>      11     char *buffer;
>      12 };
>      13
>      14 int main(void){
>      15   int fd;
>      16   struct ioctlinfo info;
>      17   info.addr=0;
>      18   char *ibuffer="Ioctl test!\n";
>      19   printf("Ioctltest:%s\n",ibuffer);
>      20   info.buffer=ibuffer;
>      21   fd=open("/dev/sjy22b0",O_RDWR);
>      22   if(fd==-1)
>      23     err(1,"open");
>      24   if(ioctl(fd,ENCRYPTFILE,&info)==-1)
>      25     err(1,"CRYPTFILE!");
>      26   close(fd);
>      27   return (0);
>      28 }
>
> A really simple one:),and the ioctl() in driver adjust as this:
> ...
>     313         case    ENCRYPTFILE:
>     314                         ioinfo=(struct ioctlinfo *)addr;
>     315                         printf("Ioctlinfo :\n
> addr=%ld\nbuffer:%s\n",ioinfo->addr,ioinfo->buffer); 316
>     317                         break;
> ...
> but when I complie the test application(ioctltest.c),it told me:
>
>
> ioctltest.c: In function `main':
> ioctltest.c:24: error: invalid operands to binary <<
> *** Error code 1       ~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> what's wrong?did I lose any headers or anything else?

Use 's' rather than "s" in _IOW.  Also, ioctl() is just going to copy in the 
info structure, it's not going to follow pointers inside that structure for 
you.  You would have to copy that in from userland explicitly.  Something 
like:

	char buffer[MAXPATHLEN];

	...

	case ENCRYPTFILE:
		ioinfo = (struct ioctlinfo *)addr;
		error = copyinstr(info->buffer, &buffer, sizeof(buffer), NULL);
		if (error)
			return (error);
		printf("Ioctlinfo:\naddr=%ld\nbuffer:%s\n", info->addr, buffer);
		break;

Alternatively, you could change the info struct to contain the buffer rather 
than the pointer like so:

	struct ioctlinfo {
		unsigned long addr;
		char buffer[MAXPATHLEN];
	};

	And in userland:

	info.addr = 0;
	strcpy(ibuffer, info.buffer);
	ioctl(fd, ENCRYPTFILE, &info);

	And in kernel you can do:

	case ENCRYPTFILE:
		ioinfo = (struct ioctlinfo *)addr;
		printf("IoctlInfo:\naddr=%ld\nbuffer:%s\n", info->addr, info->buffer);
		break;

-- 
John Baldwin <jhb at FreeBSD.org>  <><  http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve"  =  http://www.FreeBSD.org


More information about the freebsd-drivers mailing list