Hello List,
I'm new to the list and not a native English speaker.
I develop a module for HP laptops and I have a problem. Most of
(PIII/Celeron) HP laptops have some mutimedia buttons. After enabling them
they generate scancodes expect on HP OmniBook XE3 GF/HP Pavilion N52xx. On
these models the volume control buttons do not generate scancodes.
It is possible to get button press info from Embedded Controller as
follows (only the substantial parts of the code):
#define OMNIBOOK_POLL 50 * HZ / 1000
static struct timer_list omnibook_key_timer;
static spinlock_t omnibook_ec_lock = SPIN_LOCK_UNLOCKED;
int omnibook_ec_read(u8 addr, u8 *data)
{
unsigned long flags;
int retval;
spin_lock_irqsave(&omnibook_ec_lock, flags);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_IBF);
if (retval)
goto end;
omnibook_ec_write_command(OMNIBOOK_EC_CMD_READ);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_IBF);
if (retval)
goto end;
omnibook_ec_write_data(addr);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_OBF);
if (retval)
goto end;
*data = omnibook_ec_read_data();
end:
spin_unlock_irqrestore(&omnibook_ec_lock, flags);
return retval;
}
int omnibook_ec_write(u8 addr, u8 data)
{
unsigned long flags;
int retval;
spin_lock_irqsave(&omnibook_ec_lock, flags);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_IBF);
if (retval)
goto end;
omnibook_ec_write_command(OMNIBOOK_EC_CMD_WRITE);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_IBF);
if (retval)
goto end;
omnibook_ec_write_data(addr);
retval = omnibook_ec_wait(OMNIBOOK_EC_STAT_IBF);
if (retval)
goto end;
omnibook_ec_write_data(data);
end:
spin_unlock_irqrestore(&omnibook_ec_lock, flags);
return retval;
}
static void xe3gc_key_poller(unsigned long dummy)
{
u8 q0a;
omnibook_ec_read(XE3GC_Q0A, &q0a);
omnibook_ec_write(XE3GC_Q0A, 0);
if (q0a & XE3GC_VOLD_MASK) {
printk(KERN_INFO "%s: Fn-down arrow or Volume down pressed.\n", MODULE_NAME);
}
if (q0a & XE3GC_VOLU_MASK) {
printk(KERN_INFO "%s: Fn-up arrow or Volume up pressed.\n", MODULE_NAME);
}
if (q0a & XE3GC_MUTE_MASK) {
printk(KERN_INFO "%s: Fn+F7 - Volume mute pressed.\n", MODULE_NAME);
}
mod_timer(&omnibook_key_timer, jiffies + OMNIBOOK_POLL);
}
int __init module_init(void)
{
...
init_timer(&omnibook_key_timer);
key_timer.data = 0;
key_timer.function = xe3gc_key_poller;
key_timer.expires = jiffies + OMNIBOOK_POLL;
add_timer(&omnibook_key_timer);
...
}
It is working but through spinlocks the interrupt sensible drives doesn't
works (e.g. ppp interface have 5-80% of packet loss) when the OMNIBOOK_POLL
value is in the usable range. This is too high price for the volume
control buttons.
Does anybody idea to solve this problem?
Thanks,
Peter
--
So?s P?ter Pannonhalmi F?ap?ts?g / Archabbey of Pannonhalma
H-9090 Pannonhalma, V?r 1.
Tel: +3696570189, Fax: +3696470011
On Thu, 2002-09-12 at 08:54, Soos Peter wrote:
> It is working but through spinlocks the interrupt sensible drives doesn't
> works (e.g. ppp interface have 5-80% of packet loss) when the OMNIBOOK_POLL
> value is in the usable range. This is too high price for the volume
> control buttons.
>
> Does anybody idea to solve this problem?
If its very slow hardware then that might explain your problem. You
would be sitting with interrupts off for a very long time. What really
makes the difference to how you handle it is - if the irq is shared, and
how easy it is to block.
If it is not shared, or can be blocked fast then you end up with code
that basically says
irq_handler
block irq
set work_to_do
kick off a tasklet
return
and take care in the tasklet to avoid blocking IRQ's during the actual
reads from the chip. You might do something like
if(!test_and_set_bit(0, &chip_do_read))
{
add_read_to_queue();
reenable_int
}
else
set_bit(1, &chip_do_read);
and elsewhere where you touch that data or might lock against it do
set_bit(0, &chip_do_read);
/* Above maybe code that waits politely for that.. */
blah
blah
/* Now clean up */
if(test_bit(1, &chip_do_read)) /* Poll deferred */
{
clear_bit(1, &chip_do_read);
add_read_to_queue();
clear_bit(0, &chip_do_read);
reeanable_int
}
else
clear_bit(0, &chip_do_read);
or use xchg, or atomic_t counters