(a bit involved so bear with me, i really want to figure out how
this works.)

  i'm following the sample code in the LDD3 book for the "scull"
driver, and i *think* i know the answer to this but i just want to
make sure.  it has to do with how defining a new character device and
opening it manages to set the i_cdev pointer back in the inode
structure.

  first, from <linux/fs.h>, we have the definition of an inode, with
only the relevant fields that i care about:

  struct inode {
        ...
        union {
                struct pipe_inode_info  *i_pipe;
                struct block_device     *i_bdev;
                struct cdev             *i_cdev;
        };
        ...

  so, depending on the type of file, only one of those pointers will
be valid.  but, initially, if nothing has opened the file represented
by this inode yet, none of those pointers will point at anything
meaningful, right?  so, in the beginning, the pointer i_cdev will not
have a valid address.  so far, so good?  now consider how this relates
to the "scull" example from LDD3.

  here's the structure for representing that new kind of character
device:

  struct scull_dev {
        struct scull_qset *data;  /* Pointer to first quantum set */
        int quantum;              /* the current quantum size */
        int qset;                 /* the current array size */
        unsigned long size;       /* amount of data stored here */
        unsigned int access_key;  /* used by sculluid and scullpriv */
        struct semaphore sem;     /* mutual exclusion semaphore     */
        struct cdev cdev;         /* Char device structure     */
  };

and there at the bottom is the embedded "struct cdev" you're supposed
to build in to your new character device.  the question is -- how does
that structure get associated with the structure pointer back in the
inode?  moving on, here's the character device initialization routine
for that sample "scull" char device:

  /*
   * Set up the char_dev structure for this device.
   */
  static void scull_setup_cdev(struct scull_dev *dev, int index)
  {
        int err, devno = MKDEV(scull_major, scull_minor + index);

        cdev_init(&dev->cdev, &scull_fops);
        dev->cdev.owner = THIS_MODULE;
        dev->cdev.ops = &scull_fops;
        err = cdev_add (&dev->cdev, devno, 1);
        /* Fail gracefully if need be */
        if (err)
                printk(KERN_NOTICE "Error %d adding scull%d", err, index);
  }

  so what does this code do?  from fs/char_dev.c, here's the code for
cdev_init():

  void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  {
        memset(cdev, 0, sizeof *cdev);
        INIT_LIST_HEAD(&cdev->list);
        kobject_init(&cdev->kobj, &ktype_cdev_default);
        cdev->ops = fops;
  }

that doesn't appear to link the cdev back to the inode, but it *does*
create an initialized kobject for the character device.  following
that, cdev_add() is run:

  int cdev_add(struct cdev *p, dev_t dev, unsigned count)
  {
        p->dev = dev;
        p->count = count;
        return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
  }

which appears to fill in the new kobject with information
corresponding to this new character device, but *still*, there's no
connection back to the inode for the device file.  finally, it looks
like it happens here:

  /*
   * Called every time a character special file is opened
   */
  static int chrdev_open(struct inode *inode, struct file *filp)
  {
        struct cdev *p;
        struct cdev *new = NULL;
        int ret = 0;

        spin_lock(&cdev_lock);
        p = inode->i_cdev;
        if (!p) {
                struct kobject *kobj;
                int idx;
                spin_unlock(&cdev_lock);
                kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
                if (!kobj)
                        return -ENXIO;
                new = container_of(kobj, struct cdev, kobj);
                spin_lock(&cdev_lock);
                p = inode->i_cdev;
                if (!p) {
                        inode->i_cdev = p = new;
                        inode->i_cindex = idx;
                        list_add(&inode->i_devices, &p->list);
                        new = NULL;
                        ... snip ...

that routine might be called every time a char special file is opened
but, at this point, it still seems that the inode "i_cdev" pointer
isn't yet pointing to the "struct cdev" we've created and initialized.
finally, it seems to happen in this routine, where a test is done to
see if that pointer is set yet:

  p = inode->i_cdev;
  if (!p) {

and if it isn't, then:

  kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  if (!kobj)
    return -ENXIO;
  new = container_of(kobj, struct cdev, kobj);

that *appears* to do a wide-ranging kobject lookup, looking for the
kobject that matches the device ID (inode->i_rdev) and, if it finds
it, then the struct cdev we're looking for is simply the container of
that kobject.  and *finally*,

  if (!p) {
          inode->i_cdev = p = new;

the "i_cdev" struct cdev pointer back in the inode is set and can be
used from now on.  does all that make sense?  in short, the very first
time a character device file is opened and it's noticed that the
inode->i_cdev pointer hasn't been set yet, a kobject-based search is
done to find the corresponding "struct cdev" address to fill into the
inode.  does all this seem to follow?

rday

p.s.  i am a bit confused as to why there are two tests for whether
inode->i_cdev is non-NULL:

        p = inode->i_cdev;
        if (!p) {                            <-- first test of p
                struct kobject *kobj;
                int idx;
                spin_unlock(&cdev_lock);
                kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
                if (!kobj)
                        return -ENXIO;
                new = container_of(kobj, struct cdev, kobj);
                spin_lock(&cdev_lock);
                p = inode->i_cdev;
                if (!p) {                    <-- second test of p
                        inode->i_cdev = p = new;


why is that first test being repeated further down?  if p was NULL at
that first test, how could it possibly have changed before that second
test?  isn't that second test redundant?  or am i missing something?


========================================================================
Robert P. J. Day
Linux Consulting, Training and Annoying Kernel Pedantry:
    Have classroom, will lecture.

http://crashcourse.ca                          Waterloo, Ontario, CANADA
========================================================================

--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to [EMAIL PROTECTED]
Please read the FAQ at http://kernelnewbies.org/FAQ

Reply via email to