8

I have been trying to port a driver from 2.6 to 4.X without support from the original board manufacturer (and very limited Linux experience).

The original driver uses init_timer() and passes in a pointer to the timer_list structure. That timer_list structure's data element was set to a pointer to another memory structure and the function element set to the callback. Inside the callback function the data element was used to access other bits of stuff.

The current timer init-method uses:

timer_setup( timer_list *, callback, (unsigned int) flags);

and the timer_list structure was changed to eliminate the data field.

I'm not sure what is the best/proper way to inform the callback function of the equivalent data element. Can anyone provide some guidance?

Here is a snippet of the old driver:

myDevice * dev;

dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);    
init_timer(dev->getIntrTimer);

dev->getIntrTimer->data = (unsigned long) dev;
dev->getIntrTimer->function = GetIntrTimerCallback;

The callback function starts off like this:

void GetIntrTimerCallback(unsigned long devAddr)
{
    myDevice *dev = (myDevice *) devAddr;
    dev->blahBlah++; // etc.

So the old code gets passed the pointer to myDevice so inside the callback that structure can be accessed.

But with the new timer method only has available an int that is 4 bytes but a pointer is 8 (or whatever).

What I'd like to do is this:

dev->getIntrTimer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
timer_setup(dev->getIntrTimer, GetIntrTimerCallback, dev);

but of course that generates compile errors because dev is a pointer to type myDevice, which does not fit in an int.

red0ct
  • 4,840
  • 3
  • 17
  • 44
El Ronaldo
  • 383
  • 1
  • 3
  • 11

1 Answers1

21

The timer_setup() with three args is present since 4.14 Linux kernel (FYI there was setup_timer() in slightly earlier versions). If you maintain some code which should be relevant up to recent kernels - you have to change it in appropriate way every time the API changes. Now you can access your data through the special function from_timer() based on container_of().

timer_list is normally used not as pointer inside struct, so the example implies normal usage and could be something like:

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
    init_timer(&dev->getIntrTimer);
    dev->getIntrTimer.data = (unsigned long) dev;
    dev->getIntrTimer.function = GetIntrTimerCallback;
    /* ... */
    add_timer(&dev->getIntrTimer);
#else
    timer_setup(&dev->getIntrTimer, GetIntrTimerCallback, 0);
    /* the third argument may include TIMER_* flags */
    /* ... */
#endif

The callback function:

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
void GetIntrTimerCallback(unsigned long devAddr)
{
    myDevice *dev = (myDevice *) devAddr;
#else
void GetIntrTimerCallback(struct timer_list *t)
{
    myDevice *dev = from_timer(dev, t, getIntrTimer);
#endif
    /* Do something with "dev" */

Read also:

El Ronaldo
  • 383
  • 1
  • 3
  • 11
red0ct
  • 4,840
  • 3
  • 17
  • 44
  • Yikes! And timer_setup was supposed to be an improvement!?! – El Ronaldo Dec 19 '18 at 21:37
  • Anyway, I made the changes (apparently page_cache_release() and get_user_pages() have been similarly improved). But I get compile errors in the callback function with the from_timer() call. The error is "pointer type mismatch in container_of()". Am I supposed to be casting any/all of those arguments? The error message doesn't give many clues (that I understand) on which argument is flagged. – El Ronaldo Dec 19 '18 at 21:45
  • 3
    @ElRonaldo Error messages about `container_of()` are quite reasonable if `timer_list` is declared as pointer in your struct. As mentioned above the example ***is not for case*** `typedef struct { struct timer_list *getIntrTimer; /*smth else*/ } myDevice;`, but ***is for case*** `typedef struct { struct timer_list getIntrTimer; /*smth else*/ } myDevice;` – red0ct Dec 19 '18 at 22:05
  • 1
    Ohhhh, you mean I'm supposed to read your **whole** response!?!!?? I've too short an attention span for that, man! – El Ronaldo Dec 19 '18 at 22:15
  • Does from_timer **require** that struct timer_list be the first element in the myDevice struct? In my case it is not only a pointer, but also not the first element, and frankly I do not follow the container_of() expansion. – El Ronaldo Dec 19 '18 at 22:58
  • =) It doesn't have to be the first element. – red0ct Dec 19 '18 at 23:31
  • @ElRonaldo, new *timer_setup()* API is definitely an improvement. – 0andriy Jan 11 '19 at 22:40
  • 1
    Don't forget to add #include to your code. – Ken A Feb 21 '19 at 16:33