0

i am trying to figure out how the code behind a basic kernel driver works.

I have the following struct:

    static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
};

And my dev_open function is defined as:

static int     dev_open(struct inode *, struct file *);

Now Im also familiar with the fact that the prototype for opening a device file is defined in the linux/fs.h:

http://lxr.linux.no/linux+v3.10/include/linux/fs.h#L1517

Here is the specific line from that link:

int (*open) (struct inode *, struct file *);

Now my question is what is the relationship between .open = dev_open, and int (*open) (struct inode *, struct file *); which is defined in linux/fs.h? Is it passing the address of dev_open to the function pointer int (*open) defined in the linux/fs.h? There must be some relation or what is the point of defining the struct fops as type "file operation"?

A similar question was asked and answered here but i feel that my question was left out: File operations in drivers

Thank you

programmer25
  • 375
  • 1
  • 2
  • 14
  • The line `int (*open) (struct inode *, struct file *);` defines a **field** for the `struct file_operations`. Name of the field is `open`, type of the field is a **pointer to function**, which accepts 2 arguments (of type `struct inode*` and `struct file*`) correspondingly and returns `int`. – Tsyvarev Aug 17 '20 at 12:38
  • It may help to know that the member initializer `.open = dev_open` means exactly the same thing as `.open = &dev_open`. C has weird rules for the unary `*` and `&` operators on functions. In both case, `fops.open` is being initialized to point to the `dev_open` function. – Ian Abbott Aug 17 '20 at 12:45
  • In `struct file_operations`, The `open` member needs to be declared explicitly as a function pointer `int (*open)(struct inode *, struct file *);`. Members of a struct or union type can have "pointer to function" type, but not "function" type, so the `open` member cannot be declared as `int open(struct inode *. struct file *);`. – Ian Abbott Aug 17 '20 at 14:22

1 Answers1

0

I think this question is more about C than the Linux kernel.

Members of structure or union types cannot have function type, but they can have pointer to function type. For example, in the Linux kernel, the open member of struct file_operations needs to be declared with a pointer to function type: int (*open)(struct inode *, struct file *);. Declaring the member as int open(struct inode *, struct file *); is an error.

In this variable definition in Linux kernel code:

static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
};

Incidentally, the above should normally have the owner member initialized like so:

   .owner = THIS_MODULE,

The expressions dev_open, dev_read, dev_write and dev_release are function designators being used as assignment expressions to initialize the members of fops. A function designator is an expression that has function type. Unless it is the operand of sizeof, _Alignof, or the unary & operator, a function designator is converted to a pointer to function type. Therefore, the above definition of variable foo is exactly equivalent to:

static struct file_operations fops =
{
   .open = &dev_open,
   .read = &dev_read,
   .write = &dev_write,
   .release = &dev_release,
};

(Don't forget to also initialize .owner = THIS_MODULE,.)

There, the function designators are operands of the unary & operator and so are not converted to pointer to function types implicitly, but the & operator is converting them to pointer to function types explicitly.

After the above initialization of fops, rc = fops.open(inode, file); indirectly calls dev_open(inode, file) and assigns the return value to rc. You may sometimes see this written in an older style: rc = (*fops.open)(inode, file);. They both do the same thing. The operand of the function call operator ( ) is in fact always a pointer to a function. In the case of rc = (*fops.open)(inode, file);, fops.open has a pointer to a function type. (*fops.open) dereferences fops.open to a function type but since (*fops.open) is a function designator it is implicitly converted back to a pointer to function type before the function call. Similarly, in the direct call rc = dev_open(inode, file);, dev_open is a function designator and so has a function type, but is implicitly converted to a pointer to function type before the function call.

Ian Abbott
  • 15,083
  • 19
  • 33
  • I believe my confusion is with the left hand side of the equation. Is it true that `.open = dev_open` is the short form of `.(*open)(struct inode *, struct file *)=dev_open` ? – programmer25 Aug 17 '20 at 17:23
  • @programmer25 No, `.(*open)(struct inode *, struct file *)=dev_open` is invalid syntax. The `open` member already has a type. It just needs to be initialized (or assigned) with a value of compatible type. The `open` member has type `int (*)(struct inode *, struct file *)` (a _pointer to function_ type). `dev_open` has type `int (struct inode *, struct file *)` (a _function_ type) but is converted to `int (*)(struct inode *, struct file *)` (a _pointer to function_ type matching the `open` member), so is compatible with the `open` member. – Ian Abbott Aug 18 '20 at 09:28