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: