Skip to content
Advertisement

Adaptation from old init_timer to new timer_setup

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.

Advertisement

Answer

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:

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement