0262ab0df6
Some of the flags should be accessed atomically to prevent race conditions. The flags that are most important are those that can change often and indicate the actual state of the device, queue or queue entry. The big flag rename was done to move all state flags to the same naming type as the other rt2x00dev flags and made sure all places where the flags were used were changed. ;) Thanks to Stephen for most of the queue flags updates, which fixes some of the most obvious consequences of the race conditions. Among those the notorious: rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. Signed-off-by: Stephen Blackheath <tramp.enshrine.stephen@blacksapphire.com> Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
162 lines
4.4 KiB
C
162 lines
4.4 KiB
C
/*
|
|
Copyright (C) 2004 - 2008 rt2x00 SourceForge Project
|
|
<http://rt2x00.serialmonkey.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the
|
|
Free Software Foundation, Inc.,
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
Module: rt2x00rfkill
|
|
Abstract: rt2x00 rfkill routines.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/rfkill.h>
|
|
|
|
#include "rt2x00.h"
|
|
#include "rt2x00lib.h"
|
|
|
|
static int rt2x00rfkill_toggle_radio(void *data, enum rfkill_state state)
|
|
{
|
|
struct rt2x00_dev *rt2x00dev = data;
|
|
int retval = 0;
|
|
|
|
if (unlikely(!rt2x00dev))
|
|
return 0;
|
|
|
|
/*
|
|
* Only continue if there are enabled interfaces.
|
|
*/
|
|
if (!test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags))
|
|
return 0;
|
|
|
|
if (state == RFKILL_STATE_UNBLOCKED) {
|
|
INFO(rt2x00dev, "Hardware button pressed, enabling radio.\n");
|
|
clear_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
|
|
retval = rt2x00lib_enable_radio(rt2x00dev);
|
|
} else if (state == RFKILL_STATE_SOFT_BLOCKED) {
|
|
INFO(rt2x00dev, "Hardware button pressed, disabling radio.\n");
|
|
set_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags);
|
|
rt2x00lib_disable_radio(rt2x00dev);
|
|
} else {
|
|
WARNING(rt2x00dev, "Received unexpected rfkill state %d.\n",
|
|
state);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int rt2x00rfkill_get_state(void *data, enum rfkill_state *state)
|
|
{
|
|
struct rt2x00_dev *rt2x00dev = data;
|
|
|
|
*state = rt2x00dev->rfkill->state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rt2x00rfkill_poll(struct work_struct *work)
|
|
{
|
|
struct rt2x00_dev *rt2x00dev =
|
|
container_of(work, struct rt2x00_dev, rfkill_work.work);
|
|
int state;
|
|
|
|
if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
|
|
return;
|
|
|
|
/*
|
|
* rfkill_poll reports 1 when the key has been pressed and the
|
|
* radio should be blocked.
|
|
*/
|
|
state = !rt2x00dev->ops->lib->rfkill_poll(rt2x00dev) ?
|
|
RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
|
|
|
|
rfkill_force_state(rt2x00dev->rfkill, state);
|
|
|
|
queue_delayed_work(rt2x00dev->hw->workqueue,
|
|
&rt2x00dev->rfkill_work, RFKILL_POLL_INTERVAL);
|
|
}
|
|
|
|
void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev)
|
|
{
|
|
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
|
|
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
|
|
return;
|
|
|
|
if (rfkill_register(rt2x00dev->rfkill)) {
|
|
ERROR(rt2x00dev, "Failed to register rfkill handler.\n");
|
|
return;
|
|
}
|
|
|
|
__set_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state);
|
|
|
|
/*
|
|
* Force initial poll which will detect the initial device state,
|
|
* and correctly sends the signal to the rfkill layer about this
|
|
* state.
|
|
*/
|
|
rt2x00rfkill_poll(&rt2x00dev->rfkill_work.work);
|
|
}
|
|
|
|
void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev)
|
|
{
|
|
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
|
|
!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state))
|
|
return;
|
|
|
|
cancel_delayed_work_sync(&rt2x00dev->rfkill_work);
|
|
|
|
rfkill_unregister(rt2x00dev->rfkill);
|
|
|
|
__clear_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state);
|
|
}
|
|
|
|
void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev)
|
|
{
|
|
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags))
|
|
return;
|
|
|
|
rt2x00dev->rfkill =
|
|
rfkill_allocate(wiphy_dev(rt2x00dev->hw->wiphy), RFKILL_TYPE_WLAN);
|
|
if (!rt2x00dev->rfkill) {
|
|
ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n");
|
|
return;
|
|
}
|
|
|
|
rt2x00dev->rfkill->name = rt2x00dev->ops->name;
|
|
rt2x00dev->rfkill->data = rt2x00dev;
|
|
rt2x00dev->rfkill->state = -1;
|
|
rt2x00dev->rfkill->toggle_radio = rt2x00rfkill_toggle_radio;
|
|
rt2x00dev->rfkill->get_state = rt2x00rfkill_get_state;
|
|
|
|
INIT_DELAYED_WORK(&rt2x00dev->rfkill_work, rt2x00rfkill_poll);
|
|
|
|
return;
|
|
}
|
|
|
|
void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev)
|
|
{
|
|
if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) ||
|
|
!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state))
|
|
return;
|
|
|
|
cancel_delayed_work_sync(&rt2x00dev->rfkill_work);
|
|
|
|
rfkill_free(rt2x00dev->rfkill);
|
|
rt2x00dev->rfkill = NULL;
|
|
}
|