mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
39
net/wimax/Kconfig
Normal file
39
net/wimax/Kconfig
Normal file
|
@ -0,0 +1,39 @@
|
|||
#
|
||||
# WiMAX LAN device configuration
|
||||
#
|
||||
|
||||
menuconfig WIMAX
|
||||
tristate "WiMAX Wireless Broadband support"
|
||||
depends on RFKILL || !RFKILL
|
||||
help
|
||||
|
||||
Select to configure support for devices that provide
|
||||
wireless broadband connectivity using the WiMAX protocol
|
||||
(IEEE 802.16).
|
||||
|
||||
Please note that most of these devices require signing up
|
||||
for a service plan with a provider.
|
||||
|
||||
The different WiMAX drivers can be enabled in the menu entry
|
||||
|
||||
Device Drivers > Network device support > WiMAX Wireless
|
||||
Broadband devices
|
||||
|
||||
If unsure, it is safe to select M (module).
|
||||
|
||||
config WIMAX_DEBUG_LEVEL
|
||||
int "WiMAX debug level"
|
||||
depends on WIMAX
|
||||
default 8
|
||||
help
|
||||
|
||||
Select the maximum debug verbosity level to be compiled into
|
||||
the WiMAX stack code.
|
||||
|
||||
By default, debug messages are disabled at runtime and can
|
||||
be selectively enabled for different parts of the code using
|
||||
the sysfs debug-levels file.
|
||||
|
||||
If set at zero, this will compile out all the debug code.
|
||||
|
||||
It is recommended that it is left at 8.
|
14
net/wimax/Makefile
Normal file
14
net/wimax/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
obj-$(CONFIG_WIMAX) += wimax.o
|
||||
|
||||
wimax-y := \
|
||||
id-table.o \
|
||||
op-msg.o \
|
||||
op-reset.o \
|
||||
op-rfkill.o \
|
||||
op-state-get.o \
|
||||
stack.o
|
||||
|
||||
wimax-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
||||
|
43
net/wimax/debug-levels.h
Normal file
43
net/wimax/debug-levels.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Linux WiMAX Stack
|
||||
* Debug levels control file for the wimax module
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
#ifndef __debug_levels__h__
|
||||
#define __debug_levels__h__
|
||||
|
||||
/* Maximum compile and run time debug level for all submodules */
|
||||
#define D_MODULENAME wimax
|
||||
#define D_MASTER CONFIG_WIMAX_DEBUG_LEVEL
|
||||
|
||||
#include <linux/wimax/debug.h>
|
||||
|
||||
/* List of all the enabled modules */
|
||||
enum d_module {
|
||||
D_SUBMODULE_DECLARE(debugfs),
|
||||
D_SUBMODULE_DECLARE(id_table),
|
||||
D_SUBMODULE_DECLARE(op_msg),
|
||||
D_SUBMODULE_DECLARE(op_reset),
|
||||
D_SUBMODULE_DECLARE(op_rfkill),
|
||||
D_SUBMODULE_DECLARE(op_state_get),
|
||||
D_SUBMODULE_DECLARE(stack),
|
||||
};
|
||||
|
||||
#endif /* #ifndef __debug_levels__h__ */
|
80
net/wimax/debugfs.c
Normal file
80
net/wimax/debugfs.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Debugfs support
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/wimax.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE debugfs
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
#define __debugfs_register(prefix, name, parent) \
|
||||
do { \
|
||||
result = d_level_register_debugfs(prefix, name, parent); \
|
||||
if (result < 0) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
|
||||
|
||||
int wimax_debugfs_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result;
|
||||
struct net_device *net_dev = wimax_dev->net_dev;
|
||||
struct device *dev = net_dev->dev.parent;
|
||||
struct dentry *dentry;
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "wimax:%s", net_dev->name);
|
||||
dentry = debugfs_create_dir(buf, NULL);
|
||||
result = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry)) {
|
||||
if (result == -ENODEV)
|
||||
result = 0; /* No debugfs support */
|
||||
else
|
||||
dev_err(dev, "Can't create debugfs dentry: %d\n",
|
||||
result);
|
||||
goto out;
|
||||
}
|
||||
wimax_dev->debugfs_dentry = dentry;
|
||||
__debugfs_register("wimax_dl_", debugfs, dentry);
|
||||
__debugfs_register("wimax_dl_", id_table, dentry);
|
||||
__debugfs_register("wimax_dl_", op_msg, dentry);
|
||||
__debugfs_register("wimax_dl_", op_reset, dentry);
|
||||
__debugfs_register("wimax_dl_", op_rfkill, dentry);
|
||||
__debugfs_register("wimax_dl_", op_state_get, dentry);
|
||||
__debugfs_register("wimax_dl_", stack, dentry);
|
||||
result = 0;
|
||||
out:
|
||||
return result;
|
||||
|
||||
error:
|
||||
debugfs_remove_recursive(wimax_dev->debugfs_dentry);
|
||||
return result;
|
||||
}
|
||||
|
||||
void wimax_debugfs_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
debugfs_remove_recursive(wimax_dev->debugfs_dentry);
|
||||
}
|
||||
|
||||
|
145
net/wimax/id-table.c
Normal file
145
net/wimax/id-table.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Mappping of generic netlink family IDs to net devices
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* We assign a single generic netlink family ID to each device (to
|
||||
* simplify lookup).
|
||||
*
|
||||
* We need a way to map family ID to a wimax_dev pointer.
|
||||
*
|
||||
* The idea is to use a very simple lookup. Using a netlink attribute
|
||||
* with (for example) the interface name implies a heavier search over
|
||||
* all the network devices; seemed kind of a waste given that we know
|
||||
* we are looking for a WiMAX device and that most systems will have
|
||||
* just a single WiMAX adapter.
|
||||
*
|
||||
* We put all the WiMAX devices in the system in a linked list and
|
||||
* match the generic link family ID against the list.
|
||||
*
|
||||
* By using a linked list, the case of a single adapter in the system
|
||||
* becomes (almost) no overhead, while still working for many more. If
|
||||
* it ever goes beyond two, I'll be surprised.
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/wimax.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE id_table
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(wimax_id_table_lock);
|
||||
static struct list_head wimax_id_table = LIST_HEAD_INIT(wimax_id_table);
|
||||
|
||||
|
||||
/*
|
||||
* wimax_id_table_add - add a gennetlink familiy ID / wimax_dev mapping
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor to associate to the Generic
|
||||
* Netlink family ID.
|
||||
*
|
||||
* Look for an empty spot in the ID table; if none found, double the
|
||||
* table's size and get the first spot.
|
||||
*/
|
||||
void wimax_id_table_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_add(&wimax_dev->id_table_node, &wimax_id_table);
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
d_fnend(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wimax_get_netdev_by_info - lookup a wimax_dev from the gennetlink info
|
||||
*
|
||||
* The generic netlink family ID has been filled out in the
|
||||
* nlmsghdr->nlmsg_type field, so we pull it from there, look it up in
|
||||
* the mapping table and reference the wimax_dev.
|
||||
*
|
||||
* When done, the reference should be dropped with
|
||||
* 'dev_put(wimax_dev->net_dev)'.
|
||||
*/
|
||||
struct wimax_dev *wimax_dev_get_by_genl_info(
|
||||
struct genl_info *info, int ifindex)
|
||||
{
|
||||
struct wimax_dev *wimax_dev = NULL;
|
||||
|
||||
d_fnstart(3, NULL, "(info %p ifindex %d)\n", info, ifindex);
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) {
|
||||
if (wimax_dev->net_dev->ifindex == ifindex) {
|
||||
dev_hold(wimax_dev->net_dev);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
wimax_dev = NULL;
|
||||
d_printf(1, NULL, "wimax: no devices found with ifindex %d\n",
|
||||
ifindex);
|
||||
found:
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
d_fnend(3, NULL, "(info %p ifindex %d) = %p\n",
|
||||
info, ifindex, wimax_dev);
|
||||
return wimax_dev;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* wimax_id_table_rm - Remove a gennetlink familiy ID / wimax_dev mapping
|
||||
*
|
||||
* @id: family ID to remove from the table
|
||||
*/
|
||||
void wimax_id_table_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_del_init(&wimax_dev->id_table_node);
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Release the gennetlink family id / mapping table
|
||||
*
|
||||
* On debug, verify that the table is empty upon removal. We want the
|
||||
* code always compiled, to ensure it doesn't bit rot. It will be
|
||||
* compiled out if CONFIG_BUG is disabled.
|
||||
*/
|
||||
void wimax_id_table_release(void)
|
||||
{
|
||||
struct wimax_dev *wimax_dev;
|
||||
|
||||
#ifndef CONFIG_BUG
|
||||
return;
|
||||
#endif
|
||||
spin_lock(&wimax_id_table_lock);
|
||||
list_for_each_entry(wimax_dev, &wimax_id_table, id_table_node) {
|
||||
pr_err("BUG: %s wimax_dev %p ifindex %d not cleared\n",
|
||||
__func__, wimax_dev, wimax_dev->net_dev->ifindex);
|
||||
WARN_ON(1);
|
||||
}
|
||||
spin_unlock(&wimax_id_table_lock);
|
||||
}
|
407
net/wimax/op-msg.c
Normal file
407
net/wimax/op-msg.c
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Generic messaging interface between userspace and driver/device
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements a direct communication channel between user space and
|
||||
* the driver/device, by which free form messages can be sent back and
|
||||
* forth.
|
||||
*
|
||||
* This is intended for device-specific features, vendor quirks, etc.
|
||||
*
|
||||
* See include/net/wimax.h
|
||||
*
|
||||
* GENERIC NETLINK ENCODING AND CAPACITY
|
||||
*
|
||||
* A destination "pipe name" is added to each message; it is up to the
|
||||
* drivers to assign or use those names (if using them at all).
|
||||
*
|
||||
* Messages are encoded as a binary netlink attribute using nla_put()
|
||||
* using type NLA_UNSPEC (as some versions of libnl still in
|
||||
* deployment don't yet understand NLA_BINARY).
|
||||
*
|
||||
* The maximum capacity of this transport is PAGESIZE per message (so
|
||||
* the actual payload will be bit smaller depending on the
|
||||
* netlink/generic netlink attributes and headers).
|
||||
*
|
||||
* RECEPTION OF MESSAGES
|
||||
*
|
||||
* When a message is received from user space, it is passed verbatim
|
||||
* to the driver calling wimax_dev->op_msg_from_user(). The return
|
||||
* value from this function is passed back to user space as an ack
|
||||
* over the generic netlink protocol.
|
||||
*
|
||||
* The stack doesn't do any processing or interpretation of these
|
||||
* messages.
|
||||
*
|
||||
* SENDING MESSAGES
|
||||
*
|
||||
* Messages can be sent with wimax_msg().
|
||||
*
|
||||
* If the message delivery needs to happen on a different context to
|
||||
* that of its creation, wimax_msg_alloc() can be used to get a
|
||||
* pointer to the message that can be delivered later on with
|
||||
* wimax_msg_send().
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* wimax_gnl_doit_msg_from_user() Process a message from user space
|
||||
* wimax_dev_get_by_genl_info()
|
||||
* wimax_dev->op_msg_from_user() Delivery of message to the driver
|
||||
*
|
||||
* wimax_msg() Send a message to user space
|
||||
* wimax_msg_alloc()
|
||||
* wimax_msg_send()
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/export.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE op_msg
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_alloc - Create a new skb for sending a message to userspace
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
* @pipe_name: "named pipe" the message will be sent to
|
||||
* @msg: pointer to the message data to send
|
||||
* @size: size of the message to send (in bytes), including the header.
|
||||
* @gfp_flags: flags for memory allocation.
|
||||
*
|
||||
* Returns: %0 if ok, negative errno code on error
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Allocates an skb that will contain the message to send to user
|
||||
* space over the messaging pipe and initializes it, copying the
|
||||
* payload.
|
||||
*
|
||||
* Once this call is done, you can deliver it with
|
||||
* wimax_msg_send().
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
|
||||
* wimax_msg_send() depends on skb->data being placed at the
|
||||
* beginning of the user message.
|
||||
*
|
||||
* Unlike other WiMAX stack calls, this call can be used way early,
|
||||
* even before wimax_dev_add() is called, as long as the
|
||||
* wimax_dev->net_dev pointer is set to point to a proper
|
||||
* net_dev. This is so that drivers can use it early in case they need
|
||||
* to send stuff around or communicate with user space.
|
||||
*/
|
||||
struct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev,
|
||||
const char *pipe_name,
|
||||
const void *msg, size_t size,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
size_t msg_size;
|
||||
void *genl_msg;
|
||||
struct sk_buff *skb;
|
||||
|
||||
msg_size = nla_total_size(size)
|
||||
+ nla_total_size(sizeof(u32))
|
||||
+ (pipe_name ? nla_total_size(strlen(pipe_name)) : 0);
|
||||
result = -ENOMEM;
|
||||
skb = genlmsg_new(msg_size, gfp_flags);
|
||||
if (skb == NULL)
|
||||
goto error_new;
|
||||
genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family,
|
||||
0, WIMAX_GNL_OP_MSG_TO_USER);
|
||||
if (genl_msg == NULL) {
|
||||
dev_err(dev, "no memory to create generic netlink message\n");
|
||||
goto error_genlmsg_put;
|
||||
}
|
||||
result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX,
|
||||
wimax_dev->net_dev->ifindex);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add ifindex attribute\n");
|
||||
goto error_nla_put;
|
||||
}
|
||||
if (pipe_name) {
|
||||
result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME,
|
||||
pipe_name);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add pipe_name attribute\n");
|
||||
goto error_nla_put;
|
||||
}
|
||||
}
|
||||
result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "no memory to add payload (msg %p size %zu) in "
|
||||
"attribute: %d\n", msg, size, result);
|
||||
goto error_nla_put;
|
||||
}
|
||||
genlmsg_end(skb, genl_msg);
|
||||
return skb;
|
||||
|
||||
error_nla_put:
|
||||
error_genlmsg_put:
|
||||
error_new:
|
||||
nlmsg_free(skb);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_alloc);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_data_len - Return a pointer and size of a message's payload
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
* @size: Pointer to where to store the message's size
|
||||
*
|
||||
* Returns the pointer to the message data.
|
||||
*/
|
||||
const void *wimax_msg_data_len(struct sk_buff *msg, size_t *size)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return NULL;
|
||||
}
|
||||
*size = nla_len(nla);
|
||||
return nla_data(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_data_len);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_data - Return a pointer to a message's payload
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
*/
|
||||
const void *wimax_msg_data(struct sk_buff *msg)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return NULL;
|
||||
}
|
||||
return nla_data(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_data);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_len - Return a message's payload length
|
||||
*
|
||||
* @msg: Pointer to a message created with wimax_msg_alloc()
|
||||
*/
|
||||
ssize_t wimax_msg_len(struct sk_buff *msg)
|
||||
{
|
||||
struct nlmsghdr *nlh = (void *) msg->head;
|
||||
struct nlattr *nla;
|
||||
|
||||
nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
|
||||
WIMAX_GNL_MSG_DATA);
|
||||
if (nla == NULL) {
|
||||
pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return nla_len(nla);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_len);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg_send - Send a pre-allocated message to user space
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the
|
||||
* ownership of @skb is transferred to this function.
|
||||
*
|
||||
* Returns: 0 if ok, < 0 errno code on error
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Sends a free-form message that was preallocated with
|
||||
* wimax_msg_alloc() and filled up.
|
||||
*
|
||||
* Assumes that once you pass an skb to this function for sending, it
|
||||
* owns it and will release it when done (on success).
|
||||
*
|
||||
* IMPORTANT:
|
||||
*
|
||||
* Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
|
||||
* wimax_msg_send() depends on skb->data being placed at the
|
||||
* beginning of the user message.
|
||||
*
|
||||
* Unlike other WiMAX stack calls, this call can be used way early,
|
||||
* even before wimax_dev_add() is called, as long as the
|
||||
* wimax_dev->net_dev pointer is set to point to a proper
|
||||
* net_dev. This is so that drivers can use it early in case they need
|
||||
* to send stuff around or communicate with user space.
|
||||
*/
|
||||
int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
void *msg = skb->data;
|
||||
size_t size = skb->len;
|
||||
might_sleep();
|
||||
|
||||
d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
|
||||
d_dump(2, dev, msg, size);
|
||||
genlmsg_multicast(&wimax_gnl_family, skb, 0, 0, GFP_KERNEL);
|
||||
d_printf(1, dev, "CTX: genl multicast done\n");
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg_send);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_msg - Send a message to user space
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (properly referenced)
|
||||
* @pipe_name: "named pipe" the message will be sent to
|
||||
* @buf: pointer to the message to send.
|
||||
* @size: size of the buffer pointed to by @buf (in bytes).
|
||||
* @gfp_flags: flags for memory allocation.
|
||||
*
|
||||
* Returns: %0 if ok, negative errno code on error.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Sends a free-form message to user space on the device @wimax_dev.
|
||||
*
|
||||
* NOTES:
|
||||
*
|
||||
* Once the @skb is given to this function, who will own it and will
|
||||
* release it when done (unless it returns error).
|
||||
*/
|
||||
int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
|
||||
const void *buf, size_t size, gfp_t gfp_flags)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags);
|
||||
if (IS_ERR(skb))
|
||||
result = PTR_ERR(skb);
|
||||
else
|
||||
result = wimax_msg_send(wimax_dev, skb);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_msg);
|
||||
|
||||
/*
|
||||
* Relays a message from user space to the driver
|
||||
*
|
||||
* The skb is passed to the driver-specific function with the netlink
|
||||
* and generic netlink headers already stripped.
|
||||
*
|
||||
* This call will block while handling/relaying the message.
|
||||
*/
|
||||
int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
struct device *dev;
|
||||
struct nlmsghdr *nlh = info->nlhdr;
|
||||
char *pipe_name;
|
||||
void *msg_buf;
|
||||
size_t msg_len;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) {
|
||||
pr_err("WIMAX_GNL_MSG_FROM_USER: can't find IFIDX attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
/* Unpack arguments */
|
||||
result = -EINVAL;
|
||||
if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) {
|
||||
dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA "
|
||||
"attribute\n");
|
||||
goto error_no_data;
|
||||
}
|
||||
msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]);
|
||||
msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]);
|
||||
|
||||
if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL)
|
||||
pipe_name = NULL;
|
||||
else {
|
||||
struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME];
|
||||
size_t attr_len = nla_len(attr);
|
||||
/* libnl-1.1 does not yet support NLA_NUL_STRING */
|
||||
result = -ENOMEM;
|
||||
pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL);
|
||||
if (pipe_name == NULL)
|
||||
goto error_alloc;
|
||||
pipe_name[attr_len] = 0;
|
||||
}
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result == -ENOMEDIUM)
|
||||
result = 0;
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
result = -ENOSYS;
|
||||
if (wimax_dev->op_msg_from_user == NULL)
|
||||
goto error_noop;
|
||||
|
||||
d_printf(1, dev,
|
||||
"CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n",
|
||||
nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
|
||||
nlh->nlmsg_seq, nlh->nlmsg_pid);
|
||||
d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len);
|
||||
d_dump(2, dev, msg_buf, msg_len);
|
||||
|
||||
result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name,
|
||||
msg_buf, msg_len, info);
|
||||
error_noop:
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
error_alloc:
|
||||
kfree(pipe_name);
|
||||
error_no_data:
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
||||
|
123
net/wimax/op-reset.c
Normal file
123
net/wimax/op-reset.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Implement and export a method for resetting a WiMAX device
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements a simple synchronous call to reset a WiMAX device.
|
||||
*
|
||||
* Resets aim at being warm, keeping the device handles active;
|
||||
* however, when that fails, it falls back to a cold reset (that will
|
||||
* disconnect and reconnect the device).
|
||||
*/
|
||||
|
||||
#include <net/wimax.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/export.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_reset
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/**
|
||||
* wimax_reset - Reset a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* %0 if ok and a warm reset was done (the device still exists in
|
||||
* the system).
|
||||
*
|
||||
* -%ENODEV if a cold/bus reset had to be done (device has
|
||||
* disconnected and reconnected, so current handle is not valid
|
||||
* any more).
|
||||
*
|
||||
* -%EINVAL if the device is not even registered.
|
||||
*
|
||||
* Any other negative error code shall be considered as
|
||||
* non-recoverable.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Called when wanting to reset the device for any reason. Device is
|
||||
* taken back to power on status.
|
||||
*
|
||||
* This call blocks; on successful return, the device has completed the
|
||||
* reset process and is ready to operate.
|
||||
*/
|
||||
int wimax_reset(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result = -EINVAL;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st state;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
dev_hold(wimax_dev->net_dev);
|
||||
state = wimax_dev->state;
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
|
||||
if (state >= WIMAX_ST_DOWN) {
|
||||
mutex_lock(&wimax_dev->mutex_reset);
|
||||
result = wimax_dev->op_reset(wimax_dev);
|
||||
mutex_unlock(&wimax_dev->mutex_reset);
|
||||
}
|
||||
dev_put(wimax_dev->net_dev);
|
||||
|
||||
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(wimax_reset);
|
||||
|
||||
|
||||
/*
|
||||
* Exporting to user space over generic netlink
|
||||
*
|
||||
* Parse the reset command from user space, return error code.
|
||||
*
|
||||
* No attributes.
|
||||
*/
|
||||
int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_RESET_IFIDX] == NULL) {
|
||||
pr_err("WIMAX_GNL_OP_RFKILL: can't find IFIDX attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RESET_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
/* Execute the operation and send the result back to user space */
|
||||
result = wimax_reset(wimax_dev);
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
447
net/wimax/op-rfkill.c
Normal file
447
net/wimax/op-rfkill.c
Normal file
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* RF-kill framework integration
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This integrates into the Linux Kernel rfkill susbystem so that the
|
||||
* drivers just have to do the bare minimal work, which is providing a
|
||||
* method to set the software RF-Kill switch and to report changes in
|
||||
* the software and hardware switch status.
|
||||
*
|
||||
* A non-polled generic rfkill device is embedded into the WiMAX
|
||||
* subsystem's representation of a device.
|
||||
*
|
||||
* FIXME: Need polled support? Let drivers provide a poll routine
|
||||
* and hand it to rfkill ops then?
|
||||
*
|
||||
* All device drivers have to do is after wimax_dev_init(), call
|
||||
* wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update
|
||||
* initial state and then every time it changes. See wimax.h:struct
|
||||
* wimax_dev for more information.
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* wimax_gnl_doit_rfkill() User space calling wimax_rfkill()
|
||||
* wimax_rfkill() Kernel calling wimax_rfkill()
|
||||
* __wimax_rf_toggle_radio()
|
||||
*
|
||||
* wimax_rfkill_set_radio_block() RF-Kill subsystem calling
|
||||
* __wimax_rf_toggle_radio()
|
||||
*
|
||||
* __wimax_rf_toggle_radio()
|
||||
* wimax_dev->op_rfkill_sw_toggle() Driver backend
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_report_rfkill_sw() Driver reports state change
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_report_rfkill_hw() Driver reports state change
|
||||
* __wimax_state_change()
|
||||
*
|
||||
* wimax_rfkill_add() Initialize/shutdown rfkill support
|
||||
* wimax_rfkill_rm() [called by wimax_dev_add/rm()]
|
||||
*/
|
||||
|
||||
#include <net/wimax.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/export.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_rfkill
|
||||
#include "debug-levels.h"
|
||||
|
||||
/**
|
||||
* wimax_report_rfkill_hw - Reports changes in the hardware RF switch
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New state of the RF Kill switch. %WIMAX_RF_ON radio on,
|
||||
* %WIMAX_RF_OFF radio off.
|
||||
*
|
||||
* When the device detects a change in the state of thehardware RF
|
||||
* switch, it must call this function to let the WiMAX kernel stack
|
||||
* know that the state has changed so it can be properly propagated.
|
||||
*
|
||||
* The WiMAX stack caches the state (the driver doesn't need to). As
|
||||
* well, as the change is propagated it will come back as a request to
|
||||
* change the software state to mirror the hardware state.
|
||||
*
|
||||
* If the device doesn't have a hardware kill switch, just report
|
||||
* it on initialization as always on (%WIMAX_RF_ON, radio on).
|
||||
*/
|
||||
void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
BUG_ON(state == WIMAX_RF_QUERY);
|
||||
BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
|
||||
if (state != wimax_dev->rf_hw) {
|
||||
wimax_dev->rf_hw = state;
|
||||
if (wimax_dev->rf_hw == WIMAX_RF_ON &&
|
||||
wimax_dev->rf_sw == WIMAX_RF_ON)
|
||||
wimax_state = WIMAX_ST_READY;
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
|
||||
result = rfkill_set_hw_state(wimax_dev->rfkill,
|
||||
state == WIMAX_RF_OFF);
|
||||
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n",
|
||||
wimax_dev, state, result);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_report_rfkill_sw - Reports changes in the software RF switch
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New state of the RF kill switch. %WIMAX_RF_ON radio on,
|
||||
* %WIMAX_RF_OFF radio off.
|
||||
*
|
||||
* Reports changes in the software RF switch state to the the WiMAX
|
||||
* stack.
|
||||
*
|
||||
* The main use is during initialization, so the driver can query the
|
||||
* device for its current software radio kill switch state and feed it
|
||||
* to the system.
|
||||
*
|
||||
* On the side, the device does not change the software state by
|
||||
* itself. In practice, this can happen, as the device might decide to
|
||||
* switch (in software) the radio off for different reasons.
|
||||
*/
|
||||
void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
BUG_ON(state == WIMAX_RF_QUERY);
|
||||
BUG_ON(state != WIMAX_RF_ON && state != WIMAX_RF_OFF);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_not_ready;
|
||||
|
||||
if (state != wimax_dev->rf_sw) {
|
||||
wimax_dev->rf_sw = state;
|
||||
if (wimax_dev->rf_hw == WIMAX_RF_ON &&
|
||||
wimax_dev->rf_sw == WIMAX_RF_ON)
|
||||
wimax_state = WIMAX_ST_READY;
|
||||
else
|
||||
wimax_state = WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
|
||||
}
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = void [%d]\n",
|
||||
wimax_dev, state, result);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw);
|
||||
|
||||
|
||||
/*
|
||||
* Callback for the RF Kill toggle operation
|
||||
*
|
||||
* This function is called by:
|
||||
*
|
||||
* - The rfkill subsystem when the RF-Kill key is pressed in the
|
||||
* hardware and the driver notifies through
|
||||
* wimax_report_rfkill_hw(). The rfkill subsystem ends up calling back
|
||||
* here so the software RF Kill switch state is changed to reflect
|
||||
* the hardware switch state.
|
||||
*
|
||||
* - When the user sets the state through sysfs' rfkill/state file
|
||||
*
|
||||
* - When the user calls wimax_rfkill().
|
||||
*
|
||||
* This call blocks!
|
||||
*
|
||||
* WARNING! When we call rfkill_unregister(), this will be called with
|
||||
* state 0!
|
||||
*
|
||||
* WARNING: wimax_dev must be locked
|
||||
*/
|
||||
static
|
||||
int __wimax_rf_toggle_radio(struct wimax_dev *wimax_dev,
|
||||
enum wimax_rf_state state)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st wimax_state;
|
||||
|
||||
might_sleep();
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
if (wimax_dev->rf_sw == state)
|
||||
goto out_no_change;
|
||||
if (wimax_dev->op_rfkill_sw_toggle != NULL)
|
||||
result = wimax_dev->op_rfkill_sw_toggle(wimax_dev, state);
|
||||
else if (state == WIMAX_RF_OFF) /* No op? can't turn off */
|
||||
result = -ENXIO;
|
||||
else /* No op? can turn on */
|
||||
result = 0; /* should never happen tho */
|
||||
if (result >= 0) {
|
||||
result = 0;
|
||||
wimax_dev->rf_sw = state;
|
||||
wimax_state = state == WIMAX_RF_ON ?
|
||||
WIMAX_ST_READY : WIMAX_ST_RADIO_OFF;
|
||||
__wimax_state_change(wimax_dev, wimax_state);
|
||||
}
|
||||
out_no_change:
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Translate from rfkill state to wimax state
|
||||
*
|
||||
* NOTE: Special state handling rules here
|
||||
*
|
||||
* Just pretend the call didn't happen if we are in a state where
|
||||
* we know for sure it cannot be handled (WIMAX_ST_DOWN or
|
||||
* __WIMAX_ST_QUIESCING). rfkill() needs it to register and
|
||||
* unregister, as it will run this path.
|
||||
*
|
||||
* NOTE: This call will block until the operation is completed.
|
||||
*/
|
||||
static int wimax_rfkill_set_radio_block(void *data, bool blocked)
|
||||
{
|
||||
int result;
|
||||
struct wimax_dev *wimax_dev = data;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_rf_state rf_state;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p blocked %u)\n", wimax_dev, blocked);
|
||||
rf_state = WIMAX_RF_ON;
|
||||
if (blocked)
|
||||
rf_state = WIMAX_RF_OFF;
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
if (wimax_dev->state <= __WIMAX_ST_QUIESCING)
|
||||
result = 0;
|
||||
else
|
||||
result = __wimax_rf_toggle_radio(wimax_dev, rf_state);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p blocked %u) = %d\n",
|
||||
wimax_dev, blocked, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct rfkill_ops wimax_rfkill_ops = {
|
||||
.set_block = wimax_rfkill_set_radio_block,
|
||||
};
|
||||
|
||||
/**
|
||||
* wimax_rfkill - Set the software RF switch state for a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* @state: New RF state.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* >= 0 toggle state if ok, < 0 errno code on error. The toggle state
|
||||
* is returned as a bitmap, bit 0 being the hardware RF state, bit 1
|
||||
* the software RF state.
|
||||
*
|
||||
* 0 means disabled (%WIMAX_RF_ON, radio on), 1 means enabled radio
|
||||
* off (%WIMAX_RF_OFF).
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* Called by the user when he wants to request the WiMAX radio to be
|
||||
* switched on (%WIMAX_RF_ON) or off (%WIMAX_RF_OFF). With
|
||||
* %WIMAX_RF_QUERY, just the current state is returned.
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* This call will block until the operation is complete.
|
||||
*/
|
||||
int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state);
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
result = wimax_dev_is_ready(wimax_dev);
|
||||
if (result < 0) {
|
||||
/* While initializing, < 1.4.3 wimax-tools versions use
|
||||
* this call to check if the device is a valid WiMAX
|
||||
* device; so we allow it to proceed always,
|
||||
* considering the radios are all off. */
|
||||
if (result == -ENOMEDIUM && state == WIMAX_RF_QUERY)
|
||||
result = WIMAX_RF_OFF << 1 | WIMAX_RF_OFF;
|
||||
goto error_not_ready;
|
||||
}
|
||||
switch (state) {
|
||||
case WIMAX_RF_ON:
|
||||
case WIMAX_RF_OFF:
|
||||
result = __wimax_rf_toggle_radio(wimax_dev, state);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF);
|
||||
break;
|
||||
case WIMAX_RF_QUERY:
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
result = wimax_dev->rf_sw << 1 | wimax_dev->rf_hw;
|
||||
error:
|
||||
error_not_ready:
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n",
|
||||
wimax_dev, state, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(wimax_rfkill);
|
||||
|
||||
|
||||
/*
|
||||
* Register a new WiMAX device's RF Kill support
|
||||
*
|
||||
* WARNING: wimax_dev->mutex must be unlocked
|
||||
*/
|
||||
int wimax_rfkill_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
int result;
|
||||
struct rfkill *rfkill;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
/* Initialize RF Kill */
|
||||
result = -ENOMEM;
|
||||
rfkill = rfkill_alloc(wimax_dev->name, dev, RFKILL_TYPE_WIMAX,
|
||||
&wimax_rfkill_ops, wimax_dev);
|
||||
if (rfkill == NULL)
|
||||
goto error_rfkill_allocate;
|
||||
|
||||
d_printf(1, dev, "rfkill %p\n", rfkill);
|
||||
|
||||
wimax_dev->rfkill = rfkill;
|
||||
|
||||
rfkill_init_sw_state(rfkill, 1);
|
||||
result = rfkill_register(wimax_dev->rfkill);
|
||||
if (result < 0)
|
||||
goto error_rfkill_register;
|
||||
|
||||
/* If there is no SW toggle op, SW RFKill is always on */
|
||||
if (wimax_dev->op_rfkill_sw_toggle == NULL)
|
||||
wimax_dev->rf_sw = WIMAX_RF_ON;
|
||||
|
||||
d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev);
|
||||
return 0;
|
||||
|
||||
error_rfkill_register:
|
||||
rfkill_destroy(wimax_dev->rfkill);
|
||||
error_rfkill_allocate:
|
||||
d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deregister a WiMAX device's RF Kill support
|
||||
*
|
||||
* Ick, we can't call rfkill_free() after rfkill_unregister()...oh
|
||||
* well.
|
||||
*
|
||||
* WARNING: wimax_dev->mutex must be unlocked
|
||||
*/
|
||||
void wimax_rfkill_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
rfkill_unregister(wimax_dev->rfkill);
|
||||
rfkill_destroy(wimax_dev->rfkill);
|
||||
d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Exporting to user space over generic netlink
|
||||
*
|
||||
* Parse the rfkill command from user space, return a combination
|
||||
* value that describe the states of the different toggles.
|
||||
*
|
||||
* Only one attribute: the new state requested (on, off or no change,
|
||||
* just query).
|
||||
*/
|
||||
|
||||
int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
struct device *dev;
|
||||
enum wimax_rf_state new_state;
|
||||
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_RFKILL_IFIDX] == NULL) {
|
||||
pr_err("WIMAX_GNL_OP_RFKILL: can't find IFIDX attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
dev = wimax_dev_to_dev(wimax_dev);
|
||||
result = -EINVAL;
|
||||
if (info->attrs[WIMAX_GNL_RFKILL_STATE] == NULL) {
|
||||
dev_err(dev, "WIMAX_GNL_RFKILL: can't find RFKILL_STATE "
|
||||
"attribute\n");
|
||||
goto error_no_pid;
|
||||
}
|
||||
new_state = nla_get_u32(info->attrs[WIMAX_GNL_RFKILL_STATE]);
|
||||
|
||||
/* Execute the operation and send the result back to user space */
|
||||
result = wimax_rfkill(wimax_dev, new_state);
|
||||
error_no_pid:
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
65
net/wimax/op-state-get.c
Normal file
65
net/wimax/op-state-get.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Implement and export a method for getting a WiMAX device current state
|
||||
*
|
||||
* Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
|
||||
*
|
||||
* Based on previous WiMAX core work by:
|
||||
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <net/wimax.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/security.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
#define D_SUBMODULE op_state_get
|
||||
#include "debug-levels.h"
|
||||
|
||||
|
||||
/*
|
||||
* Exporting to user space over generic netlink
|
||||
*
|
||||
* Parse the state get command from user space, return a combination
|
||||
* value that describe the current state.
|
||||
*
|
||||
* No attributes.
|
||||
*/
|
||||
int wimax_gnl_doit_state_get(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int result, ifindex;
|
||||
struct wimax_dev *wimax_dev;
|
||||
|
||||
d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
|
||||
result = -ENODEV;
|
||||
if (info->attrs[WIMAX_GNL_STGET_IFIDX] == NULL) {
|
||||
pr_err("WIMAX_GNL_OP_STATE_GET: can't find IFIDX attribute\n");
|
||||
goto error_no_wimax_dev;
|
||||
}
|
||||
ifindex = nla_get_u32(info->attrs[WIMAX_GNL_STGET_IFIDX]);
|
||||
wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
|
||||
if (wimax_dev == NULL)
|
||||
goto error_no_wimax_dev;
|
||||
/* Execute the operation and send the result back to user space */
|
||||
result = wimax_state_get(wimax_dev);
|
||||
dev_put(wimax_dev->net_dev);
|
||||
error_no_wimax_dev:
|
||||
d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
|
||||
return result;
|
||||
}
|
632
net/wimax/stack.c
Normal file
632
net/wimax/stack.c
Normal file
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Initialization, addition and removal of wimax devices
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This implements:
|
||||
*
|
||||
* - basic life cycle of 'struct wimax_dev' [wimax_dev_*()]; on
|
||||
* addition/registration initialize all subfields and allocate
|
||||
* generic netlink resources for user space communication. On
|
||||
* removal/unregistration, undo all that.
|
||||
*
|
||||
* - device state machine [wimax_state_change()] and support to send
|
||||
* reports to user space when the state changes
|
||||
* [wimax_gnl_re_state_change*()].
|
||||
*
|
||||
* See include/net/wimax.h for rationales and design.
|
||||
*
|
||||
* ROADMAP
|
||||
*
|
||||
* [__]wimax_state_change() Called by drivers to update device's state
|
||||
* wimax_gnl_re_state_change_alloc()
|
||||
* wimax_gnl_re_state_change_send()
|
||||
*
|
||||
* wimax_dev_init() Init a device
|
||||
* wimax_dev_add() Register
|
||||
* wimax_rfkill_add()
|
||||
* wimax_gnl_add() Register all the generic netlink resources.
|
||||
* wimax_id_table_add()
|
||||
* wimax_dev_rm() Unregister
|
||||
* wimax_id_table_rm()
|
||||
* wimax_gnl_rm()
|
||||
* wimax_rfkill_rm()
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wimax.h>
|
||||
#include <linux/module.h>
|
||||
#include "wimax-internal.h"
|
||||
|
||||
|
||||
#define D_SUBMODULE stack
|
||||
#include "debug-levels.h"
|
||||
|
||||
static char wimax_debug_params[128];
|
||||
module_param_string(debug, wimax_debug_params, sizeof(wimax_debug_params),
|
||||
0644);
|
||||
MODULE_PARM_DESC(debug,
|
||||
"String of space-separated NAME:VALUE pairs, where NAMEs "
|
||||
"are the different debug submodules and VALUE are the "
|
||||
"initial debug value to set.");
|
||||
|
||||
/*
|
||||
* Authoritative source for the RE_STATE_CHANGE attribute policy
|
||||
*
|
||||
* We don't really use it here, but /me likes to keep the definition
|
||||
* close to where the data is generated.
|
||||
*/
|
||||
/*
|
||||
static const struct nla_policy wimax_gnl_re_status_change[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 },
|
||||
[WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 },
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Allocate a Report State Change message
|
||||
*
|
||||
* @header: save it, you need it for _send()
|
||||
*
|
||||
* Creates and fills a basic state change message; different code
|
||||
* paths can then add more attributes to the message as needed.
|
||||
*
|
||||
* Use wimax_gnl_re_state_change_send() to send the returned skb.
|
||||
*
|
||||
* Returns: skb with the genl message if ok, IS_ERR() ptr on error
|
||||
* with an errno code.
|
||||
*/
|
||||
static
|
||||
struct sk_buff *wimax_gnl_re_state_change_alloc(
|
||||
struct wimax_dev *wimax_dev,
|
||||
enum wimax_st new_state, enum wimax_st old_state,
|
||||
void **header)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
void *data;
|
||||
struct sk_buff *report_skb;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p new_state %u old_state %u)\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
result = -ENOMEM;
|
||||
report_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (report_skb == NULL) {
|
||||
dev_err(dev, "RE_STCH: can't create message\n");
|
||||
goto error_new;
|
||||
}
|
||||
/* FIXME: sending a group ID as the seq is wrong */
|
||||
data = genlmsg_put(report_skb, 0, wimax_gnl_family.mcgrp_offset,
|
||||
&wimax_gnl_family, 0, WIMAX_GNL_RE_STATE_CHANGE);
|
||||
if (data == NULL) {
|
||||
dev_err(dev, "RE_STCH: can't put data into message\n");
|
||||
goto error_put;
|
||||
}
|
||||
*header = data;
|
||||
|
||||
result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_OLD, old_state);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding OLD attr: %d\n", result);
|
||||
goto error_put;
|
||||
}
|
||||
result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_NEW, new_state);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding NEW attr: %d\n", result);
|
||||
goto error_put;
|
||||
}
|
||||
result = nla_put_u32(report_skb, WIMAX_GNL_STCH_IFIDX,
|
||||
wimax_dev->net_dev->ifindex);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RE_STCH: Error adding IFINDEX attribute\n");
|
||||
goto error_put;
|
||||
}
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %p\n",
|
||||
wimax_dev, new_state, old_state, report_skb);
|
||||
return report_skb;
|
||||
|
||||
error_put:
|
||||
nlmsg_free(report_skb);
|
||||
error_new:
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %d\n",
|
||||
wimax_dev, new_state, old_state, result);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send a Report State Change message (as created with _alloc).
|
||||
*
|
||||
* @report_skb: as returned by wimax_gnl_re_state_change_alloc()
|
||||
* @header: as returned by wimax_gnl_re_state_change_alloc()
|
||||
*
|
||||
* Returns: 0 if ok, < 0 errno code on error.
|
||||
*
|
||||
* If the message is NULL, pretend it didn't happen.
|
||||
*/
|
||||
static
|
||||
int wimax_gnl_re_state_change_send(
|
||||
struct wimax_dev *wimax_dev, struct sk_buff *report_skb,
|
||||
void *header)
|
||||
{
|
||||
int result = 0;
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n",
|
||||
wimax_dev, report_skb);
|
||||
if (report_skb == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
genlmsg_end(report_skb, header);
|
||||
genlmsg_multicast(&wimax_gnl_family, report_skb, 0, 0, GFP_KERNEL);
|
||||
out:
|
||||
d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n",
|
||||
wimax_dev, report_skb, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void __check_new_state(enum wimax_st old_state, enum wimax_st new_state,
|
||||
unsigned int allowed_states_bm)
|
||||
{
|
||||
if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) {
|
||||
pr_err("SW BUG! Forbidden state change %u -> %u\n",
|
||||
old_state, new_state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the current state of a WiMAX device [unlocking version of
|
||||
* wimax_state_change().
|
||||
*/
|
||||
void __wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
|
||||
{
|
||||
struct device *dev = wimax_dev_to_dev(wimax_dev);
|
||||
enum wimax_st old_state = wimax_dev->state;
|
||||
struct sk_buff *stch_skb;
|
||||
void *header;
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p new_state %u [old %u])\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
|
||||
if (WARN_ON(new_state >= __WIMAX_ST_INVALID)) {
|
||||
dev_err(dev, "SW BUG: requesting invalid state %u\n",
|
||||
new_state);
|
||||
goto out;
|
||||
}
|
||||
if (old_state == new_state)
|
||||
goto out;
|
||||
header = NULL; /* gcc complains? can't grok why */
|
||||
stch_skb = wimax_gnl_re_state_change_alloc(
|
||||
wimax_dev, new_state, old_state, &header);
|
||||
|
||||
/* Verify the state transition and do exit-from-state actions */
|
||||
switch (old_state) {
|
||||
case __WIMAX_ST_NULL:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << WIMAX_ST_DOWN);
|
||||
break;
|
||||
case WIMAX_ST_DOWN:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_UNINITIALIZED
|
||||
| 1 << WIMAX_ST_RADIO_OFF);
|
||||
break;
|
||||
case __WIMAX_ST_QUIESCING:
|
||||
__check_new_state(old_state, new_state, 1 << WIMAX_ST_DOWN);
|
||||
break;
|
||||
case WIMAX_ST_UNINITIALIZED:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF);
|
||||
break;
|
||||
case WIMAX_ST_RADIO_OFF:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_READY);
|
||||
break;
|
||||
case WIMAX_ST_READY:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_SCANNING
|
||||
| 1 << WIMAX_ST_CONNECTING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_SCANNING:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY
|
||||
| 1 << WIMAX_ST_CONNECTING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_CONNECTING:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY
|
||||
| 1 << WIMAX_ST_SCANNING
|
||||
| 1 << WIMAX_ST_CONNECTED);
|
||||
break;
|
||||
case WIMAX_ST_CONNECTED:
|
||||
__check_new_state(old_state, new_state,
|
||||
1 << __WIMAX_ST_QUIESCING
|
||||
| 1 << WIMAX_ST_RADIO_OFF
|
||||
| 1 << WIMAX_ST_READY);
|
||||
netif_tx_disable(wimax_dev->net_dev);
|
||||
netif_carrier_off(wimax_dev->net_dev);
|
||||
break;
|
||||
case __WIMAX_ST_INVALID:
|
||||
default:
|
||||
dev_err(dev, "SW BUG: wimax_dev %p is in unknown state %u\n",
|
||||
wimax_dev, wimax_dev->state);
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Execute the actions of entry to the new state */
|
||||
switch (new_state) {
|
||||
case __WIMAX_ST_NULL:
|
||||
dev_err(dev, "SW BUG: wimax_dev %p entering NULL state "
|
||||
"from %u\n", wimax_dev, wimax_dev->state);
|
||||
WARN_ON(1); /* Nobody can enter this state */
|
||||
break;
|
||||
case WIMAX_ST_DOWN:
|
||||
break;
|
||||
case __WIMAX_ST_QUIESCING:
|
||||
break;
|
||||
case WIMAX_ST_UNINITIALIZED:
|
||||
break;
|
||||
case WIMAX_ST_RADIO_OFF:
|
||||
break;
|
||||
case WIMAX_ST_READY:
|
||||
break;
|
||||
case WIMAX_ST_SCANNING:
|
||||
break;
|
||||
case WIMAX_ST_CONNECTING:
|
||||
break;
|
||||
case WIMAX_ST_CONNECTED:
|
||||
netif_carrier_on(wimax_dev->net_dev);
|
||||
netif_wake_queue(wimax_dev->net_dev);
|
||||
break;
|
||||
case __WIMAX_ST_INVALID:
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
__wimax_state_set(wimax_dev, new_state);
|
||||
if (!IS_ERR(stch_skb))
|
||||
wimax_gnl_re_state_change_send(wimax_dev, stch_skb, header);
|
||||
out:
|
||||
d_fnend(3, dev, "(wimax_dev %p new_state %u [old %u]) = void\n",
|
||||
wimax_dev, new_state, old_state);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wimax_state_change - Set the current state of a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (properly referenced)
|
||||
* @new_state: New state to switch to
|
||||
*
|
||||
* This implements the state changes for the wimax devices. It will
|
||||
*
|
||||
* - verify that the state transition is legal (for now it'll just
|
||||
* print a warning if not) according to the table in
|
||||
* linux/wimax.h's documentation for 'enum wimax_st'.
|
||||
*
|
||||
* - perform the actions needed for leaving the current state and
|
||||
* whichever are needed for entering the new state.
|
||||
*
|
||||
* - issue a report to user space indicating the new state (and an
|
||||
* optional payload with information about the new state).
|
||||
*
|
||||
* NOTE: @wimax_dev must be locked
|
||||
*/
|
||||
void wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
|
||||
{
|
||||
/*
|
||||
* A driver cannot take the wimax_dev out of the
|
||||
* __WIMAX_ST_NULL state unless by calling wimax_dev_add(). If
|
||||
* the wimax_dev's state is still NULL, we ignore any request
|
||||
* to change its state because it means it hasn't been yet
|
||||
* registered.
|
||||
*
|
||||
* There is no need to complain about it, as routines that
|
||||
* call this might be shared from different code paths that
|
||||
* are called before or after wimax_dev_add() has done its
|
||||
* job.
|
||||
*/
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
if (wimax_dev->state > __WIMAX_ST_NULL)
|
||||
__wimax_state_change(wimax_dev, new_state);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_state_change);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_state_get() - Return the current state of a WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Returns: Current state of the device according to its driver.
|
||||
*/
|
||||
enum wimax_st wimax_state_get(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
enum wimax_st state;
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
state = wimax_dev->state;
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
return state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_state_get);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_init - initialize a newly allocated instance
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor to initialize.
|
||||
*
|
||||
* Initializes fields of a freshly allocated @wimax_dev instance. This
|
||||
* function assumes that after allocation, the memory occupied by
|
||||
* @wimax_dev was zeroed.
|
||||
*/
|
||||
void wimax_dev_init(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
INIT_LIST_HEAD(&wimax_dev->id_table_node);
|
||||
__wimax_state_set(wimax_dev, __WIMAX_ST_NULL);
|
||||
mutex_init(&wimax_dev->mutex);
|
||||
mutex_init(&wimax_dev->mutex_reset);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_init);
|
||||
|
||||
static const struct nla_policy wimax_gnl_policy[WIMAX_GNL_ATTR_MAX + 1] = {
|
||||
[WIMAX_GNL_RESET_IFIDX] = { .type = NLA_U32, },
|
||||
[WIMAX_GNL_RFKILL_IFIDX] = { .type = NLA_U32, },
|
||||
[WIMAX_GNL_RFKILL_STATE] = {
|
||||
.type = NLA_U32 /* enum wimax_rf_state */
|
||||
},
|
||||
[WIMAX_GNL_STGET_IFIDX] = { .type = NLA_U32, },
|
||||
[WIMAX_GNL_MSG_IFIDX] = { .type = NLA_U32, },
|
||||
[WIMAX_GNL_MSG_DATA] = {
|
||||
.type = NLA_UNSPEC, /* libnl doesn't grok BINARY yet */
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_ops wimax_gnl_ops[] = {
|
||||
{
|
||||
.cmd = WIMAX_GNL_OP_MSG_FROM_USER,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_policy,
|
||||
.doit = wimax_gnl_doit_msg_from_user,
|
||||
},
|
||||
{
|
||||
.cmd = WIMAX_GNL_OP_RESET,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_policy,
|
||||
.doit = wimax_gnl_doit_reset,
|
||||
},
|
||||
{
|
||||
.cmd = WIMAX_GNL_OP_RFKILL,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_policy,
|
||||
.doit = wimax_gnl_doit_rfkill,
|
||||
},
|
||||
{
|
||||
.cmd = WIMAX_GNL_OP_STATE_GET,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.policy = wimax_gnl_policy,
|
||||
.doit = wimax_gnl_doit_state_get,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
size_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size,
|
||||
unsigned char *addr, size_t addr_len)
|
||||
{
|
||||
unsigned int cnt, total;
|
||||
|
||||
for (total = cnt = 0; cnt < addr_len; cnt++)
|
||||
total += scnprintf(addr_str + total, addr_str_size - total,
|
||||
"%02x%c", addr[cnt],
|
||||
cnt == addr_len - 1 ? '\0' : ':');
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_add - Register a new WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor (as embedded in your @net_dev's
|
||||
* priv data). You must have called wimax_dev_init() on it before.
|
||||
*
|
||||
* @net_dev: net device the @wimax_dev is associated with. The
|
||||
* function expects SET_NETDEV_DEV() and register_netdev() were
|
||||
* already called on it.
|
||||
*
|
||||
* Registers the new WiMAX device, sets up the user-kernel control
|
||||
* interface (generic netlink) and common WiMAX infrastructure.
|
||||
*
|
||||
* Note that the parts that will allow interaction with user space are
|
||||
* setup at the very end, when the rest is in place, as once that
|
||||
* happens, the driver might get user space control requests via
|
||||
* netlink or from debugfs that might translate into calls into
|
||||
* wimax_dev->op_*().
|
||||
*/
|
||||
int wimax_dev_add(struct wimax_dev *wimax_dev, struct net_device *net_dev)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = net_dev->dev.parent;
|
||||
char addr_str[32];
|
||||
|
||||
d_fnstart(3, dev, "(wimax_dev %p net_dev %p)\n", wimax_dev, net_dev);
|
||||
|
||||
/* Do the RFKILL setup before locking, as RFKILL will call
|
||||
* into our functions. */
|
||||
wimax_dev->net_dev = net_dev;
|
||||
result = wimax_rfkill_add(wimax_dev);
|
||||
if (result < 0)
|
||||
goto error_rfkill_add;
|
||||
|
||||
/* Set up user-space interaction */
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
wimax_id_table_add(wimax_dev);
|
||||
result = wimax_debugfs_add(wimax_dev);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot initialize debugfs: %d\n",
|
||||
result);
|
||||
goto error_debugfs_add;
|
||||
}
|
||||
|
||||
__wimax_state_set(wimax_dev, WIMAX_ST_DOWN);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
|
||||
wimax_addr_scnprint(addr_str, sizeof(addr_str),
|
||||
net_dev->dev_addr, net_dev->addr_len);
|
||||
dev_err(dev, "WiMAX interface %s (%s) ready\n",
|
||||
net_dev->name, addr_str);
|
||||
d_fnend(3, dev, "(wimax_dev %p net_dev %p) = 0\n", wimax_dev, net_dev);
|
||||
return 0;
|
||||
|
||||
error_debugfs_add:
|
||||
wimax_id_table_rm(wimax_dev);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
wimax_rfkill_rm(wimax_dev);
|
||||
error_rfkill_add:
|
||||
d_fnend(3, dev, "(wimax_dev %p net_dev %p) = %d\n",
|
||||
wimax_dev, net_dev, result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_add);
|
||||
|
||||
|
||||
/**
|
||||
* wimax_dev_rm - Unregister an existing WiMAX device
|
||||
*
|
||||
* @wimax_dev: WiMAX device descriptor
|
||||
*
|
||||
* Unregisters a WiMAX device previously registered for use with
|
||||
* wimax_add_rm().
|
||||
*
|
||||
* IMPORTANT! Must call before calling unregister_netdev().
|
||||
*
|
||||
* After this function returns, you will not get any more user space
|
||||
* control requests (via netlink or debugfs) and thus to wimax_dev->ops.
|
||||
*
|
||||
* Reentrancy control is ensured by setting the state to
|
||||
* %__WIMAX_ST_QUIESCING. rfkill operations coming through
|
||||
* wimax_*rfkill*() will be stopped by the quiescing state; ops coming
|
||||
* from the rfkill subsystem will be stopped by the support being
|
||||
* removed by wimax_rfkill_rm().
|
||||
*/
|
||||
void wimax_dev_rm(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev);
|
||||
|
||||
mutex_lock(&wimax_dev->mutex);
|
||||
__wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
|
||||
wimax_debugfs_rm(wimax_dev);
|
||||
wimax_id_table_rm(wimax_dev);
|
||||
__wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
|
||||
mutex_unlock(&wimax_dev->mutex);
|
||||
wimax_rfkill_rm(wimax_dev);
|
||||
d_fnend(3, NULL, "(wimax_dev %p) = void\n", wimax_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wimax_dev_rm);
|
||||
|
||||
|
||||
/* Debug framework control of debug levels */
|
||||
struct d_level D_LEVEL[] = {
|
||||
D_SUBMODULE_DEFINE(debugfs),
|
||||
D_SUBMODULE_DEFINE(id_table),
|
||||
D_SUBMODULE_DEFINE(op_msg),
|
||||
D_SUBMODULE_DEFINE(op_reset),
|
||||
D_SUBMODULE_DEFINE(op_rfkill),
|
||||
D_SUBMODULE_DEFINE(op_state_get),
|
||||
D_SUBMODULE_DEFINE(stack),
|
||||
};
|
||||
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
|
||||
|
||||
|
||||
struct genl_family wimax_gnl_family = {
|
||||
.id = GENL_ID_GENERATE,
|
||||
.name = "WiMAX",
|
||||
.version = WIMAX_GNL_VERSION,
|
||||
.hdrsize = 0,
|
||||
.maxattr = WIMAX_GNL_ATTR_MAX,
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group wimax_gnl_mcgrps[] = {
|
||||
{ .name = "msg", },
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Shutdown the wimax stack */
|
||||
static
|
||||
int __init wimax_subsys_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
d_fnstart(4, NULL, "()\n");
|
||||
d_parse_params(D_LEVEL, D_LEVEL_SIZE, wimax_debug_params,
|
||||
"wimax.debug");
|
||||
|
||||
snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name),
|
||||
"WiMAX");
|
||||
result = genl_register_family_with_ops_groups(&wimax_gnl_family,
|
||||
wimax_gnl_ops,
|
||||
wimax_gnl_mcgrps);
|
||||
if (unlikely(result < 0)) {
|
||||
pr_err("cannot register generic netlink family: %d\n", result);
|
||||
goto error_register_family;
|
||||
}
|
||||
|
||||
d_fnend(4, NULL, "() = 0\n");
|
||||
return 0;
|
||||
|
||||
error_register_family:
|
||||
d_fnend(4, NULL, "() = %d\n", result);
|
||||
return result;
|
||||
|
||||
}
|
||||
module_init(wimax_subsys_init);
|
||||
|
||||
|
||||
/* Shutdown the wimax stack */
|
||||
static
|
||||
void __exit wimax_subsys_exit(void)
|
||||
{
|
||||
wimax_id_table_release();
|
||||
genl_unregister_family(&wimax_gnl_family);
|
||||
}
|
||||
module_exit(wimax_subsys_exit);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
|
||||
MODULE_DESCRIPTION("Linux WiMAX stack");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
103
net/wimax/wimax-internal.h
Normal file
103
net/wimax/wimax-internal.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Linux WiMAX
|
||||
* Internal API for kernel space WiMAX stack
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
|
||||
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version
|
||||
* 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*
|
||||
* This header file is for declarations and definitions internal to
|
||||
* the WiMAX stack. For public APIs and documentation, see
|
||||
* include/net/wimax.h and include/linux/wimax.h.
|
||||
*/
|
||||
|
||||
#ifndef __WIMAX_INTERNAL_H__
|
||||
#define __WIMAX_INTERNAL_H__
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#endif
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <net/wimax.h>
|
||||
|
||||
|
||||
/*
|
||||
* Decide if a (locked) device is ready for use
|
||||
*
|
||||
* Before using the device structure, it must be locked
|
||||
* (wimax_dev->mutex). As well, most operations need to call this
|
||||
* function to check if the state is the right one.
|
||||
*
|
||||
* An error value will be returned if the state is not the right
|
||||
* one. In that case, the caller should not attempt to use the device
|
||||
* and just unlock it.
|
||||
*/
|
||||
static inline __must_check
|
||||
int wimax_dev_is_ready(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
if (wimax_dev->state == __WIMAX_ST_NULL)
|
||||
return -EINVAL; /* Device is not even registered! */
|
||||
if (wimax_dev->state == WIMAX_ST_DOWN)
|
||||
return -ENOMEDIUM;
|
||||
if (wimax_dev->state == __WIMAX_ST_QUIESCING)
|
||||
return -ESHUTDOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline
|
||||
void __wimax_state_set(struct wimax_dev *wimax_dev, enum wimax_st state)
|
||||
{
|
||||
wimax_dev->state = state;
|
||||
}
|
||||
void __wimax_state_change(struct wimax_dev *, enum wimax_st);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
int wimax_debugfs_add(struct wimax_dev *);
|
||||
void wimax_debugfs_rm(struct wimax_dev *);
|
||||
#else
|
||||
static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void wimax_debugfs_rm(struct wimax_dev *wimax_dev) {}
|
||||
#endif
|
||||
|
||||
void wimax_id_table_add(struct wimax_dev *);
|
||||
struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int);
|
||||
void wimax_id_table_rm(struct wimax_dev *);
|
||||
void wimax_id_table_release(void);
|
||||
|
||||
int wimax_rfkill_add(struct wimax_dev *);
|
||||
void wimax_rfkill_rm(struct wimax_dev *);
|
||||
|
||||
/* generic netlink */
|
||||
extern struct genl_family wimax_gnl_family;
|
||||
|
||||
/* ops */
|
||||
int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info);
|
||||
int wimax_gnl_doit_reset(struct sk_buff *skb, struct genl_info *info);
|
||||
int wimax_gnl_doit_rfkill(struct sk_buff *skb, struct genl_info *info);
|
||||
int wimax_gnl_doit_state_get(struct sk_buff *skb, struct genl_info *info);
|
||||
|
||||
#endif /* #ifdef __KERNEL__ */
|
||||
#endif /* #ifndef __WIMAX_INTERNAL_H__ */
|
Loading…
Add table
Add a link
Reference in a new issue