[snip]
An understanding of this C code for starters. There is something called
ioctl in kernel code. But I guess it has nothing to do with this code and
there are no functions in it.
Here's how it works: The ioctl() C library function generates a system
call in a completely architecture-dependent manner. The system call is
decoded by the kernel which will call some internal function which may
or may not be named "ioctl". Let's assume that it is, although it could
just as easily be called "do_ioctl" or "sys_ioctl", or anything else.
The internal ioctl() function will receive as one of its arguments a
file descriptor. It will use the file descriptor to look up some sort
of device control structure.
(I'm being deliberately vauge here, because I've worked on more than
one operating system in my life, and they all do things in their own way.)
The device control structure will either have an "int (*ioctl)(...);" entry
in it, or it will point to some other structure that does. One way or another,
the kernel will drill down through the data structures until it finds that
entry.
The kernel has no idea what that entry points to or what it's called. It may
in fact not point to anything (null pointer), in which case the kernel will
return an ENOTTY error (or something else, depends on the os version).
If the ioctl pointer is non-null, the kernel will assume that it points to a legitimate
ioctl function provided by a device driver, and call it. What happens next depends
on the driver in question.
By convention, a device driver named "foo" will usually name its ioctl function
"foo_ioctl()", but that's not a requirement. You could just as easily name it
"ioctl()" as long as you made it private to your driver module. (This is bad practice,
however, since it makes debugging harder.)
Under Linux especially, it gets more complicated because Linux loves to group
similar devices together and share as much code as possible, via sub-devices
and other techniques. For instance, caling ioctl() on a block device will
result in a call to the block driver ioctl() function which may handle the
call itself, or pass it on to device-type-specific (CD, Disk, etc.) ioctl
function, which in turn may pass the request onto a driver-specific ioctl
function and so forth. All of these calls will be made through further
"int (*ioctl)()" pointers.
I have never used a int (*ioctl) or anything
related. So with the code on this page how would I pass something from
(*ioctl) or from void (*disconnect) ?
One of the white-light epiphanies I experienced when I was first exposed
to C was the insanely radical notion that you could have a *pointer*
to a *function* and change that pointer any time you wanted. After that
moment, I never looked back at the legacy languages I had been programming
in previously.
The way you use such a function goes back to my previous post, where I wrote
int (*foo)()
(*foo) is a function that returns int.
So what you do is refer to (*foo) as a function call.
For example:
int (*ioctl)(args); /* "ioctl" is a pointer to a function. It
* does not point to anything yet.
/
ioctl = &foo_ioctl; /* "ioctl" now points to the
* foo_ioctl() function.
*/
(*ioctl)(args); /* call foo_ioctl() /
(A couple of notes:
1) The compiler knows that if you refer to a function name alone, you
mean the address of that function, so "&foo_ioctl" can be written as
simply "foo_ioctl".
2) The compiler also knows what to do if you treat a pointer to a function
as if it were the name of a function, so "(*ioctl)(args)" can be written
as simply "ioctl(args)"
So I could have written:
int (*ioctl)(args);
ioctl = foo_ioctl;
ioctl(args);
I find that the shorter form is more common and looks better, but it does
require you to know in your head what are simple function names and what
are function pointers.
A more useful example:
int do_ioctl(fd, args)
{
struct dev_info *dev = get_dev_from_fd(fd);
struct file_operations *fops;
if (dev == NULL || dev->fops == NULL)
return ENOTTY;
fops = dev->fops;
if (fops->ioctl == NULL)
return ENOTTY;
else
return fops->ioctl(args);
}
Or even (assuming I'm confident I won't hit any NULL pointers):
int do_ioctl(fd, args)
{
return get_dev_from_fd(fd)->fops->ioctl(args);
}
struct file_operations *fops;
Somewhere there is a struct called file_operations that fops points to. Am I
making sense?
Yes. "file_operations" is the *type* of struct that fops points to.
We neither know nor care what its actual name is.