3

I am trying to understand different ways linux kernel initialize structures. In this query I wrote a sample usb driver but I do not understand some points, pointed as comments preceding ??

static struct usb_device_id pen_table[] =  //?? why pen_table is an array
{
    { USB_DEVICE(0xaaaa , 0x8816) },       //??what type of array initialization is this
    {} /* Terminating entry */             //??how this terminates
};

I tried to initialize device id table in this way, but I am getting errors as near initialization

static struct  usb_device_id pen_table = {
    .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
    .idVendor=0xaaaa,
    .idProduct = 0x8816,
};
nikhil chaubey
  • 369
  • 1
  • 5
  • 11

2 Answers2

2

You should have Linux kernel source at hand to really understand this.

  • Why pen_table is an array?

It wil be necessary in MODULE_DEVICE_TABLE (see Hard time in understanding MODULE_DEVICE_TABLE(usb, id_table) usage) and in defining instance of usb_driver struct, see http://opensourceforu.efytimes.com/2011/11/usb-drivers-in-linux-2/.

  • what type of array initialization is this?

USB_DEVICE is a macro defined in include/linux/usb.h:

#define USB_DEVICE(vend, prod) \
    .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
    .idVendor = (vend), \
    .idProduct = (prod)
  • how this terminates?

C standard says:

The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

and:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then:

  • if it has pointer type, it is initialized to a null pointer;
  • if it has arithmetic type, it is initialized to (positive or unsigned) zero;
  • if it is an aggregate, every member is initialized (recursively) according to these rules;
  • if it is a union, the first named member is initialized (recursively) according to these rules.

Thanks to this, id_table is defined as a pointer and not as an array inside usb_driver:

const struct usb_device_id *id_table;

Instead of passing an array size independently a function that uses id_table will increment pointer to id_table until one of its elements is NULL. See this short example that represents this technique:

#include <stdio.h>
#include <stdlib.h>

struct small
{
    int a;
    int b;
};

struct big
{
    struct small *s;
};

struct small table[] =
    {
        {1, 1},
        {2, 2},
        {3, 3},
        {}
    };

int main(void)
{
    struct big b = {
        .s = table
    };

    const struct small *s;
        /* traverse through table using pointer arithmetic */
    for (s = b.s; s->a; s++)
        {
            printf("%d\n", s->a);
            printf("%d\n", s->b);
        }

    exit(0);
}
  • I tried to initialize device id table in this way, but I am getting errors as near initialization

I don't, are you sure you're not trying to redefine pen_table? What's an error message?

Arkadiusz Drabczyk
  • 11,227
  • 2
  • 25
  • 38
  • your example is good but you shouldn't use 's' at three places in different contexts. – nikhil chaubey Oct 29 '15 at 15:35
  • The only place where a different identifier could be used is in `const struct small *s`, for example it can be `const struct small *sp;` and `for` loop could be `for (sp = b.s; sp->a; sp++) { printf("%d\n", sp->a); printf("%d\n", sp->b); }` – Arkadiusz Drabczyk Oct 29 '15 at 15:39
0

It's an array because it's how the data structure is defined and used.

You have n number of entries, and then a terminating entry (in this case, all zeros).

Wherever this array is used in initialization, it will start at the table symbol and consume entries until it hits the terminator, then stop. This way you don't need to communicate the number of entries as well.

This pattern facilitates late binding of configuration data with the library, and allows more compile time configuration, rather than run time configuration, and also requires fewer things to be in sync to operate correctly (so less chance for error).

Your second struct doesn't have a terminator, so the thing that's parsing the table just keeps on going and going and going until it crashes or gets errors.

Russ Schultz
  • 2,545
  • 20
  • 22