staging: wimax: delete from the tree.

As stated in f54ec58fee ("wimax: move out to staging"), the wimax code
is dead with no known users.  It has stayed in staging for 5 months,
with no one willing to take up the codebase for maintance and support,
so let's just remove it entirely for now.

If someone comes along and wants to revive it, a simple revert of this
patch is a good place to start.

Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Wang Hai <wanghai38@huawei.com>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: Anirudh Rayabharam <mail@anirudhrb.com>
Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Hemansh Agnihotri <hemanshagnihotri27@gmail.com>
Cc: Ayush <ayush@disroot.org>
Cc: Xin Tan <tanxin.ctf@gmail.com>
Cc: Xiyu Yang <xiyuyang19@fudan.edu.cn>
Cc: Shannon Nelson <snelson@pensando.io>
Link: https://lore.kernel.org/r/20210318093315.694404-1-gregkh@linuxfoundation.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Greg Kroah-Hartman 2021-03-18 10:33:15 +01:00
parent 6da2f76058
commit 18507b8f63
41 changed files with 0 additions and 15265 deletions

View File

@ -106,8 +106,6 @@ source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/qlge/Kconfig"
source "drivers/staging/wimax/Kconfig"
source "drivers/staging/wfx/Kconfig"
source "drivers/staging/hikey9xx/Kconfig"

View File

@ -43,6 +43,5 @@ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_QLGE) += qlge/
obj-$(CONFIG_WIMAX) += wimax/
obj-$(CONFIG_WFX) += wfx/
obj-y += hikey9xx/

View File

@ -1,283 +0,0 @@
.. include:: <isonum.txt>
====================================================
Driver for the Intel Wireless Wimax Connection 2400m
====================================================
:Copyright: |copy| 2008 Intel Corporation < linux-wimax@intel.com >
This provides a driver for the Intel Wireless WiMAX Connection 2400m
and a basic Linux kernel WiMAX stack.
1. Requirements
===============
* Linux installation with Linux kernel 2.6.22 or newer (if building
from a separate tree)
* Intel i2400m Echo Peak or Baxter Peak; this includes the Intel
Wireless WiMAX/WiFi Link 5x50 series.
* build tools:
+ Linux kernel development package for the target kernel; to
build against your currently running kernel, you need to have
the kernel development package corresponding to the running
image installed (usually if your kernel is named
linux-VERSION, the development package is called
linux-dev-VERSION or linux-headers-VERSION).
+ GNU C Compiler, make
2. Compilation and installation
===============================
2.1. Compilation of the drivers included in the kernel
------------------------------------------------------
Configure the kernel; to enable the WiMAX drivers select Drivers >
Networking Drivers > WiMAX device support. Enable all of them as
modules (easier).
If USB or SDIO are not enabled in the kernel configuration, the options
to build the i2400m USB or SDIO drivers will not show. Enable said
subsystems and go back to the WiMAX menu to enable the drivers.
Compile and install your kernel as usual.
2.2. Compilation of the drivers distributed as an standalone module
-------------------------------------------------------------------
To compile::
$ cd source/directory
$ make
Once built you can load and unload using the provided load.sh script;
load.sh will load the modules, load.sh u will unload them.
To install in the default kernel directories (and enable auto loading
when the device is plugged)::
$ make install
$ depmod -a
If your kernel development files are located in a non standard
directory or if you want to build for a kernel that is not the
currently running one, set KDIR to the right location::
$ make KDIR=/path/to/kernel/dev/tree
For more information, please contact linux-wimax@intel.com.
3. Installing the firmware
--------------------------
The firmware can be obtained from http://linuxwimax.org or might have
been supplied with your hardware.
It has to be installed in the target system::
$ cp FIRMWAREFILE.sbcf /lib/firmware/i2400m-fw-BUSTYPE-1.3.sbcf
* NOTE: if your firmware came in an .rpm or .deb file, just install
it as normal, with the rpm (rpm -i FIRMWARE.rpm) or dpkg
(dpkg -i FIRMWARE.deb) commands. No further action is needed.
* BUSTYPE will be usb or sdio, depending on the hardware you have.
Each hardware type comes with its own firmware and will not work
with other types.
4. Design
=========
This package contains two major parts: a WiMAX kernel stack and a
driver for the Intel i2400m.
The WiMAX stack is designed to provide for common WiMAX control
services to current and future WiMAX devices from any vendor; please
see README.wimax for details.
The i2400m kernel driver is broken up in two main parts: the bus
generic driver and the bus-specific drivers. The bus generic driver
forms the drivercore and contain no knowledge of the actual method we
use to connect to the device. The bus specific drivers are just the
glue to connect the bus-generic driver and the device. Currently only
USB and SDIO are supported. See drivers/net/wimax/i2400m/i2400m.h for
more information.
The bus generic driver is logically broken up in two parts: OS-glue and
hardware-glue. The OS-glue interfaces with Linux. The hardware-glue
interfaces with the device on using an interface provided by the
bus-specific driver. The reason for this breakup is to be able to
easily reuse the hardware-glue to write drivers for other OSes; note
the hardware glue part is written as a native Linux driver; no
abstraction layers are used, so to port to another OS, the Linux kernel
API calls should be replaced with the target OS's.
5. Usage
========
To load the driver, follow the instructions in the install section;
once the driver is loaded, plug in the device (unless it is permanently
plugged in). The driver will enumerate the device, upload the firmware
and output messages in the kernel log (dmesg, /var/log/messages or
/var/log/kern.log) such as::
...
i2400m_usb 5-4:1.0: firmware interface version 8.0.0
i2400m_usb 5-4:1.0: WiMAX interface wmx0 (00:1d:e1:01:94:2c) ready
At this point the device is ready to work.
Current versions require the Intel WiMAX Network Service in userspace
to make things work. See the network service's README for instructions
on how to scan, connect and disconnect.
5.1. Module parameters
----------------------
Module parameters can be set at kernel or module load time or by
echoing values::
$ echo VALUE > /sys/module/MODULENAME/parameters/PARAMETERNAME
To make changes permanent, for example, for the i2400m module, you can
also create a file named /etc/modprobe.d/i2400m containing::
options i2400m idle_mode_disabled=1
To find which parameters are supported by a module, run::
$ modinfo path/to/module.ko
During kernel bootup (if the driver is linked in the kernel), specify
the following to the kernel command line::
i2400m.PARAMETER=VALUE
5.1.1. i2400m: idle_mode_disabled
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The i2400m module supports a parameter to disable idle mode. This
parameter, once set, will take effect only when the device is
reinitialized by the driver (eg: following a reset or a reconnect).
5.2. Debug operations: debugfs entries
--------------------------------------
The driver will register debugfs entries that allow the user to tweak
debug settings. There are three main container directories where
entries are placed, which correspond to the three blocks a i2400m WiMAX
driver has:
* /sys/kernel/debug/wimax:DEVNAME/ for the generic WiMAX stack
controls
* /sys/kernel/debug/wimax:DEVNAME/i2400m for the i2400m generic
driver controls
* /sys/kernel/debug/wimax:DEVNAME/i2400m-usb (or -sdio) for the
bus-specific i2400m-usb or i2400m-sdio controls).
Of course, if debugfs is mounted in a directory other than
/sys/kernel/debug, those paths will change.
5.2.1. Increasing debug output
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The files named *dl_* indicate knobs for controlling the debug output
of different submodules::
# find /sys/kernel/debug/wimax\:wmx0 -name \*dl_\*
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_tx
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_rx
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_notif
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_fw
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_usb
/sys/kernel/debug/wimax:wmx0/i2400m/dl_tx
/sys/kernel/debug/wimax:wmx0/i2400m/dl_rx
/sys/kernel/debug/wimax:wmx0/i2400m/dl_rfkill
/sys/kernel/debug/wimax:wmx0/i2400m/dl_netdev
/sys/kernel/debug/wimax:wmx0/i2400m/dl_fw
/sys/kernel/debug/wimax:wmx0/i2400m/dl_debugfs
/sys/kernel/debug/wimax:wmx0/i2400m/dl_driver
/sys/kernel/debug/wimax:wmx0/i2400m/dl_control
/sys/kernel/debug/wimax:wmx0/wimax_dl_stack
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_rfkill
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_reset
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_msg
/sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
/sys/kernel/debug/wimax:wmx0/wimax_dl_debugfs
By reading the file you can obtain the current value of said debug
level; by writing to it, you can set it.
To increase the debug level of, for example, the i2400m's generic TX
engine, just write::
$ echo 3 > /sys/kernel/debug/wimax:wmx0/i2400m/dl_tx
Increasing numbers yield increasing debug information; for details of
what is printed and the available levels, check the source. The code
uses 0 for disabled and increasing values until 8.
5.2.2. RX and TX statistics
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The i2400m/rx_stats and i2400m/tx_stats provide statistics about the
data reception/delivery from the device::
$ cat /sys/kernel/debug/wimax:wmx0/i2400m/rx_stats
45 1 3 34 3104 48 480
The numbers reported are:
* packets/RX-buffer: total, min, max
* RX-buffers: total RX buffers received, accumulated RX buffer size
in bytes, min size received, max size received
Thus, to find the average buffer size received, divide accumulated
RX-buffer / total RX-buffers.
To clear the statistics back to 0, write anything to the rx_stats file::
$ echo 1 > /sys/kernel/debug/wimax:wmx0/i2400m_rx_stats
Likewise for TX.
Note the packets this debug file refers to are not network packet, but
packets in the sense of the device-specific protocol for communication
to the host. See drivers/net/wimax/i2400m/tx.c.
5.2.3. Tracing messages received from user space
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To echo messages received from user space into the trace pipe that the
i2400m driver creates, set the debug file i2400m/trace_msg_from_user to
1::
$ echo 1 > /sys/kernel/debug/wimax:wmx0/i2400m/trace_msg_from_user
5.2.4. Performing a device reset
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By writing a 0, a 1 or a 2 to the file
/sys/kernel/debug/wimax:wmx0/reset, the driver performs a warm (without
disconnecting from the bus), cold (disconnecting from the bus) or bus
(bus specific) reset on the device.
5.2.5. Asking the device to enter power saving mode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By writing any value to the /sys/kernel/debug/wimax:wmx0 file, the
device will attempt to enter power saving mode.
6. Troubleshooting
==================
6.1. Driver complains about ``i2400m-fw-usb-1.2.sbcf: request failed``
----------------------------------------------------------------------
If upon connecting the device, the following is output in the kernel
log::
i2400m_usb 5-4:1.0: fw i2400m-fw-usb-1.3.sbcf: request failed: -2
This means that the driver cannot locate the firmware file named
/lib/firmware/i2400m-fw-usb-1.2.sbcf. Check that the file is present in
the right location.

View File

@ -1,19 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0
===============
WiMAX subsystem
===============
.. toctree::
:maxdepth: 2
wimax
i2400m
.. only:: subproject and html
Indices
=======
* :ref:`genindex`

View File

@ -1,89 +0,0 @@
.. include:: <isonum.txt>
========================
Linux kernel WiMAX stack
========================
:Copyright: |copy| 2008 Intel Corporation < linux-wimax@intel.com >
This provides a basic Linux kernel WiMAX stack to provide a common
control API for WiMAX devices, usable from kernel and user space.
1. Design
=========
The WiMAX stack is designed to provide for common WiMAX control
services to current and future WiMAX devices from any vendor.
Because currently there is only one and we don't know what would be the
common services, the APIs it currently provides are very minimal.
However, it is done in such a way that it is easily extensible to
accommodate future requirements.
The stack works by embedding a struct wimax_dev in your device's
control structures. This provides a set of callbacks that the WiMAX
stack will call in order to implement control operations requested by
the user. As well, the stack provides API functions that the driver
calls to notify about changes of state in the device.
The stack exports the API calls needed to control the device to user
space using generic netlink as a marshalling mechanism. You can access
them using your own code or use the wrappers provided for your
convenience in libwimax (in the wimax-tools package).
For detailed information on the stack, please see
include/linux/wimax.h.
2. Usage
========
For usage in a driver (registration, API, etc) please refer to the
instructions in the header file include/linux/wimax.h.
When a device is registered with the WiMAX stack, a set of debugfs
files will appear in /sys/kernel/debug/wimax:wmxX can tweak for
control.
2.1. Obtaining debug information: debugfs entries
-------------------------------------------------
The WiMAX stack is compiled, by default, with debug messages that can
be used to diagnose issues. By default, said messages are disabled.
The drivers will register debugfs entries that allow the user to tweak
debug settings.
Each driver, when registering with the stack, will cause a debugfs
directory named wimax:DEVICENAME to be created; optionally, it might
create more subentries below it.
2.1.1. Increasing debug output
------------------------------
The files named *dl_* indicate knobs for controlling the debug output
of different submodules of the WiMAX stack::
# find /sys/kernel/debug/wimax\:wmx0 -name \*dl_\*
/sys/kernel/debug/wimax:wmx0/wimax_dl_stack
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_rfkill
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_reset
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_msg
/sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
/sys/kernel/debug/wimax:wmx0/wimax_dl_debugfs
/sys/kernel/debug/wimax:wmx0/.... # other driver specific files
NOTE:
Of course, if debugfs is mounted in a directory other than
/sys/kernel/debug, those paths will change.
By reading the file you can obtain the current value of said debug
level; by writing to it, you can set it.
To increase the debug level of, for example, the id-table submodule,
just write:
$ echo 3 > /sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
Increasing numbers yield increasing debug information; for details of
what is printed and the available levels, check the source. The code
uses 0 for disabled and increasing values until 8.

View File

@ -1,47 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# WiMAX LAN device configuration
#
menuconfig WIMAX
tristate "WiMAX Wireless Broadband support"
depends on NET
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).
if WIMAX
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.
source "drivers/staging/wimax/i2400m/Kconfig"
endif

View File

@ -1,15 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
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
obj-$(CONFIG_WIMAX_I2400M) += i2400m/

View File

@ -1,18 +0,0 @@
There are no known users of this driver as of October 2020, and it will
be removed unless someone turns out to still need it in future releases.
According to https://en.wikipedia.org/wiki/List_of_WiMAX_networks, there
have been many public wimax networks, but it appears that many of these
have migrated to LTE or discontinued their service altogether. As most
PCs and phones lack WiMAX hardware support, the remaining networks tend
to use standalone routers. These almost certainly run Linux, but not a
modern kernel or the mainline wimax driver stack.
NetworkManager appears to have dropped userspace support in 2015
https://bugzilla.gnome.org/show_bug.cgi?id=747846, the www.linuxwimax.org
site had already shut down earlier.
WiMax is apparently still being deployed on airport campus networks
("AeroMACS"), but in a frequency band that was not supported by the old
Intel 2400m (used in Sandy Bridge laptops and earlier), which is the
only driver using the kernel's wimax stack.

View File

@ -1,29 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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>
*/
#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__ */

View File

@ -1,38 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux WiMAX
* Debugfs support
*
* Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*/
#include <linux/debugfs.h>
#include "linux-wimax.h"
#include "wimax-internal.h"
#define D_SUBMODULE debugfs
#include "debug-levels.h"
void wimax_debugfs_add(struct wimax_dev *wimax_dev)
{
struct net_device *net_dev = wimax_dev->net_dev;
struct dentry *dentry;
char buf[128];
snprintf(buf, sizeof(buf), "wimax:%s", net_dev->name);
dentry = debugfs_create_dir(buf, NULL);
wimax_dev->debugfs_dentry = dentry;
d_level_register_debugfs("wimax_dl_", debugfs, dentry);
d_level_register_debugfs("wimax_dl_", id_table, dentry);
d_level_register_debugfs("wimax_dl_", op_msg, dentry);
d_level_register_debugfs("wimax_dl_", op_reset, dentry);
d_level_register_debugfs("wimax_dl_", op_rfkill, dentry);
d_level_register_debugfs("wimax_dl_", op_state_get, dentry);
d_level_register_debugfs("wimax_dl_", stack, dentry);
}
void wimax_debugfs_rm(struct wimax_dev *wimax_dev)
{
debugfs_remove_recursive(wimax_dev->debugfs_dentry);
}

View File

@ -1,37 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config WIMAX_I2400M
tristate
depends on WIMAX
select FW_LOADER
comment "Enable USB support to see WiMAX USB drivers"
depends on USB = n
config WIMAX_I2400M_USB
tristate "Intel Wireless WiMAX Connection 2400 over USB (including 5x50)"
depends on WIMAX && USB
select WIMAX_I2400M
help
Select if you have a device based on the Intel WiMAX
Connection 2400 over USB (like any of the Intel Wireless
WiMAX/WiFi Link 5x50 series).
If unsure, it is safe to select M (module).
config WIMAX_I2400M_DEBUG_LEVEL
int "WiMAX i2400m debug level"
depends on WIMAX_I2400M
default 8
help
Select the maximum debug verbosity level to be compiled into
the WiMAX i2400m driver code.
By default, this is disabled at runtime and can be
selectively enabled at runtime 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.

View File

@ -1,23 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_WIMAX_I2400M) += i2400m.o
obj-$(CONFIG_WIMAX_I2400M_USB) += i2400m-usb.o
i2400m-y := \
control.o \
driver.o \
fw.o \
op-rfkill.o \
sysfs.o \
netdev.o \
tx.o \
rx.o
i2400m-$(CONFIG_DEBUG_FS) += debugfs.o
i2400m-usb-y := \
usb-fw.o \
usb-notif.o \
usb-tx.o \
usb-rx.o \
usb.o

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Wireless WiMAX Connection 2400m
* Debug levels control file for the i2400m module
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*/
#ifndef __debug_levels__h__
#define __debug_levels__h__
/* Maximum compile and run time debug level for all submodules */
#define D_MODULENAME i2400m
#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL
#include "../linux-wimax-debug.h"
/* List of all the enabled modules */
enum d_module {
D_SUBMODULE_DECLARE(control),
D_SUBMODULE_DECLARE(driver),
D_SUBMODULE_DECLARE(debugfs),
D_SUBMODULE_DECLARE(fw),
D_SUBMODULE_DECLARE(netdev),
D_SUBMODULE_DECLARE(rfkill),
D_SUBMODULE_DECLARE(rx),
D_SUBMODULE_DECLARE(sysfs),
D_SUBMODULE_DECLARE(tx),
};
#endif /* #ifndef __debug_levels__h__ */

View File

@ -1,253 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless WiMAX Connection 2400m
* Debugfs interfaces to manipulate driver and device information
*
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*/
#include <linux/debugfs.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/export.h>
#include "i2400m.h"
#define D_SUBMODULE debugfs
#include "debug-levels.h"
static
int debugfs_netdev_queue_stopped_get(void *data, u64 *val)
{
struct i2400m *i2400m = data;
*val = netif_queue_stopped(i2400m->wimax_dev.net_dev);
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_netdev_queue_stopped,
debugfs_netdev_queue_stopped_get,
NULL, "%llu\n");
/*
* We don't allow partial reads of this file, as then the reader would
* get weirdly confused data as it is updated.
*
* So or you read it all or nothing; if you try to read with an offset
* != 0, we consider you are done reading.
*/
static
ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
char buf[128];
unsigned long flags;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
spin_lock_irqsave(&i2400m->rx_lock, flags);
snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
i2400m->rx_pl_num, i2400m->rx_pl_min,
i2400m->rx_pl_max, i2400m->rx_num,
i2400m->rx_size_acc,
i2400m->rx_size_min, i2400m->rx_size_max);
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
}
/* Any write clears the stats */
static
ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
unsigned long flags;
spin_lock_irqsave(&i2400m->rx_lock, flags);
i2400m->rx_pl_num = 0;
i2400m->rx_pl_max = 0;
i2400m->rx_pl_min = UINT_MAX;
i2400m->rx_num = 0;
i2400m->rx_size_acc = 0;
i2400m->rx_size_min = UINT_MAX;
i2400m->rx_size_max = 0;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
return count;
}
static
const struct file_operations i2400m_rx_stats_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = i2400m_rx_stats_read,
.write = i2400m_rx_stats_write,
.llseek = default_llseek,
};
/* See i2400m_rx_stats_read() */
static
ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
char buf[128];
unsigned long flags;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
spin_lock_irqsave(&i2400m->tx_lock, flags);
snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
i2400m->tx_pl_num, i2400m->tx_pl_min,
i2400m->tx_pl_max, i2400m->tx_num,
i2400m->tx_size_acc,
i2400m->tx_size_min, i2400m->tx_size_max);
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
}
/* Any write clears the stats */
static
ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
unsigned long flags;
spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400m->tx_pl_num = 0;
i2400m->tx_pl_max = 0;
i2400m->tx_pl_min = UINT_MAX;
i2400m->tx_num = 0;
i2400m->tx_size_acc = 0;
i2400m->tx_size_min = UINT_MAX;
i2400m->tx_size_max = 0;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
return count;
}
static
const struct file_operations i2400m_tx_stats_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.read = i2400m_tx_stats_read,
.write = i2400m_tx_stats_write,
.llseek = default_llseek,
};
/* Write 1 to ask the device to go into suspend */
static
int debugfs_i2400m_suspend_set(void *data, u64 val)
{
int result;
struct i2400m *i2400m = data;
result = i2400m_cmd_enter_powersave(i2400m);
if (result >= 0)
result = 0;
return result;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_suspend,
NULL, debugfs_i2400m_suspend_set,
"%llu\n");
/*
* Reset the device
*
* Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus
* reset (as defined by enum i2400m_reset_type).
*/
static
int debugfs_i2400m_reset_set(void *data, u64 val)
{
int result;
struct i2400m *i2400m = data;
enum i2400m_reset_type rt = val;
switch(rt) {
case I2400M_RT_WARM:
case I2400M_RT_COLD:
case I2400M_RT_BUS:
result = i2400m_reset(i2400m, rt);
if (result >= 0)
result = 0;
break;
default:
result = -EINVAL;
}
return result;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_i2400m_reset,
NULL, debugfs_i2400m_reset_set,
"%llu\n");
void i2400m_debugfs_add(struct i2400m *i2400m)
{
struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry;
dentry = debugfs_create_dir("i2400m", dentry);
i2400m->debugfs_dentry = dentry;
d_level_register_debugfs("dl_", control, dentry);
d_level_register_debugfs("dl_", driver, dentry);
d_level_register_debugfs("dl_", debugfs, dentry);
d_level_register_debugfs("dl_", fw, dentry);
d_level_register_debugfs("dl_", netdev, dentry);
d_level_register_debugfs("dl_", rfkill, dentry);
d_level_register_debugfs("dl_", rx, dentry);
d_level_register_debugfs("dl_", tx, dentry);
debugfs_create_size_t("tx_in", 0400, dentry, &i2400m->tx_in);
debugfs_create_size_t("tx_out", 0400, dentry, &i2400m->tx_out);
debugfs_create_u32("state", 0600, dentry, &i2400m->state);
/*
* Trace received messages from user space
*
* In order to tap the bidirectional message stream in the
* 'msg' pipe, user space can read from the 'msg' pipe;
* however, due to limitations in libnl, we can't know what
* the different applications are sending down to the kernel.
*
* So we have this hack where the driver will echo any message
* received on the msg pipe from user space [through a call to
* wimax_dev->op_msg_from_user() into
* i2400m_op_msg_from_user()] into the 'trace' pipe that this
* driver creates.
*
* So then, reading from both the 'trace' and 'msg' pipes in
* user space will provide a full dump of the traffic.
*
* Write 1 to activate, 0 to clear.
*
* It is not really very atomic, but it is also not too
* critical.
*/
debugfs_create_u8("trace_msg_from_user", 0600, dentry,
&i2400m->trace_msg_from_user);
debugfs_create_file("netdev_queue_stopped", 0400, dentry, i2400m,
&fops_netdev_queue_stopped);
debugfs_create_file("rx_stats", 0600, dentry, i2400m,
&i2400m_rx_stats_fops);
debugfs_create_file("tx_stats", 0600, dentry, i2400m,
&i2400m_tx_stats_fops);
debugfs_create_file("suspend", 0200, dentry, i2400m,
&fops_i2400m_suspend);
debugfs_create_file("reset", 0200, dentry, i2400m, &fops_i2400m_reset);
}
void i2400m_debugfs_rm(struct i2400m *i2400m)
{
debugfs_remove_recursive(i2400m->debugfs_dentry);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,275 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB-specific i2400m driver definitions
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
*
* This driver implements the bus-specific part of the i2400m for
* USB. Check i2400m.h for a generic driver description.
*
* ARCHITECTURE
*
* This driver listens to notifications sent from the notification
* endpoint (in usb-notif.c); when data is ready to read, the code in
* there schedules a read from the device (usb-rx.c) and then passes
* the data to the generic RX code (rx.c).
*
* When the generic driver needs to send data (network or control), it
* queues up in the TX FIFO (tx.c) and that will notify the driver
* through the i2400m->bus_tx_kick() callback
* (usb-tx.c:i2400mu_bus_tx_kick) which will send the items in the
* FIFO queue.
*
* This driver, as well, implements the USB-specific ops for the generic
* driver to be able to setup/teardown communication with the device
* [i2400m_bus_dev_start() and i2400m_bus_dev_stop()], reseting the
* device [i2400m_bus_reset()] and performing firmware upload
* [i2400m_bus_bm_cmd() and i2400_bus_bm_wait_for_ack()].
*/
#ifndef __I2400M_USB_H__
#define __I2400M_USB_H__
#include "i2400m.h"
#include <linux/kthread.h>
/*
* Error Density Count: cheapo error density (over time) counter
*
* Originally by Reinette Chatre <reinette.chatre@intel.com>
*
* Embed an 'struct edc' somewhere. Each time there is a soft or
* retryable error, call edc_inc() and check if the error top
* watermark has been reached.
*/
enum {
EDC_MAX_ERRORS = 10,
EDC_ERROR_TIMEFRAME = HZ,
};
/* error density counter */
struct edc {
unsigned long timestart;
u16 errorcount;
};
struct i2400m_endpoint_cfg {
unsigned char bulk_out;
unsigned char notification;
unsigned char reset_cold;
unsigned char bulk_in;
};
static inline void edc_init(struct edc *edc)
{
edc->timestart = jiffies;
}
/**
* edc_inc - report a soft error and check if we are over the watermark
*
* @edc: pointer to error density counter.
* @max_err: maximum number of errors we can accept over the timeframe
* @timeframe: length of the timeframe (in jiffies).
*
* Returns: !0 1 if maximum acceptable errors per timeframe has been
* exceeded. 0 otherwise.
*
* This is way to determine if the number of acceptable errors per time
* period has been exceeded. It is not accurate as there are cases in which
* this scheme will not work, for example if there are periodic occurrences
* of errors that straddle updates to the start time. This scheme is
* sufficient for our usage.
*
* To use, embed a 'struct edc' somewhere, initialize it with
* edc_init() and when an error hits:
*
* if (do_something_fails_with_a_soft_error) {
* if (edc_inc(&my->edc, MAX_ERRORS, MAX_TIMEFRAME))
* Ops, hard error, do something about it
* else
* Retry or ignore, depending on whatever
* }
*/
static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe)
{
unsigned long now;
now = jiffies;
if (time_after(now, edc->timestart + timeframe)) {
edc->errorcount = 1;
edc->timestart = now;
} else if (++edc->errorcount > max_err) {
edc->errorcount = 0;
edc->timestart = now;
return 1;
}
return 0;
}
/* Host-Device interface for USB */
enum {
I2400M_USB_BOOT_RETRIES = 3,
I2400MU_MAX_NOTIFICATION_LEN = 256,
I2400MU_BLK_SIZE = 16,
I2400MU_PL_SIZE_MAX = 0x3EFF,
/* Device IDs */
USB_DEVICE_ID_I6050 = 0x0186,
USB_DEVICE_ID_I6050_2 = 0x0188,
USB_DEVICE_ID_I6150 = 0x07d6,
USB_DEVICE_ID_I6150_2 = 0x07d7,
USB_DEVICE_ID_I6150_3 = 0x07d9,
USB_DEVICE_ID_I6250 = 0x0187,
};
/**
* struct i2400mu - descriptor for a USB connected i2400m
*
* @i2400m: bus-generic i2400m implementation; has to be first (see
* it's documentation in i2400m.h).
*
* @usb_dev: pointer to our USB device
*
* @usb_iface: pointer to our USB interface
*
* @urb_edc: error density counter; used to keep a density-on-time tab
* on how many soft (retryable or ignorable) errors we get. If we
* go over the threshold, we consider the bus transport is failing
* too much and reset.
*
* @notif_urb: URB for receiving notifications from the device.
*
* @tx_kthread: thread we use for data TX. We use a thread because in
* order to do deep power saving and put the device to sleep, we
* need to call usb_autopm_*() [blocking functions].
*
* @tx_wq: waitqueue for the TX kthread to sleep when there is no data
* to be sent; when more data is available, it is woken up by
* i2400mu_bus_tx_kick().
*
* @rx_kthread: thread we use for data RX. We use a thread because in
* order to do deep power saving and put the device to sleep, we
* need to call usb_autopm_*() [blocking functions].
*
* @rx_wq: waitqueue for the RX kthread to sleep when there is no data
* to receive. When data is available, it is woken up by
* usb-notif.c:i2400mu_notification_grok().
*
* @rx_pending_count: number of rx-data-ready notifications that were
* still not handled by the RX kthread.
*
* @rx_size: current RX buffer size that is being used.
*
* @rx_size_acc: accumulator of the sizes of the previous read
* transactions.
*
* @rx_size_cnt: number of read transactions accumulated in
* @rx_size_acc.
*
* @do_autopm: disable(0)/enable(>0) calling the
* usb_autopm_get/put_interface() barriers when executing
* commands. See doc in i2400mu_suspend() for more information.
*
* @rx_size_auto_shrink: if true, the rx_size is shrunk
* automatically based on the average size of the received
* transactions. This allows the receive code to allocate smaller
* chunks of memory and thus reduce pressure on the memory
* allocator by not wasting so much space. By default it is
* enabled.
*
* @debugfs_dentry: hookup for debugfs files.
* These have to be in a separate directory, a child of
* (wimax_dev->debugfs_dentry) so they can be removed when the
* module unloads, as we don't keep each dentry.
*/
struct i2400mu {
struct i2400m i2400m; /* FIRST! See doc */
struct usb_device *usb_dev;
struct usb_interface *usb_iface;
struct edc urb_edc; /* Error density counter */
struct i2400m_endpoint_cfg endpoint_cfg;
struct urb *notif_urb;
struct task_struct *tx_kthread;
wait_queue_head_t tx_wq;
struct task_struct *rx_kthread;
wait_queue_head_t rx_wq;
atomic_t rx_pending_count;
size_t rx_size, rx_size_acc, rx_size_cnt;
atomic_t do_autopm;
u8 rx_size_auto_shrink;
struct dentry *debugfs_dentry;
unsigned i6050:1; /* 1 if this is a 6050 based SKU */
};
static inline
void i2400mu_init(struct i2400mu *i2400mu)
{
i2400m_init(&i2400mu->i2400m);
edc_init(&i2400mu->urb_edc);
init_waitqueue_head(&i2400mu->tx_wq);
atomic_set(&i2400mu->rx_pending_count, 0);
init_waitqueue_head(&i2400mu->rx_wq);
i2400mu->rx_size = PAGE_SIZE - sizeof(struct skb_shared_info);
atomic_set(&i2400mu->do_autopm, 1);
i2400mu->rx_size_auto_shrink = 1;
}
int i2400mu_notification_setup(struct i2400mu *);
void i2400mu_notification_release(struct i2400mu *);
int i2400mu_rx_setup(struct i2400mu *);
void i2400mu_rx_release(struct i2400mu *);
void i2400mu_rx_kick(struct i2400mu *);
int i2400mu_tx_setup(struct i2400mu *);
void i2400mu_tx_release(struct i2400mu *);
void i2400mu_bus_tx_kick(struct i2400m *);
ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *,
const struct i2400m_bootrom_header *, size_t,
int);
ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *,
struct i2400m_bootrom_header *, size_t);
#endif /* #ifndef __I2400M_USB_H__ */

View File

@ -1,970 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Declarations for bus-generic internal APIs
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
*
* GENERAL DRIVER ARCHITECTURE
*
* The i2400m driver is split in the following two major parts:
*
* - bus specific driver
* - bus generic driver (this part)
*
* The bus specific driver sets up stuff specific to the bus the
* device is connected to (USB, PCI, tam-tam...non-authoritative
* nor binding list) which is basically the device-model management
* (probe/disconnect, etc), moving data from device to kernel and
* back, doing the power saving details and reseting the device.
*
* For details on each bus-specific driver, see it's include file,
* i2400m-BUSNAME.h
*
* The bus-generic functionality break up is:
*
* - Firmware upload: fw.c - takes care of uploading firmware to the
* device. bus-specific driver just needs to provides a way to
* execute boot-mode commands and to reset the device.
*
* - RX handling: rx.c - receives data from the bus-specific code and
* feeds it to the network or WiMAX stack or uses it to modify
* the driver state. bus-specific driver only has to receive
* frames and pass them to this module.
*
* - TX handling: tx.c - manages the TX FIFO queue and provides means
* for the bus-specific TX code to pull data from the FIFO
* queue. bus-specific code just pulls frames from this module
* to sends them to the device.
*
* - netdev glue: netdev.c - interface with Linux networking
* stack. Pass around data frames, and configure when the
* device is up and running or shutdown (through ifconfig up /
* down). Bus-generic only.
*
* - control ops: control.c - implements various commands for
* controlling the device. bus-generic only.
*
* - device model glue: driver.c - implements helpers for the
* device-model glue done by the bus-specific layer
* (setup/release the driver resources), turning the device on
* and off, handling the device reboots/resets and a few simple
* WiMAX stack ops.
*
* Code is also broken up in linux-glue / device-glue.
*
* Linux glue contains functions that deal mostly with gluing with the
* rest of the Linux kernel.
*
* Device-glue are functions that deal mostly with the way the device
* does things and talk the device's language.
*
* device-glue code is licensed BSD so other open source OSes can take
* it to implement their drivers.
*
*
* APIs AND HEADER FILES
*
* This bus generic code exports three APIs:
*
* - HDI (host-device interface) definitions common to all busses
* (include/linux/wimax/i2400m.h); these can be also used by user
* space code.
* - internal API for the bus-generic code
* - external API for the bus-specific drivers
*
*
* LIFE CYCLE:
*
* When the bus-specific driver probes, it allocates a network device
* with enough space for it's data structue, that must contain a
* &struct i2400m at the top.
*
* On probe, it needs to fill the i2400m members marked as [fill], as
* well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The
* i2400m driver will only register with the WiMAX and network stacks;
* the only access done to the device is to read the MAC address so we
* can register a network device.
*
* The high-level call flow is:
*
* bus_probe()
* i2400m_setup()
* i2400m->bus_setup()
* boot rom initialization / read mac addr
* network / WiMAX stacks registration
* i2400m_dev_start()
* i2400m->bus_dev_start()
* i2400m_dev_initialize()
*
* The reverse applies for a disconnect() call:
*
* bus_disconnect()
* i2400m_release()
* i2400m_dev_stop()
* i2400m_dev_shutdown()
* i2400m->bus_dev_stop()
* network / WiMAX stack unregistration
* i2400m->bus_release()
*
* At this point, control and data communications are possible.
*
* While the device is up, it might reset. The bus-specific driver has
* to catch that situation and call i2400m_dev_reset_handle() to deal
* with it (reset the internal driver structures and go back to square
* one).
*/
#ifndef __I2400M_H__
#define __I2400M_H__
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/completion.h>
#include <linux/rwsem.h>
#include <linux/atomic.h>
#include "../net-wimax.h"
#include "linux-wimax-i2400m.h"
#include <asm/byteorder.h>
enum {
/* netdev interface */
/*
* Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size
*
* The MTU is 1400 or less
*/
I2400M_MAX_MTU = 1400,
};
/* Misc constants */
enum {
/* Size of the Boot Mode Command buffer */
I2400M_BM_CMD_BUF_SIZE = 16 * 1024,
I2400M_BM_ACK_BUF_SIZE = 256,
};
enum {
/* Maximum number of bus reset can be retried */
I2400M_BUS_RESET_RETRIES = 3,
};
/**
* struct i2400m_poke_table - Hardware poke table for the Intel 2400m
*
* This structure will be used to create a device specific poke table
* to put the device in a consistent state at boot time.
*
* @address: The device address to poke
*
* @data: The data value to poke to the device address
*
*/
struct i2400m_poke_table{
__le32 address;
__le32 data;
};
#define I2400M_FW_POKE(a, d) { \
.address = cpu_to_le32(a), \
.data = cpu_to_le32(d) \
}
/**
* i2400m_reset_type - methods to reset a device
*
* @I2400M_RT_WARM: Reset without device disconnection, device handles
* are kept valid but state is back to power on, with firmware
* re-uploaded.
* @I2400M_RT_COLD: Tell the device to disconnect itself from the bus
* and reconnect. Renders all device handles invalid.
* @I2400M_RT_BUS: Tells the bus to reset the device; last measure
* used when both types above don't work.
*/
enum i2400m_reset_type {
I2400M_RT_WARM, /* first measure */
I2400M_RT_COLD, /* second measure */
I2400M_RT_BUS, /* call in artillery */
};
struct i2400m_reset_ctx;
struct i2400m_roq;
struct i2400m_barker_db;
/**
* struct i2400m - descriptor for an Intel 2400m
*
* Members marked with [fill] must be filled out/initialized before
* calling i2400m_setup().
*
* Note the @bus_setup/@bus_release, @bus_dev_start/@bus_dev_release
* call pairs are very much doing almost the same, and depending on
* the underlying bus, some stuff has to be put in one or the
* other. The idea of setup/release is that they setup the minimal
* amount needed for loading firmware, where us dev_start/stop setup
* the rest needed to do full data/control traffic.
*
* @bus_tx_block_size: [fill] USB imposes a 16 block size, but other
* busses will differ. So we have a tx_blk_size variable that the
* bus layer sets to tell the engine how much of that we need.
*
* @bus_tx_room_min: [fill] Minimum room required while allocating
* TX queue's buffer space for message header. USB requires
* 16 bytes. Refer to bus specific driver code for details.
*
* @bus_pl_size_max: [fill] Maximum payload size.
*
* @bus_setup: [optional fill] Function called by the bus-generic code
* [i2400m_setup()] to setup the basic bus-specific communications
* to the the device needed to load firmware. See LIFE CYCLE above.
*
* NOTE: Doesn't need to upload the firmware, as that is taken
* care of by the bus-generic code.
*
* @bus_release: [optional fill] Function called by the bus-generic
* code [i2400m_release()] to shutdown the basic bus-specific
* communications to the the device needed to load firmware. See
* LIFE CYCLE above.
*
* This function does not need to reset the device, just tear down
* all the host resources created to handle communication with
* the device.
*
* @bus_dev_start: [optional fill] Function called by the bus-generic
* code [i2400m_dev_start()] to do things needed to start the
* device. See LIFE CYCLE above.
*
* NOTE: Doesn't need to upload the firmware, as that is taken
* care of by the bus-generic code.
*
* @bus_dev_stop: [optional fill] Function called by the bus-generic
* code [i2400m_dev_stop()] to do things needed for stopping the
* device. See LIFE CYCLE above.
*
* This function does not need to reset the device, just tear down
* all the host resources created to handle communication with
* the device.
*
* @bus_tx_kick: [fill] Function called by the bus-generic code to let
* the bus-specific code know that there is data available in the
* TX FIFO for transmission to the device.
*
* This function cannot sleep.
*
* @bus_reset: [fill] Function called by the bus-generic code to reset
* the device in in various ways. Doesn't need to wait for the
* reset to finish.
*
* If warm or cold reset fail, this function is expected to do a
* bus-specific reset (eg: USB reset) to get the device to a
* working state (even if it implies device disconecction).
*
* Note the warm reset is used by the firmware uploader to
* reinitialize the device.
*
* IMPORTANT: this is called very early in the device setup
* process, so it cannot rely on common infrastructure being laid
* out.
*
* IMPORTANT: don't call reset on RT_BUS with i2400m->init_mutex
* held, as the .pre/.post reset handlers will deadlock.
*
* @bus_bm_retries: [fill] How many times shall a firmware upload /
* device initialization be retried? Different models of the same
* device might need different values, hence it is set by the
* bus-specific driver. Note this value is used in two places,
* i2400m_fw_dnload() and __i2400m_dev_start(); they won't become
* multiplicative (__i2400m_dev_start() calling N times
* i2400m_fw_dnload() and this trying N times to download the
* firmware), as if __i2400m_dev_start() only retries if the
* firmware crashed while initializing the device (not in a
* general case).
*
* @bus_bm_cmd_send: [fill] Function called to send a boot-mode
* command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This
* is synchronous and has to return 0 if ok or < 0 errno code in
* any error condition.
*
* @bus_bm_wait_for_ack: [fill] Function called to wait for a
* boot-mode notification (that can be a response to a previously
* issued command or an asynchronous one). Will read until all the
* indicated size is read or timeout. Reading more or less data
* than asked for is an error condition. Return 0 if ok, < 0 errno
* code on error.
*
* The caller to this function will check if the response is a
* barker that indicates the device going into reset mode.
*
* @bus_fw_names: [fill] a NULL-terminated array with the names of the
* firmware images to try loading. This is made a list so we can
* support backward compatibility of firmware releases (eg: if we
* can't find the default v1.4, we try v1.3). In general, the name
* should be i2400m-fw-X-VERSION.sbcf, where X is the bus name.
* The list is tried in order and the first one that loads is
* used. The fw loader will set i2400m->fw_name to point to the
* active firmware image.
*
* @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC
* address provided in boot mode is kind of broken and needs to
* be re-read later on.
*
* @bus_bm_pokes_table: [fill/optional] A table of device addresses
* and values that will be poked at device init time to move the
* device to the correct state for the type of boot/firmware being
* used. This table MUST be terminated with (0x000000,
* 0x00000000) or bad things will happen.
*
*
* @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX
* stack. Due to the way a net_device is allocated, we need to
* force this to be the first field so that we can get from
* netdev_priv() the right pointer.
*
* @updown: the device is up and ready for transmitting control and
* data packets. This implies @ready (communication infrastructure
* with the device is ready) and the device's firmware has been
* loaded and the device initialized.
*
* Write to it only inside a i2400m->init_mutex protected area
* followed with a wmb(); rmb() before accesing (unless locked
* inside i2400m->init_mutex). Read access can be loose like that
* [just using rmb()] because the paths that use this also do
* other error checks later on.
*
* @ready: Communication infrastructure with the device is ready, data
* frames can start to be passed around (this is lighter than
* using the WiMAX state for certain hot paths).
*
* Write to it only inside a i2400m->init_mutex protected area
* followed with a wmb(); rmb() before accesing (unless locked
* inside i2400m->init_mutex). Read access can be loose like that
* [just using rmb()] because the paths that use this also do
* other error checks later on.
*
* @rx_reorder: 1 if RX reordering is enabled; this can only be
* set at probe time.
*
* @state: device's state (as reported by it)
*
* @state_wq: waitqueue that is woken up whenever the state changes
*
* @tx_lock: spinlock to protect TX members
*
* @tx_buf: FIFO buffer for TX; we queue data here
*
* @tx_in: FIFO index for incoming data. Note this doesn't wrap around
* and it is always greater than @tx_out.
*
* @tx_out: FIFO index for outgoing data
*
* @tx_msg: current TX message that is active in the FIFO for
* appending payloads.
*
* @tx_sequence: current sequence number for TX messages from the
* device to the host.
*
* @tx_msg_size: size of the current message being transmitted by the
* bus-specific code.
*
* @tx_pl_num: total number of payloads sent
*
* @tx_pl_max: maximum number of payloads sent in a TX message
*
* @tx_pl_min: minimum number of payloads sent in a TX message
*
* @tx_num: number of TX messages sent
*
* @tx_size_acc: number of bytes in all TX messages sent
* (this is different to net_dev's statistics as it also counts
* control messages).
*
* @tx_size_min: smallest TX message sent.
*
* @tx_size_max: biggest TX message sent.
*
* @rx_lock: spinlock to protect RX members and rx_roq_refcount.
*
* @rx_pl_num: total number of payloads received
*
* @rx_pl_max: maximum number of payloads received in a RX message
*
* @rx_pl_min: minimum number of payloads received in a RX message
*
* @rx_num: number of RX messages received
*
* @rx_size_acc: number of bytes in all RX messages received
* (this is different to net_dev's statistics as it also counts
* control messages).
*
* @rx_size_min: smallest RX message received.
*
* @rx_size_max: buggest RX message received.
*
* @rx_roq: RX ReOrder queues. (fw >= v1.4) When packets are received
* out of order, the device will ask the driver to hold certain
* packets until the ones that are received out of order can be
* delivered. Then the driver can release them to the host. See
* drivers/net/i2400m/rx.c for details.
*
* @rx_roq_refcount: refcount rx_roq. This refcounts any access to
* rx_roq thus preventing rx_roq being destroyed when rx_roq
* is being accessed. rx_roq_refcount is protected by rx_lock.
*
* @rx_reports: reports received from the device that couldn't be
* processed because the driver wasn't still ready; when ready,
* they are pulled from here and chewed.
*
* @rx_reports_ws: Work struct used to kick a scan of the RX reports
* list and to process each.
*
* @src_mac_addr: MAC address used to make ethernet packets be coming
* from. This is generated at i2400m_setup() time and used during
* the life cycle of the instance. See i2400m_fake_eth_header().
*
* @init_mutex: Mutex used for serializing the device bringup
* sequence; this way if the device reboots in the middle, we
* don't try to do a bringup again while we are tearing down the
* one that failed.
*
* Can't reuse @msg_mutex because from within the bringup sequence
* we need to send messages to the device and thus use @msg_mutex.
*
* @msg_mutex: mutex used to send control commands to the device (we
* only allow one at a time, per host-device interface design).
*
* @msg_completion: used to wait for an ack to a control command sent
* to the device.
*
* @ack_skb: used to store the actual ack to a control command if the
* reception of the command was successful. Otherwise, a ERR_PTR()
* errno code that indicates what failed with the ack reception.
*
* Only valid after @msg_completion is woken up. Only updateable
* if @msg_completion is armed. Only touched by
* i2400m_msg_to_dev().
*
* Protected by @rx_lock. In theory the command execution flow is
* sequential, but in case the device sends an out-of-phase or
* very delayed response, we need to avoid it trampling current
* execution.
*
* @bm_cmd_buf: boot mode command buffer for composing firmware upload
* commands.
*
* USB can't r/w to stack, vmalloc, etc...as well, we end up
* having to alloc/free a lot to compose commands, so we use these
* for stagging and not having to realloc all the time.
*
* This assumes the code always runs serialized. Only one thread
* can call i2400m_bm_cmd() at the same time.
*
* @bm_ack_buf: boot mode acknoledge buffer for staging reception of
* responses to commands.
*
* See @bm_cmd_buf.
*
* @work_queue: work queue for processing device reports. This
* workqueue cannot be used for processing TX or RX to the device,
* as from it we'll process device reports, which might require
* further communication with the device.
*
* @debugfs_dentry: hookup for debugfs files.
* These have to be in a separate directory, a child of
* (wimax_dev->debugfs_dentry) so they can be removed when the
* module unloads, as we don't keep each dentry.
*
* @fw_name: name of the firmware image that is currently being used.
*
* @fw_version: version of the firmware interface, Major.minor,
* encoded in the high word and low word (major << 16 | minor).
*
* @fw_hdrs: NULL terminated array of pointers to the firmware
* headers. This is only available during firmware load time.
*
* @fw_cached: Used to cache firmware when the system goes to
* suspend/standby/hibernation (as on resume we can't read it). If
* NULL, no firmware was cached, read it. If ~0, you can't read
* any firmware files (the system still didn't come out of suspend
* and failed to cache one), so abort; otherwise, a valid cached
* firmware to be used. Access to this variable is protected by
* the spinlock i2400m->rx_lock.
*
* @barker: barker type that the device uses; this is initialized by
* i2400m_is_boot_barker() the first time it is called. Then it
* won't change during the life cycle of the device and every time
* a boot barker is received, it is just verified for it being the
* same.
*
* @pm_notifier: used to register for PM events
*
* @bus_reset_retries: counter for the number of bus resets attempted for
* this boot. It's not for tracking the number of bus resets during
* the whole driver life cycle (from insmod to rmmod) but for the
* number of dev_start() executed until dev_start() returns a success
* (ie: a good boot means a dev_stop() followed by a successful
* dev_start()). dev_reset_handler() increments this counter whenever
* it is triggering a bus reset. It checks this counter to decide if a
* subsequent bus reset should be retried. dev_reset_handler() retries
* the bus reset until dev_start() succeeds or the counter reaches
* I2400M_BUS_RESET_RETRIES. The counter is cleared to 0 in
* dev_reset_handle() when dev_start() returns a success,
* ie: a successul boot is completed.
*
* @alive: flag to denote if the device *should* be alive. This flag is
* everything like @updown (see doc for @updown) except reflecting
* the device state *we expect* rather than the actual state as denoted
* by @updown. It is set 1 whenever @updown is set 1 in dev_start().
* Then the device is expected to be alive all the time
* (i2400m->alive remains 1) until the driver is removed. Therefore
* all the device reboot events detected can be still handled properly
* by either dev_reset_handle() or .pre_reset/.post_reset as long as
* the driver presents. It is set 0 along with @updown in dev_stop().
*
* @error_recovery: flag to denote if we are ready to take an error recovery.
* 0 for ready to take an error recovery; 1 for not ready. It is
* initialized to 1 while probe() since we don't tend to take any error
* recovery during probe(). It is decremented by 1 whenever dev_start()
* succeeds to indicate we are ready to take error recovery from now on.
* It is checked every time we wanna schedule an error recovery. If an
* error recovery is already in place (error_recovery was set 1), we
* should not schedule another one until the last one is done.
*/
struct i2400m {
struct wimax_dev wimax_dev; /* FIRST! See doc */
unsigned updown:1; /* Network device is up or down */
unsigned boot_mode:1; /* is the device in boot mode? */
unsigned sboot:1; /* signed or unsigned fw boot */
unsigned ready:1; /* Device comm infrastructure ready */
unsigned rx_reorder:1; /* RX reorder is enabled */
u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */
/* typed u8 so /sys/kernel/debug/u8 can tweak */
enum i2400m_system_state state;
wait_queue_head_t state_wq; /* Woken up when on state updates */
size_t bus_tx_block_size;
size_t bus_tx_room_min;
size_t bus_pl_size_max;
unsigned bus_bm_retries;
int (*bus_setup)(struct i2400m *);
int (*bus_dev_start)(struct i2400m *);
void (*bus_dev_stop)(struct i2400m *);
void (*bus_release)(struct i2400m *);
void (*bus_tx_kick)(struct i2400m *);
int (*bus_reset)(struct i2400m *, enum i2400m_reset_type);
ssize_t (*bus_bm_cmd_send)(struct i2400m *,
const struct i2400m_bootrom_header *,
size_t, int flags);
ssize_t (*bus_bm_wait_for_ack)(struct i2400m *,
struct i2400m_bootrom_header *, size_t);
const char **bus_fw_names;
unsigned bus_bm_mac_addr_impaired:1;
const struct i2400m_poke_table *bus_bm_pokes_table;
spinlock_t tx_lock; /* protect TX state */
void *tx_buf;
size_t tx_in, tx_out;
struct i2400m_msg_hdr *tx_msg;
size_t tx_sequence, tx_msg_size;
/* TX stats */
unsigned tx_pl_num, tx_pl_max, tx_pl_min,
tx_num, tx_size_acc, tx_size_min, tx_size_max;
/* RX stuff */
/* protect RX state and rx_roq_refcount */
spinlock_t rx_lock;
unsigned rx_pl_num, rx_pl_max, rx_pl_min,
rx_num, rx_size_acc, rx_size_min, rx_size_max;
struct i2400m_roq *rx_roq; /* access is refcounted */
struct kref rx_roq_refcount; /* refcount access to rx_roq */
u8 src_mac_addr[ETH_HLEN];
struct list_head rx_reports; /* under rx_lock! */
struct work_struct rx_report_ws;
struct mutex msg_mutex; /* serialize command execution */
struct completion msg_completion;
struct sk_buff *ack_skb; /* protected by rx_lock */
void *bm_ack_buf; /* for receiving acks over USB */
void *bm_cmd_buf; /* for issuing commands over USB */
struct workqueue_struct *work_queue;
struct mutex init_mutex; /* protect bringup seq */
struct i2400m_reset_ctx *reset_ctx; /* protected by init_mutex */
struct work_struct wake_tx_ws;
struct sk_buff *wake_tx_skb;
struct work_struct reset_ws;
const char *reset_reason;
struct work_struct recovery_ws;
struct dentry *debugfs_dentry;
const char *fw_name; /* name of the current firmware image */
unsigned long fw_version; /* version of the firmware interface */
const struct i2400m_bcf_hdr **fw_hdrs;
struct i2400m_fw *fw_cached; /* protected by rx_lock */
struct i2400m_barker_db *barker;
struct notifier_block pm_notifier;
/* counting bus reset retries in this boot */
atomic_t bus_reset_retries;
/* if the device is expected to be alive */
unsigned alive;
/* 0 if we are ready for error recovery; 1 if not ready */
atomic_t error_recovery;
};
/*
* Bus-generic internal APIs
* -------------------------
*/
static inline
struct i2400m *wimax_dev_to_i2400m(struct wimax_dev *wimax_dev)
{
return container_of(wimax_dev, struct i2400m, wimax_dev);
}
static inline
struct i2400m *net_dev_to_i2400m(struct net_device *net_dev)
{
return wimax_dev_to_i2400m(netdev_priv(net_dev));
}
/*
* Boot mode support
*/
/**
* i2400m_bm_cmd_flags - flags to i2400m_bm_cmd()
*
* @I2400M_BM_CMD_RAW: send the command block as-is, without doing any
* extra processing for adding CRC.
*/
enum i2400m_bm_cmd_flags {
I2400M_BM_CMD_RAW = 1 << 2,
};
/**
* i2400m_bri - Boot-ROM indicators
*
* Flags for i2400m_bootrom_init() and i2400m_dev_bootstrap() [which
* are passed from things like i2400m_setup()]. Can be combined with
* |.
*
* @I2400M_BRI_SOFT: The device rebooted already and a reboot
* barker received, proceed directly to ack the boot sequence.
* @I2400M_BRI_NO_REBOOT: Do not reboot the device and proceed
* directly to wait for a reboot barker from the device.
* @I2400M_BRI_MAC_REINIT: We need to reinitialize the boot
* rom after reading the MAC address. This is quite a dirty hack,
* if you ask me -- the device requires the bootrom to be
* initialized after reading the MAC address.
*/
enum i2400m_bri {
I2400M_BRI_SOFT = 1 << 1,
I2400M_BRI_NO_REBOOT = 1 << 2,
I2400M_BRI_MAC_REINIT = 1 << 3,
};
void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);
int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri);
int i2400m_read_mac_addr(struct i2400m *);
int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri);
int i2400m_is_boot_barker(struct i2400m *, const void *, size_t);
static inline
int i2400m_is_d2h_barker(const void *buf)
{
const __le32 *barker = buf;
return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER;
}
void i2400m_unknown_barker(struct i2400m *, const void *, size_t);
/* Make/grok boot-rom header commands */
static inline
__le32 i2400m_brh_command(enum i2400m_brh_opcode opcode, unsigned use_checksum,
unsigned direct_access)
{
return cpu_to_le32(
I2400M_BRH_SIGNATURE
| (direct_access ? I2400M_BRH_DIRECT_ACCESS : 0)
| I2400M_BRH_RESPONSE_REQUIRED /* response always required */
| (use_checksum ? I2400M_BRH_USE_CHECKSUM : 0)
| (opcode & I2400M_BRH_OPCODE_MASK));
}
static inline
void i2400m_brh_set_opcode(struct i2400m_bootrom_header *hdr,
enum i2400m_brh_opcode opcode)
{
hdr->command = cpu_to_le32(
(le32_to_cpu(hdr->command) & ~I2400M_BRH_OPCODE_MASK)
| (opcode & I2400M_BRH_OPCODE_MASK));
}
static inline
unsigned i2400m_brh_get_opcode(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_OPCODE_MASK;
}
static inline
unsigned i2400m_brh_get_response(const struct i2400m_bootrom_header *hdr)
{
return (le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_MASK)
>> I2400M_BRH_RESPONSE_SHIFT;
}
static inline
unsigned i2400m_brh_get_use_checksum(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_USE_CHECKSUM;
}
static inline
unsigned i2400m_brh_get_response_required(
const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_REQUIRED;
}
static inline
unsigned i2400m_brh_get_direct_access(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_DIRECT_ACCESS;
}
static inline
unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
{
return (le32_to_cpu(hdr->command) & I2400M_BRH_SIGNATURE_MASK)
>> I2400M_BRH_SIGNATURE_SHIFT;
}
/*
* Driver / device setup and internal functions
*/
void i2400m_init(struct i2400m *);
int i2400m_reset(struct i2400m *, enum i2400m_reset_type);
void i2400m_netdev_setup(struct net_device *net_dev);
int i2400m_sysfs_setup(struct device_driver *);
void i2400m_sysfs_release(struct device_driver *);
int i2400m_tx_setup(struct i2400m *);
void i2400m_wake_tx_work(struct work_struct *);
void i2400m_tx_release(struct i2400m *);
int i2400m_rx_setup(struct i2400m *);
void i2400m_rx_release(struct i2400m *);
void i2400m_fw_cache(struct i2400m *);
void i2400m_fw_uncache(struct i2400m *);
void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *,
int);
void i2400m_net_erx(struct i2400m *, struct sk_buff *, enum i2400m_cs);
void i2400m_net_wake_stop(struct i2400m *);
enum i2400m_pt;
int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt);
#ifdef CONFIG_DEBUG_FS
void i2400m_debugfs_add(struct i2400m *);
void i2400m_debugfs_rm(struct i2400m *);
#else
static inline void i2400m_debugfs_add(struct i2400m *i2400m) {}
static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {}
#endif
/* Initialize/shutdown the device */
int i2400m_dev_initialize(struct i2400m *);
void i2400m_dev_shutdown(struct i2400m *);
extern struct attribute_group i2400m_dev_attr_group;
/* HDI message's payload description handling */
static inline
size_t i2400m_pld_size(const struct i2400m_pld *pld)
{
return I2400M_PLD_SIZE_MASK & le32_to_cpu(pld->val);
}
static inline
enum i2400m_pt i2400m_pld_type(const struct i2400m_pld *pld)
{
return (I2400M_PLD_TYPE_MASK & le32_to_cpu(pld->val))
>> I2400M_PLD_TYPE_SHIFT;
}
static inline
void i2400m_pld_set(struct i2400m_pld *pld, size_t size,
enum i2400m_pt type)
{
pld->val = cpu_to_le32(
((type << I2400M_PLD_TYPE_SHIFT) & I2400M_PLD_TYPE_MASK)
| (size & I2400M_PLD_SIZE_MASK));
}
/*
* API for the bus-specific drivers
* --------------------------------
*/
static inline
struct i2400m *i2400m_get(struct i2400m *i2400m)
{
dev_hold(i2400m->wimax_dev.net_dev);
return i2400m;
}
static inline
void i2400m_put(struct i2400m *i2400m)
{
dev_put(i2400m->wimax_dev.net_dev);
}
int i2400m_dev_reset_handle(struct i2400m *, const char *);
int i2400m_pre_reset(struct i2400m *);
int i2400m_post_reset(struct i2400m *);
void i2400m_error_recovery(struct i2400m *);
/*
* _setup()/_release() are called by the probe/disconnect functions of
* the bus-specific drivers.
*/
int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags);
void i2400m_release(struct i2400m *);
int i2400m_rx(struct i2400m *, struct sk_buff *);
struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
void i2400m_tx_msg_sent(struct i2400m *);
/*
* Utility functions
*/
static inline
struct device *i2400m_dev(struct i2400m *i2400m)
{
return i2400m->wimax_dev.net_dev->dev.parent;
}
int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, char *, size_t);
int i2400m_msg_size_check(struct i2400m *, const struct i2400m_l3l4_hdr *,
size_t);
struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t);
void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int);
void i2400m_report_hook(struct i2400m *, const struct i2400m_l3l4_hdr *,
size_t);
void i2400m_report_hook_work(struct work_struct *);
int i2400m_cmd_enter_powersave(struct i2400m *);
int i2400m_cmd_exit_idle(struct i2400m *);
struct sk_buff *i2400m_get_device_info(struct i2400m *);
int i2400m_firmware_check(struct i2400m *);
int i2400m_set_idle_timeout(struct i2400m *, unsigned);
static inline
struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep)
{
return &iface->cur_altsetting->endpoint[ep].desc;
}
int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, enum wimax_rf_state);
void i2400m_report_tlv_rf_switches_status(struct i2400m *,
const struct i2400m_tlv_rf_switches_status *);
/*
* Helpers for firmware backwards compatibility
*
* As we aim to support at least the firmware version that was
* released with the previous kernel/driver release, some code will be
* conditionally executed depending on the firmware version. On each
* release, the code to support fw releases past the last two ones
* will be purged.
*
* By making it depend on this macros, it is easier to keep it a tab
* on what has to go and what not.
*/
static inline
unsigned i2400m_le_v1_3(struct i2400m *i2400m)
{
/* running fw is lower or v1.3 */
return i2400m->fw_version <= 0x00090001;
}
static inline
unsigned i2400m_ge_v1_4(struct i2400m *i2400m)
{
/* running fw is higher or v1.4 */
return i2400m->fw_version >= 0x00090002;
}
/*
* Do a millisecond-sleep for allowing wireshark to dump all the data
* packets. Used only for debugging.
*/
static inline
void __i2400m_msleep(unsigned ms)
{
#if 1
#else
msleep(ms);
#endif
}
/* module initialization helpers */
int i2400m_barker_db_init(const char *);
void i2400m_barker_db_exit(void);
#endif /* #ifndef __I2400M_H__ */

View File

@ -1,572 +0,0 @@
/*
* Intel Wireless WiMax Connection 2400m
* Host-Device protocol interface definitions
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
*
* This header defines the data structures and constants used to
* communicate with the device.
*
* BOOTMODE/BOOTROM/FIRMWARE UPLOAD PROTOCOL
*
* The firmware upload protocol is quite simple and only requires a
* handful of commands. See drivers/net/wimax/i2400m/fw.c for more
* details.
*
* The BCF data structure is for the firmware file header.
*
*
* THE DATA / CONTROL PROTOCOL
*
* This is the normal protocol spoken with the device once the
* firmware is uploaded. It transports data payloads and control
* messages back and forth.
*
* It consists 'messages' that pack one or more payloads each. The
* format is described in detail in drivers/net/wimax/i2400m/rx.c and
* tx.c.
*
*
* THE L3L4 PROTOCOL
*
* The term L3L4 refers to Layer 3 (the device), Layer 4 (the
* driver/host software).
*
* This is the control protocol used by the host to control the i2400m
* device (scan, connect, disconnect...). This is sent to / received
* as control frames. These frames consist of a header and zero or
* more TLVs with information. We call each control frame a "message".
*
* Each message is composed of:
*
* HEADER
* [TLV0 + PAYLOAD0]
* [TLV1 + PAYLOAD1]
* [...]
* [TLVN + PAYLOADN]
*
* The HEADER is defined by 'struct i2400m_l3l4_hdr'. The payloads are
* defined by a TLV structure (Type Length Value) which is a 'header'
* (struct i2400m_tlv_hdr) and then the payload.
*
* All integers are represented as Little Endian.
*
* - REQUESTS AND EVENTS
*
* The requests can be clasified as follows:
*
* COMMAND: implies a request from the host to the device requesting
* an action being performed. The device will reply with a
* message (with the same type as the command), status and
* no (TLV) payload. Execution of a command might cause
* events (of different type) to be sent later on as
* device's state changes.
*
* GET/SET: similar to COMMAND, but will not cause other
* EVENTs. The reply, in the case of GET, will contain
* TLVs with the requested information.
*
* EVENT: asynchronous messages sent from the device, maybe as a
* consequence of previous COMMANDs but disassociated from
* them.
*
* Only one request might be pending at the same time (ie: don't
* parallelize nor post another GET request before the previous
* COMMAND has been acknowledged with it's corresponding reply by the
* device).
*
* The different requests and their formats are described below:
*
* I2400M_MT_* Message types
* I2400M_MS_* Message status (for replies, events)
* i2400m_tlv_* TLVs
*
* data types are named 'struct i2400m_msg_OPNAME', OPNAME matching the
* operation.
*/
#ifndef __LINUX__WIMAX__I2400M_H__
#define __LINUX__WIMAX__I2400M_H__
#include <linux/types.h>
#include <linux/if_ether.h>
/*
* Host Device Interface (HDI) common to all busses
*/
/* Boot-mode (firmware upload mode) commands */
/* Header for the firmware file */
struct i2400m_bcf_hdr {
__le32 module_type;
__le32 header_len;
__le32 header_version;
__le32 module_id;
__le32 module_vendor;
__le32 date; /* BCD YYYMMDD */
__le32 size; /* in dwords */
__le32 key_size; /* in dwords */
__le32 modulus_size; /* in dwords */
__le32 exponent_size; /* in dwords */
__u8 reserved[88];
} __attribute__ ((packed));
/* Boot mode opcodes */
enum i2400m_brh_opcode {
I2400M_BRH_READ = 1,
I2400M_BRH_WRITE = 2,
I2400M_BRH_JUMP = 3,
I2400M_BRH_SIGNED_JUMP = 8,
I2400M_BRH_HASH_PAYLOAD_ONLY = 9,
};
/* Boot mode command masks and stuff */
enum i2400m_brh {
I2400M_BRH_SIGNATURE = 0xcbbc0000,
I2400M_BRH_SIGNATURE_MASK = 0xffff0000,
I2400M_BRH_SIGNATURE_SHIFT = 16,
I2400M_BRH_OPCODE_MASK = 0x0000000f,
I2400M_BRH_RESPONSE_MASK = 0x000000f0,
I2400M_BRH_RESPONSE_SHIFT = 4,
I2400M_BRH_DIRECT_ACCESS = 0x00000400,
I2400M_BRH_RESPONSE_REQUIRED = 0x00000200,
I2400M_BRH_USE_CHECKSUM = 0x00000100,
};
/**
* i2400m_bootrom_header - Header for a boot-mode command
*
* @cmd: the above command descriptor
* @target_addr: where on the device memory should the action be performed.
* @data_size: for read/write, amount of data to be read/written
* @block_checksum: checksum value (if applicable)
* @payload: the beginning of data attached to this header
*/
struct i2400m_bootrom_header {
__le32 command; /* Compose with enum i2400_brh */
__le32 target_addr;
__le32 data_size;
__le32 block_checksum;
char payload[0];
} __attribute__ ((packed));
/*
* Data / control protocol
*/
/* Packet types for the host-device interface */
enum i2400m_pt {
I2400M_PT_DATA = 0,
I2400M_PT_CTRL,
I2400M_PT_TRACE, /* For device debug */
I2400M_PT_RESET_WARM, /* device reset */
I2400M_PT_RESET_COLD, /* USB[transport] reset, like reconnect */
I2400M_PT_EDATA, /* Extended RX data */
I2400M_PT_ILLEGAL
};
/*
* Payload for a data packet
*
* This is prefixed to each and every outgoing DATA type.
*/
struct i2400m_pl_data_hdr {
__le32 reserved;
} __attribute__((packed));
/*
* Payload for an extended data packet
*
* New in fw v1.4
*
* @reorder: if this payload has to be reorder or not (and how)
* @cs: the type of data in the packet, as defined per (802.16e
* T11.13.19.1). Currently only 2 (IPv4 packet) supported.
*
* This is prefixed to each and every INCOMING DATA packet.
*/
struct i2400m_pl_edata_hdr {
__le32 reorder; /* bits defined in i2400m_ro */
__u8 cs;
__u8 reserved[11];
} __attribute__((packed));
enum i2400m_cs {
I2400M_CS_IPV4_0 = 0,
I2400M_CS_IPV4 = 2,
};
enum i2400m_ro {
I2400M_RO_NEEDED = 0x01,
I2400M_RO_TYPE = 0x03,
I2400M_RO_TYPE_SHIFT = 1,
I2400M_RO_CIN = 0x0f,
I2400M_RO_CIN_SHIFT = 4,
I2400M_RO_FBN = 0x07ff,
I2400M_RO_FBN_SHIFT = 8,
I2400M_RO_SN = 0x07ff,
I2400M_RO_SN_SHIFT = 21,
};
enum i2400m_ro_type {
I2400M_RO_TYPE_RESET = 0,
I2400M_RO_TYPE_PACKET,
I2400M_RO_TYPE_WS,
I2400M_RO_TYPE_PACKET_WS,
};
/* Misc constants */
enum {
I2400M_PL_ALIGN = 16, /* Payload data size alignment */
I2400M_PL_SIZE_MAX = 0x3EFF,
I2400M_MAX_PLS_IN_MSG = 60,
/* protocol barkers: sync sequences; for notifications they
* are sent in groups of four. */
I2400M_H2D_PREVIEW_BARKER = 0xcafe900d,
I2400M_COLD_RESET_BARKER = 0xc01dc01d,
I2400M_WARM_RESET_BARKER = 0x50f750f7,
I2400M_NBOOT_BARKER = 0xdeadbeef,
I2400M_SBOOT_BARKER = 0x0ff1c1a1,
I2400M_SBOOT_BARKER_6050 = 0x80000001,
I2400M_ACK_BARKER = 0xfeedbabe,
I2400M_D2H_MSG_BARKER = 0xbeefbabe,
};
/*
* Hardware payload descriptor
*
* Bitfields encoded in a struct to enforce typing semantics.
*
* Look in rx.c and tx.c for a full description of the format.
*/
struct i2400m_pld {
__le32 val;
} __attribute__ ((packed));
#define I2400M_PLD_SIZE_MASK 0x00003fff
#define I2400M_PLD_TYPE_SHIFT 16
#define I2400M_PLD_TYPE_MASK 0x000f0000
/*
* Header for a TX message or RX message
*
* @barker: preamble
* @size: used for management of the FIFO queue buffer; before
* sending, this is converted to be a real preamble. This
* indicates the real size of the TX message that starts at this
* point. If the highest bit is set, then this message is to be
* skipped.
* @sequence: sequence number of this message
* @offset: offset where the message itself starts -- see the comments
* in the file header about message header and payload descriptor
* alignment.
* @num_pls: number of payloads in this message
* @padding: amount of padding bytes at the end of the message to make
* it be of block-size aligned
*
* Look in rx.c and tx.c for a full description of the format.
*/
struct i2400m_msg_hdr {
union {
__le32 barker;
__u32 size; /* same size type as barker!! */
};
union {
__le32 sequence;
__u32 offset; /* same size type as barker!! */
};
__le16 num_pls;
__le16 rsv1;
__le16 padding;
__le16 rsv2;
struct i2400m_pld pld[0];
} __attribute__ ((packed));
/*
* L3/L4 control protocol
*/
enum {
/* Interface version */
I2400M_L3L4_VERSION = 0x0100,
};
/* Message types */
enum i2400m_mt {
I2400M_MT_RESERVED = 0x0000,
I2400M_MT_INVALID = 0xffff,
I2400M_MT_REPORT_MASK = 0x8000,
I2400M_MT_GET_SCAN_RESULT = 0x4202,
I2400M_MT_SET_SCAN_PARAM = 0x4402,
I2400M_MT_CMD_RF_CONTROL = 0x4602,
I2400M_MT_CMD_SCAN = 0x4603,
I2400M_MT_CMD_CONNECT = 0x4604,
I2400M_MT_CMD_DISCONNECT = 0x4605,
I2400M_MT_CMD_EXIT_IDLE = 0x4606,
I2400M_MT_GET_LM_VERSION = 0x5201,
I2400M_MT_GET_DEVICE_INFO = 0x5202,
I2400M_MT_GET_LINK_STATUS = 0x5203,
I2400M_MT_GET_STATISTICS = 0x5204,
I2400M_MT_GET_STATE = 0x5205,
I2400M_MT_GET_MEDIA_STATUS = 0x5206,
I2400M_MT_SET_INIT_CONFIG = 0x5404,
I2400M_MT_CMD_INIT = 0x5601,
I2400M_MT_CMD_TERMINATE = 0x5602,
I2400M_MT_CMD_MODE_OF_OP = 0x5603,
I2400M_MT_CMD_RESET_DEVICE = 0x5604,
I2400M_MT_CMD_MONITOR_CONTROL = 0x5605,
I2400M_MT_CMD_ENTER_POWERSAVE = 0x5606,
I2400M_MT_GET_TLS_OPERATION_RESULT = 0x6201,
I2400M_MT_SET_EAP_SUCCESS = 0x6402,
I2400M_MT_SET_EAP_FAIL = 0x6403,
I2400M_MT_SET_EAP_KEY = 0x6404,
I2400M_MT_CMD_SEND_EAP_RESPONSE = 0x6602,
I2400M_MT_REPORT_SCAN_RESULT = 0xc002,
I2400M_MT_REPORT_STATE = 0xd002,
I2400M_MT_REPORT_POWERSAVE_READY = 0xd005,
I2400M_MT_REPORT_EAP_REQUEST = 0xe002,
I2400M_MT_REPORT_EAP_RESTART = 0xe003,
I2400M_MT_REPORT_ALT_ACCEPT = 0xe004,
I2400M_MT_REPORT_KEY_REQUEST = 0xe005,
};
/*
* Message Ack Status codes
*
* When a message is replied-to, this status is reported.
*/
enum i2400m_ms {
I2400M_MS_DONE_OK = 0,
I2400M_MS_DONE_IN_PROGRESS = 1,
I2400M_MS_INVALID_OP = 2,
I2400M_MS_BAD_STATE = 3,
I2400M_MS_ILLEGAL_VALUE = 4,
I2400M_MS_MISSING_PARAMS = 5,
I2400M_MS_VERSION_ERROR = 6,
I2400M_MS_ACCESSIBILITY_ERROR = 7,
I2400M_MS_BUSY = 8,
I2400M_MS_CORRUPTED_TLV = 9,
I2400M_MS_UNINITIALIZED = 10,
I2400M_MS_UNKNOWN_ERROR = 11,
I2400M_MS_PRODUCTION_ERROR = 12,
I2400M_MS_NO_RF = 13,
I2400M_MS_NOT_READY_FOR_POWERSAVE = 14,
I2400M_MS_THERMAL_CRITICAL = 15,
I2400M_MS_MAX
};
/**
* i2400m_tlv - enumeration of the different types of TLVs
*
* TLVs stand for type-length-value and are the header for a payload
* composed of almost anything. Each payload has a type assigned
* and a length.
*/
enum i2400m_tlv {
I2400M_TLV_L4_MESSAGE_VERSIONS = 129,
I2400M_TLV_SYSTEM_STATE = 141,
I2400M_TLV_MEDIA_STATUS = 161,
I2400M_TLV_RF_OPERATION = 162,
I2400M_TLV_RF_STATUS = 163,
I2400M_TLV_DEVICE_RESET_TYPE = 132,
I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601,
I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611,
I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614,
I2400M_TLV_CONFIG_DL_HOST_REORDER = 615,
};
struct i2400m_tlv_hdr {
__le16 type;
__le16 length; /* payload's */
__u8 pl[0];
} __attribute__((packed));
struct i2400m_l3l4_hdr {
__le16 type;
__le16 length; /* payload's */
__le16 version;
__le16 resv1;
__le16 status;
__le16 resv2;
struct i2400m_tlv_hdr pl[0];
} __attribute__((packed));
/**
* i2400m_system_state - different states of the device
*/
enum i2400m_system_state {
I2400M_SS_UNINITIALIZED = 1,
I2400M_SS_INIT,
I2400M_SS_READY,
I2400M_SS_SCAN,
I2400M_SS_STANDBY,
I2400M_SS_CONNECTING,
I2400M_SS_WIMAX_CONNECTED,
I2400M_SS_DATA_PATH_CONNECTED,
I2400M_SS_IDLE,
I2400M_SS_DISCONNECTING,
I2400M_SS_OUT_OF_ZONE,
I2400M_SS_SLEEPACTIVE,
I2400M_SS_PRODUCTION,
I2400M_SS_CONFIG,
I2400M_SS_RF_OFF,
I2400M_SS_RF_SHUTDOWN,
I2400M_SS_DEVICE_DISCONNECT,
I2400M_SS_MAX,
};
/**
* i2400m_tlv_system_state - report on the state of the system
*
* @state: see enum i2400m_system_state
*/
struct i2400m_tlv_system_state {
struct i2400m_tlv_hdr hdr;
__le32 state;
} __attribute__((packed));
struct i2400m_tlv_l4_message_versions {
struct i2400m_tlv_hdr hdr;
__le16 major;
__le16 minor;
__le16 branch;
__le16 reserved;
} __attribute__((packed));
struct i2400m_tlv_detailed_device_info {
struct i2400m_tlv_hdr hdr;
__u8 reserved1[400];
__u8 mac_address[ETH_ALEN];
__u8 reserved2[2];
} __attribute__((packed));
enum i2400m_rf_switch_status {
I2400M_RF_SWITCH_ON = 1,
I2400M_RF_SWITCH_OFF = 2,
};
struct i2400m_tlv_rf_switches_status {
struct i2400m_tlv_hdr hdr;
__u8 sw_rf_switch; /* 1 ON, 2 OFF */
__u8 hw_rf_switch; /* 1 ON, 2 OFF */
__u8 reserved[2];
} __attribute__((packed));
enum {
i2400m_rf_operation_on = 1,
i2400m_rf_operation_off = 2
};
struct i2400m_tlv_rf_operation {
struct i2400m_tlv_hdr hdr;
__le32 status; /* 1 ON, 2 OFF */
} __attribute__((packed));
enum i2400m_tlv_reset_type {
I2400M_RESET_TYPE_COLD = 1,
I2400M_RESET_TYPE_WARM
};
struct i2400m_tlv_device_reset_type {
struct i2400m_tlv_hdr hdr;
__le32 reset_type;
} __attribute__((packed));
struct i2400m_tlv_config_idle_parameters {
struct i2400m_tlv_hdr hdr;
__le32 idle_timeout; /* 100 to 300000 ms [5min], 100 increments
* 0 disabled */
__le32 idle_paging_interval; /* frames */
} __attribute__((packed));
enum i2400m_media_status {
I2400M_MEDIA_STATUS_LINK_UP = 1,
I2400M_MEDIA_STATUS_LINK_DOWN,
I2400M_MEDIA_STATUS_LINK_RENEW,
};
struct i2400m_tlv_media_status {
struct i2400m_tlv_hdr hdr;
__le32 media_status;
} __attribute__((packed));
/* New in v1.4 */
struct i2400m_tlv_config_idle_timeout {
struct i2400m_tlv_hdr hdr;
__le32 timeout; /* 100 to 300000 ms [5min], 100 increments
* 0 disabled */
} __attribute__((packed));
/* New in v1.4 -- for backward compat, will be removed */
struct i2400m_tlv_config_d2h_data_format {
struct i2400m_tlv_hdr hdr;
__u8 format; /* 0 old format, 1 enhanced */
__u8 reserved[3];
} __attribute__((packed));
/* New in v1.4 */
struct i2400m_tlv_config_dl_host_reorder {
struct i2400m_tlv_hdr hdr;
__u8 reorder; /* 0 disabled, 1 enabled */
__u8 reserved[3];
} __attribute__((packed));
#endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */

View File

@ -1,603 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless WiMAX Connection 2400m
* Glue with the networking stack
*
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* This implements an ethernet device for the i2400m.
*
* We fake being an ethernet device to simplify the support from user
* space and from the other side. The world is (sadly) configured to
* take in only Ethernet devices...
*
* Because of this, when using firmwares <= v1.3, there is an
* copy-each-rxed-packet overhead on the RX path. Each IP packet has
* to be reallocated to add an ethernet header (as there is no space
* in what we get from the device). This is a known drawback and
* firmwares >= 1.4 add header space that can be used to insert the
* ethernet header without having to reallocate and copy.
*
* TX error handling is tricky; because we have to FIFO/queue the
* buffers for transmission (as the hardware likes it aggregated), we
* just give the skb to the TX subsystem and by the time it is
* transmitted, we have long forgotten about it. So we just don't care
* too much about it.
*
* Note that when the device is in idle mode with the basestation, we
* need to negotiate coming back up online. That involves negotiation
* and possible user space interaction. Thus, we defer to a workqueue
* to do all that. By default, we only queue a single packet and drop
* the rest, as potentially the time to go back from idle to normal is
* long.
*
* ROADMAP
*
* i2400m_open Called on ifconfig up
* i2400m_stop Called on ifconfig down
*
* i2400m_hard_start_xmit Called by the network stack to send a packet
* i2400m_net_wake_tx Wake up device from basestation-IDLE & TX
* i2400m_wake_tx_work
* i2400m_cmd_exit_idle
* i2400m_tx
* i2400m_net_tx TX a data frame
* i2400m_tx
*
* i2400m_change_mtu Called on ifconfig mtu XXX
*
* i2400m_tx_timeout Called when the device times out
*
* i2400m_net_rx Called by the RX code when a data frame is
* available (firmware <= 1.3)
* i2400m_net_erx Called by the RX code when a data frame is
* available (firmware >= 1.4).
* i2400m_netdev_setup Called to setup all the netdev stuff from
* alloc_netdev.
*/
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/export.h>
#include "i2400m.h"
#define D_SUBMODULE netdev
#include "debug-levels.h"
enum {
/* netdev interface */
/* 20 secs? yep, this is the maximum timeout that the device
* might take to get out of IDLE / negotiate it with the base
* station. We add 1sec for good measure. */
I2400M_TX_TIMEOUT = 21 * HZ,
/*
* Experimentation has determined that, 20 to be a good value
* for minimizing the jitter in the throughput.
*/
I2400M_TX_QLEN = 20,
};
static
int i2400m_open(struct net_device *net_dev)
{
int result;
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
/* Make sure we wait until init is complete... */
mutex_lock(&i2400m->init_mutex);
if (i2400m->updown)
result = 0;
else
result = -EBUSY;
mutex_unlock(&i2400m->init_mutex);
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
net_dev, i2400m, result);
return result;
}
static
int i2400m_stop(struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
i2400m_net_wake_stop(i2400m);
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m);
return 0;
}
/*
* Wake up the device and transmit a held SKB, then restart the net queue
*
* When the device goes into basestation-idle mode, we need to tell it
* to exit that mode; it will negotiate with the base station, user
* space may have to intervene to rehandshake crypto and then tell us
* when it is ready to transmit the packet we have "queued". Still we
* need to give it sometime after it reports being ok.
*
* On error, there is not much we can do. If the error was on TX, we
* still wake the queue up to see if the next packet will be luckier.
*
* If _cmd_exit_idle() fails...well, it could be many things; most
* commonly it is that something else took the device out of IDLE mode
* (for example, the base station). In that case we get an -EILSEQ and
* we are just going to ignore that one. If the device is back to
* connected, then fine -- if it is someother state, the packet will
* be dropped anyway.
*/
void i2400m_wake_tx_work(struct work_struct *ws)
{
int result;
struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws);
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb;
unsigned long flags;
spin_lock_irqsave(&i2400m->tx_lock, flags);
skb = i2400m->wake_tx_skb;
i2400m->wake_tx_skb = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnstart(3, dev, "(ws %p i2400m %p skb %p)\n", ws, i2400m, skb);
result = -EINVAL;
if (skb == NULL) {
dev_err(dev, "WAKE&TX: skb disappeared!\n");
goto out_put;
}
/* If we have, somehow, lost the connection after this was
* queued, don't do anything; this might be the device got
* reset or just disconnected. */
if (unlikely(!netif_carrier_ok(net_dev)))
goto out_kfree;
result = i2400m_cmd_exit_idle(i2400m);
if (result == -EILSEQ)
result = 0;
if (result < 0) {
dev_err(dev, "WAKE&TX: device didn't get out of idle: "
"%d - resetting\n", result);
i2400m_reset(i2400m, I2400M_RT_BUS);
goto error;
}
result = wait_event_timeout(i2400m->state_wq,
i2400m->state != I2400M_SS_IDLE,
net_dev->watchdog_timeo - HZ/2);
if (result == 0)
result = -ETIMEDOUT;
if (result < 0) {
dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: "
"%d - resetting\n", result);
i2400m_reset(i2400m, I2400M_RT_BUS);
goto error;
}
msleep(20); /* device still needs some time or it drops it */
result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
error:
netif_wake_queue(net_dev);
out_kfree:
kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */
out_put:
i2400m_put(i2400m);
d_fnend(3, dev, "(ws %p i2400m %p skb %p) = void [%d]\n",
ws, i2400m, skb, result);
}
/*
* Prepare the data payload TX header
*
* The i2400m expects a 4 byte header in front of a data packet.
*
* Because we pretend to be an ethernet device, this packet comes with
* an ethernet header. Pull it and push our header.
*/
static
void i2400m_tx_prep_header(struct sk_buff *skb)
{
struct i2400m_pl_data_hdr *pl_hdr;
skb_pull(skb, ETH_HLEN);
pl_hdr = skb_push(skb, sizeof(*pl_hdr));
pl_hdr->reserved = 0;
}
/*
* Cleanup resources acquired during i2400m_net_wake_tx()
*
* This is called by __i2400m_dev_stop and means we have to make sure
* the workqueue is flushed from any pending work.
*/
void i2400m_net_wake_stop(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *wake_tx_skb;
unsigned long flags;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
/*
* See i2400m_hard_start_xmit(), references are taken there and
* here we release them if the packet was still pending.
*/
cancel_work_sync(&i2400m->wake_tx_ws);
spin_lock_irqsave(&i2400m->tx_lock, flags);
wake_tx_skb = i2400m->wake_tx_skb;
i2400m->wake_tx_skb = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
if (wake_tx_skb) {
i2400m_put(i2400m);
kfree_skb(wake_tx_skb);
}
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
/*
* TX an skb to an idle device
*
* When the device is in basestation-idle mode, we need to wake it up
* and then TX. So we queue a work_struct for doing so.
*
* We need to get an extra ref for the skb (so it is not dropped), as
* well as be careful not to queue more than one request (won't help
* at all). If more than one request comes or there are errors, we
* just drop the packets (see i2400m_hard_start_xmit()).
*/
static
int i2400m_net_wake_tx(struct i2400m *i2400m, struct net_device *net_dev,
struct sk_buff *skb)
{
int result;
struct device *dev = i2400m_dev(i2400m);
unsigned long flags;
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
if (net_ratelimit()) {
d_printf(3, dev, "WAKE&NETTX: "
"skb %p sending %d bytes to radio\n",
skb, skb->len);
d_dump(4, dev, skb->data, skb->len);
}
/* We hold a ref count for i2400m and skb, so when
* stopping() the device, we need to cancel that work
* and if pending, release those resources. */
result = 0;
spin_lock_irqsave(&i2400m->tx_lock, flags);
if (!i2400m->wake_tx_skb) {
netif_stop_queue(net_dev);
i2400m_get(i2400m);
i2400m->wake_tx_skb = skb_get(skb); /* transfer ref count */
i2400m_tx_prep_header(skb);
result = schedule_work(&i2400m->wake_tx_ws);
WARN_ON(result == 0);
}
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
if (result == 0) {
/* Yes, this happens even if we stopped the
* queue -- blame the queue disciplines that
* queue without looking -- I guess there is a reason
* for that. */
if (net_ratelimit())
d_printf(1, dev, "NETTX: device exiting idle, "
"dropping skb %p, queue running %d\n",
skb, netif_queue_stopped(net_dev));
result = -EBUSY;
}
d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result);
return result;
}
/*
* Transmit a packet to the base station on behalf of the network stack.
*
* Returns: 0 if ok, < 0 errno code on error.
*
* We need to pull the ethernet header and add the hardware header,
* which is currently set to all zeroes and reserved.
*/
static
int i2400m_net_tx(struct i2400m *i2400m, struct net_device *net_dev,
struct sk_buff *skb)
{
int result;
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p net_dev %p skb %p)\n",
i2400m, net_dev, skb);
/* FIXME: check eth hdr, only IPv4 is routed by the device as of now */
netif_trans_update(net_dev);
i2400m_tx_prep_header(skb);
d_printf(3, dev, "NETTX: skb %p sending %d bytes to radio\n",
skb, skb->len);
d_dump(4, dev, skb->data, skb->len);
result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
d_fnend(3, dev, "(i2400m %p net_dev %p skb %p) = %d\n",
i2400m, net_dev, skb, result);
return result;
}
/*
* Transmit a packet to the base station on behalf of the network stack
*
*
* Returns: NETDEV_TX_OK (always, even in case of error)
*
* In case of error, we just drop it. Reasons:
*
* - we add a hw header to each skb, and if the network stack
* retries, we have no way to know if that skb has it or not.
*
* - network protocols have their own drop-recovery mechanisms
*
* - there is not much else we can do
*
* If the device is idle, we need to wake it up; that is an operation
* that will sleep. See i2400m_net_wake_tx() for details.
*/
static
netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb,
struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
int result = -1;
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
if (skb_cow_head(skb, 0))
goto drop;
if (i2400m->state == I2400M_SS_IDLE)
result = i2400m_net_wake_tx(i2400m, net_dev, skb);
else
result = i2400m_net_tx(i2400m, net_dev, skb);
if (result < 0) {
drop:
net_dev->stats.tx_dropped++;
} else {
net_dev->stats.tx_packets++;
net_dev->stats.tx_bytes += skb->len;
}
dev_kfree_skb(skb);
d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result);
return NETDEV_TX_OK;
}
static
void i2400m_tx_timeout(struct net_device *net_dev, unsigned int txqueue)
{
/*
* We might want to kick the device
*
* There is not much we can do though, as the device requires
* that we send the data aggregated. By the time we receive
* this, there might be data pending to be sent or not...
*/
net_dev->stats.tx_errors++;
}
/*
* Create a fake ethernet header
*
* For emulating an ethernet device, every received IP header has to
* be prefixed with an ethernet header. Fake it with the given
* protocol.
*/
static
void i2400m_rx_fake_eth_header(struct net_device *net_dev,
void *_eth_hdr, __be16 protocol)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct ethhdr *eth_hdr = _eth_hdr;
memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest));
memcpy(eth_hdr->h_source, i2400m->src_mac_addr,
sizeof(eth_hdr->h_source));
eth_hdr->h_proto = protocol;
}
/*
* i2400m_net_rx - pass a network packet to the stack
*
* @i2400m: device instance
* @skb_rx: the skb where the buffer pointed to by @buf is
* @i: 1 if payload is the only one
* @buf: pointer to the buffer containing the data
* @len: buffer's length
*
* This is only used now for the v1.3 firmware. It will be deprecated
* in >= 2.6.31.
*
* Note that due to firmware limitations, we don't have space to add
* an ethernet header, so we need to copy each packet. Firmware
* versions >= v1.4 fix this [see i2400m_net_erx()].
*
* We just clone the skb and set it up so that it's skb->data pointer
* points to "buf" and it's length.
*
* Note that if the payload is the last (or the only one) in a
* multi-payload message, we don't clone the SKB but just reuse it.
*
* This function is normally run from a thread context. However, we
* still use netif_rx() instead of netif_receive_skb() as was
* recommended in the mailing list. Reason is in some stress tests
* when sending/receiving a lot of data we seem to hit a softlock in
* the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using
* netif_rx() took care of the issue.
*
* This is, of course, still open to do more research on why running
* with netif_receive_skb() hits this softlock. FIXME.
*
* FIXME: currently we don't do any efforts at distinguishing if what
* we got was an IPv4 or IPv6 header, to setup the protocol field
* correctly.
*/
void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx,
unsigned i, const void *buf, int buf_len)
{
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb;
d_fnstart(2, dev, "(i2400m %p buf %p buf_len %d)\n",
i2400m, buf, buf_len);
if (i) {
skb = skb_get(skb_rx);
d_printf(2, dev, "RX: reusing first payload skb %p\n", skb);
skb_pull(skb, buf - (void *) skb->data);
skb_trim(skb, (void *) skb_end_pointer(skb) - buf);
} else {
/* Yes, this is bad -- a lot of overhead -- see
* comments at the top of the file */
skb = __netdev_alloc_skb(net_dev, buf_len, GFP_KERNEL);
if (skb == NULL) {
dev_err(dev, "NETRX: no memory to realloc skb\n");
net_dev->stats.rx_dropped++;
goto error_skb_realloc;
}
skb_put_data(skb, buf, buf_len);
}
i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev,
skb->data - ETH_HLEN,
cpu_to_be16(ETH_P_IP));
skb_set_mac_header(skb, -ETH_HLEN);
skb->dev = i2400m->wimax_dev.net_dev;
skb->protocol = htons(ETH_P_IP);
net_dev->stats.rx_packets++;
net_dev->stats.rx_bytes += buf_len;
d_printf(3, dev, "NETRX: receiving %d bytes to network stack\n",
buf_len);
d_dump(4, dev, buf, buf_len);
netif_rx_ni(skb); /* see notes in function header */
error_skb_realloc:
d_fnend(2, dev, "(i2400m %p buf %p buf_len %d) = void\n",
i2400m, buf, buf_len);
}
/*
* i2400m_net_erx - pass a network packet to the stack (extended version)
*
* @i2400m: device descriptor
* @skb: the skb where the packet is - the skb should be set to point
* at the IP packet; this function will add ethernet headers if
* needed.
* @cs: packet type
*
* This is only used now for firmware >= v1.4. Note it is quite
* similar to i2400m_net_rx() (used only for v1.3 firmware).
*
* This function is normally run from a thread context. However, we
* still use netif_rx() instead of netif_receive_skb() as was
* recommended in the mailing list. Reason is in some stress tests
* when sending/receiving a lot of data we seem to hit a softlock in
* the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using
* netif_rx() took care of the issue.
*
* This is, of course, still open to do more research on why running
* with netif_receive_skb() hits this softlock. FIXME.
*/
void i2400m_net_erx(struct i2400m *i2400m, struct sk_buff *skb,
enum i2400m_cs cs)
{
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = i2400m_dev(i2400m);
d_fnstart(2, dev, "(i2400m %p skb %p [%u] cs %d)\n",
i2400m, skb, skb->len, cs);
switch (cs) {
case I2400M_CS_IPV4_0:
case I2400M_CS_IPV4:
i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev,
skb->data - ETH_HLEN,
cpu_to_be16(ETH_P_IP));
skb_set_mac_header(skb, -ETH_HLEN);
skb->dev = i2400m->wimax_dev.net_dev;
skb->protocol = htons(ETH_P_IP);
net_dev->stats.rx_packets++;
net_dev->stats.rx_bytes += skb->len;
break;
default:
dev_err(dev, "ERX: BUG? CS type %u unsupported\n", cs);
goto error;
}
d_printf(3, dev, "ERX: receiving %d bytes to the network stack\n",
skb->len);
d_dump(4, dev, skb->data, skb->len);
netif_rx_ni(skb); /* see notes in function header */
error:
d_fnend(2, dev, "(i2400m %p skb %p [%u] cs %d) = void\n",
i2400m, skb, skb->len, cs);
}
static const struct net_device_ops i2400m_netdev_ops = {
.ndo_open = i2400m_open,
.ndo_stop = i2400m_stop,
.ndo_start_xmit = i2400m_hard_start_xmit,
.ndo_tx_timeout = i2400m_tx_timeout,
};
static void i2400m_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strscpy(info->fw_version, i2400m->fw_name ? : "",
sizeof(info->fw_version));
if (net_dev->dev.parent)
strscpy(info->bus_info, dev_name(net_dev->dev.parent),
sizeof(info->bus_info));
}
static const struct ethtool_ops i2400m_ethtool_ops = {
.get_drvinfo = i2400m_get_drvinfo,
.get_link = ethtool_op_get_link,
};
/*
* i2400m_netdev_setup - Setup setup @net_dev's i2400m private data
*
* Called by alloc_netdev()
*/
void i2400m_netdev_setup(struct net_device *net_dev)
{
d_fnstart(3, NULL, "(net_dev %p)\n", net_dev);
ether_setup(net_dev);
net_dev->mtu = I2400M_MAX_MTU;
net_dev->min_mtu = 0;
net_dev->max_mtu = I2400M_MAX_MTU;
net_dev->tx_queue_len = I2400M_TX_QLEN;
net_dev->features =
NETIF_F_VLAN_CHALLENGED
| NETIF_F_HIGHDMA;
net_dev->flags =
IFF_NOARP /* i2400m is apure IP device */
& (~IFF_BROADCAST /* i2400m is P2P */
& ~IFF_MULTICAST);
net_dev->watchdog_timeo = I2400M_TX_TIMEOUT;
net_dev->netdev_ops = &i2400m_netdev_ops;
net_dev->ethtool_ops = &i2400m_ethtool_ops;
d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev);
}
EXPORT_SYMBOL_GPL(i2400m_netdev_setup);

View File

@ -1,196 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless WiMAX Connection 2400m
* Implement backend for the WiMAX stack rfkill support
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* The WiMAX kernel stack integrates into RF-Kill and keeps the
* switches's status. We just need to:
*
* - report changes in the HW RF Kill switch [with
* wimax_rfkill_{sw,hw}_report(), which happens when we detect those
* indications coming through hardware reports]. We also do it on
* initialization to let the stack know the initial HW state.
*
* - implement indications from the stack to change the SW RF Kill
* switch (coming from sysfs, the wimax stack or user space).
*/
#include "i2400m.h"
#include "linux-wimax-i2400m.h"
#include <linux/slab.h>
#define D_SUBMODULE rfkill
#include "debug-levels.h"
/*
* Return true if the i2400m radio is in the requested wimax_rf_state state
*
*/
static
int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state)
{
if (state == WIMAX_RF_OFF)
return i2400m->state == I2400M_SS_RF_OFF
|| i2400m->state == I2400M_SS_RF_SHUTDOWN;
else if (state == WIMAX_RF_ON)
/* state == WIMAX_RF_ON */
return i2400m->state != I2400M_SS_RF_OFF
&& i2400m->state != I2400M_SS_RF_SHUTDOWN;
else {
BUG();
return -EINVAL; /* shut gcc warnings on certain arches */
}
}
/*
* WiMAX stack operation: implement SW RFKill toggling
*
* @wimax_dev: device descriptor
* @skb: skb where the message has been received; skb->data is
* expected to point to the message payload.
* @genl_info: passed by the generic netlink layer
*
* Generic Netlink will call this function when a message is sent from
* userspace to change the software RF-Kill switch status.
*
* This function will set the device's software RF-Kill switch state to
* match what is requested.
*
* NOTE: the i2400m has a strict state machine; we can only set the
* RF-Kill switch when it is on, the HW RF-Kill is on and the
* device is initialized. So we ignore errors steaming from not
* being in the right state (-EILSEQ).
*/
int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev,
enum wimax_rf_state state)
{
int result;
struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev);
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *ack_skb;
struct {
struct i2400m_l3l4_hdr hdr;
struct i2400m_tlv_rf_operation sw_rf;
} __packed *cmd;
char strerr[32];
d_fnstart(4, dev, "(wimax_dev %p state %d)\n", wimax_dev, state);
result = -ENOMEM;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
goto error_alloc;
cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_RF_CONTROL);
cmd->hdr.length = cpu_to_le16(sizeof(cmd->sw_rf));
cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION);
cmd->sw_rf.hdr.type = cpu_to_le16(I2400M_TLV_RF_OPERATION);
cmd->sw_rf.hdr.length = cpu_to_le16(sizeof(cmd->sw_rf.status));
switch (state) {
case WIMAX_RF_OFF: /* RFKILL ON, radio OFF */
cmd->sw_rf.status = cpu_to_le32(2);
break;
case WIMAX_RF_ON: /* RFKILL OFF, radio ON */
cmd->sw_rf.status = cpu_to_le32(1);
break;
default:
BUG();
}
ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
result = PTR_ERR(ack_skb);
if (IS_ERR(ack_skb)) {
dev_err(dev, "Failed to issue 'RF Control' command: %d\n",
result);
goto error_msg_to_dev;
}
result = i2400m_msg_check_status(wimax_msg_data(ack_skb),
strerr, sizeof(strerr));
if (result < 0) {
dev_err(dev, "'RF Control' (0x%04x) command failed: %d - %s\n",
I2400M_MT_CMD_RF_CONTROL, result, strerr);
goto error_cmd;
}
/* Now we wait for the state to change to RADIO_OFF or RADIO_ON */
result = wait_event_timeout(
i2400m->state_wq, i2400m_radio_is(i2400m, state),
5 * HZ);
if (result == 0)
result = -ETIMEDOUT;
if (result < 0)
dev_err(dev, "Error waiting for device to toggle RF state: "
"%d\n", result);
result = 0;
error_cmd:
kfree_skb(ack_skb);
error_msg_to_dev:
error_alloc:
d_fnend(4, dev, "(wimax_dev %p state %d) = %d\n",
wimax_dev, state, result);
kfree(cmd);
return result;
}
/*
* Inform the WiMAX stack of changes in the RF Kill switches reported
* by the device
*
* @i2400m: device descriptor
* @rfss: TLV for RF Switches status; already validated
*
* NOTE: the reports on RF switch status cannot be trusted
* or used until the device is in a state of RADIO_OFF
* or greater.
*/
void i2400m_report_tlv_rf_switches_status(
struct i2400m *i2400m,
const struct i2400m_tlv_rf_switches_status *rfss)
{
struct device *dev = i2400m_dev(i2400m);
enum i2400m_rf_switch_status hw, sw;
enum wimax_st wimax_state;
sw = rfss->sw_rf_switch;
hw = rfss->hw_rf_switch;
d_fnstart(3, dev, "(i2400m %p rfss %p [hw %u sw %u])\n",
i2400m, rfss, hw, sw);
/* We only process rw switch evens when the device has been
* fully initialized */
wimax_state = wimax_state_get(&i2400m->wimax_dev);
if (wimax_state < WIMAX_ST_RADIO_OFF) {
d_printf(3, dev, "ignoring RF switches report, state %u\n",
wimax_state);
goto out;
}
switch (sw) {
case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */
wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_ON);
break;
case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */
wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_OFF);
break;
default:
dev_err(dev, "HW BUG? Unknown RF SW state 0x%x\n", sw);
}
switch (hw) {
case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */
wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_ON);
break;
case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */
wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_OFF);
break;
default:
dev_err(dev, "HW BUG? Unknown RF HW state 0x%x\n", hw);
}
out:
d_fnend(3, dev, "(i2400m %p rfss %p [hw %u sw %u]) = void\n",
i2400m, rfss, hw, sw);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless WiMAX Connection 2400m
* Sysfs interfaces to show driver and device information
*
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*/
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include "i2400m.h"
#define D_SUBMODULE sysfs
#include "debug-levels.h"
/*
* Set the idle timeout (msecs)
*
* FIXME: eventually this should be a common WiMAX stack method, but
* would like to wait to see how other devices manage it.
*/
static
ssize_t i2400m_idle_timeout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t result;
struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev));
unsigned val;
result = -EINVAL;
if (sscanf(buf, "%u\n", &val) != 1)
goto error_no_unsigned;
if (val != 0 && (val < 100 || val > 300000 || val % 100 != 0)) {
dev_err(dev, "idle_timeout: %u: invalid msecs specification; "
"valid values are 0, 100-300000 in 100 increments\n",
val);
goto error_bad_value;
}
result = i2400m_set_idle_timeout(i2400m, val);
if (result >= 0)
result = size;
error_no_unsigned:
error_bad_value:
return result;
}
static
DEVICE_ATTR_WO(i2400m_idle_timeout);
static
struct attribute *i2400m_dev_attrs[] = {
&dev_attr_i2400m_idle_timeout.attr,
NULL,
};
struct attribute_group i2400m_dev_attr_group = {
.name = NULL, /* we want them in the same directory */
.attrs = i2400m_dev_attrs,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Intel Wireless WiMAX Connection 2400m
* Debug levels control file for the i2400m-usb module
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*/
#ifndef __debug_levels__h__
#define __debug_levels__h__
/* Maximum compile and run time debug level for all submodules */
#define D_MODULENAME i2400m_usb
#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL
#include "../linux-wimax-debug.h"
/* List of all the enabled modules */
enum d_module {
D_SUBMODULE_DECLARE(usb),
D_SUBMODULE_DECLARE(fw),
D_SUBMODULE_DECLARE(notif),
D_SUBMODULE_DECLARE(rx),
D_SUBMODULE_DECLARE(tx),
};
#endif /* #ifndef __debug_levels__h__ */

View File

@ -1,365 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Firmware uploader's USB specifics
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - bus generic/specific split
*
* THE PROCEDURE
*
* See fw.c for the generic description of this procedure.
*
* This file implements only the USB specifics. It boils down to how
* to send a command and waiting for an acknowledgement from the
* device.
*
* This code (and process) is single threaded. It assumes it is the
* only thread poking around (guaranteed by fw.c).
*
* COMMAND EXECUTION
*
* A write URB is posted with the buffer to the bulk output endpoint.
*
* ACK RECEPTION
*
* We just post a URB to the notification endpoint and wait for
* data. We repeat until we get all the data we expect (as indicated
* by the call from the bus generic code).
*
* The data is not read from the bulk in endpoint for boot mode.
*
* ROADMAP
*
* i2400mu_bus_bm_cmd_send
* i2400m_bm_cmd_prepare...
* i2400mu_tx_bulk_out
*
* i2400mu_bus_bm_wait_for_ack
* i2400m_notif_submit
*/
#include <linux/usb.h>
#include <linux/gfp.h>
#include "i2400m-usb.h"
#define D_SUBMODULE fw
#include "usb-debug-levels.h"
/*
* Synchronous write to the device
*
* Takes care of updating EDC counts and thus, handle device errors.
*/
static
ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
{
int result;
struct device *dev = &i2400mu->usb_iface->dev;
int len;
struct usb_endpoint_descriptor *epd;
int pipe, do_autopm = 1;
result = usb_autopm_get_interface(i2400mu->usb_iface);
if (result < 0) {
dev_err(dev, "BM-CMD: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200);
switch (result) {
case 0:
if (len != buf_size) {
dev_err(dev, "BM-CMD: short write (%u B vs %zu "
"expected)\n", len, buf_size);
result = -EIO;
break;
}
result = len;
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
} else {
usb_clear_halt(i2400mu->usb_dev, pipe);
msleep(10); /* give the device some time */
goto retry;
}
fallthrough;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
break;
case -ETIMEDOUT: /* bah... */
break;
default: /* any other? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: maximum errors in "
"URB exceeded; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
result = -ENODEV;
break;
}
dev_err(dev, "BM-CMD: URB error %d, retrying\n",
result);
goto retry;
}
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
return result;
}
/*
* Send a boot-mode command over the bulk-out pipe
*
* Command can be a raw command, which requires no preparation (and
* which might not even be following the command format). Checks that
* the right amount of data was transferred.
*
* To satisfy USB requirements (no onstack, vmalloc or in data segment
* buffers), we copy the command to i2400m->bm_cmd_buf and send it from
* there.
*
* @flags: pass thru from i2400m_bm_cmd()
* @return: cmd_size if ok, < 0 errno code on error.
*/
ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m,
const struct i2400m_bootrom_header *_cmd,
size_t cmd_size, int flags)
{
ssize_t result;
struct device *dev = i2400m_dev(i2400m);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd);
struct i2400m_bootrom_header *cmd;
size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */
d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n",
i2400m, _cmd, cmd_size);
result = -E2BIG;
if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
goto error_too_big;
if (_cmd != i2400m->bm_cmd_buf)
memmove(i2400m->bm_cmd_buf, _cmd, cmd_size);
cmd = i2400m->bm_cmd_buf;
if (cmd_size_a > cmd_size) /* Zero pad space */
memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
if ((flags & I2400M_BM_CMD_RAW) == 0) {
if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0))
dev_warn(dev, "SW BUG: response_required == 0\n");
i2400m_bm_cmd_prepare(cmd);
}
result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size);
if (result < 0) {
dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n",
opcode, result);
goto error_cmd_send;
}
if (result != cmd_size) { /* all was transferred? */
dev_err(dev, "boot-mode cmd %d: incomplete transfer "
"(%zd vs %zu submitted)\n", opcode, result, cmd_size);
result = -EIO;
goto error_cmd_size;
}
error_cmd_size:
error_cmd_send:
error_too_big:
d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n",
i2400m, _cmd, cmd_size, result);
return result;
}
static
void __i2400mu_bm_notif_cb(struct urb *urb)
{
complete(urb->context);
}
/*
* submit a read to the notification endpoint
*
* @i2400m: device descriptor
* @urb: urb to use
* @completion: completion variable to complete when done
*
* Data is always read to i2400m->bm_ack_buf
*/
static
int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,
struct completion *completion)
{
struct i2400m *i2400m = &i2400mu->i2400m;
struct usb_endpoint_descriptor *epd;
int pipe;
epd = usb_get_epd(i2400mu->usb_iface,
i2400mu->endpoint_cfg.notification);
pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(urb, i2400mu->usb_dev, pipe,
i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
__i2400mu_bm_notif_cb, completion,
epd->bInterval);
return usb_submit_urb(urb, GFP_KERNEL);
}
/*
* Read an ack from the notification endpoint
*
* @i2400m:
* @_ack: pointer to where to store the read data
* @ack_size: how many bytes we should read
*
* Returns: < 0 errno code on error; otherwise, amount of received bytes.
*
* Submits a notification read, appends the read data to the given ack
* buffer and then repeats (until @ack_size bytes have been
* received).
*/
ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m,
struct i2400m_bootrom_header *_ack,
size_t ack_size)
{
ssize_t result = -ENOMEM;
struct device *dev = i2400m_dev(i2400m);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct urb notif_urb;
void *ack = _ack;
size_t offset, len;
long val;
int do_autopm = 1;
DECLARE_COMPLETION_ONSTACK(notif_completion);
d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size);
BUG_ON(_ack == i2400m->bm_ack_buf);
result = usb_autopm_get_interface(i2400mu->usb_iface);
if (result < 0) {
dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result);
do_autopm = 0;
}
usb_init_urb(&notif_urb); /* ready notifications */
usb_get_urb(&notif_urb);
offset = 0;
while (offset < ack_size) {
init_completion(&notif_completion);
result = i2400mu_notif_submit(i2400mu, &notif_urb,
&notif_completion);
if (result < 0)
goto error_notif_urb_submit;
val = wait_for_completion_interruptible_timeout(
&notif_completion, HZ);
if (val == 0) {
result = -ETIMEDOUT;
usb_kill_urb(&notif_urb); /* Timedout */
goto error_notif_wait;
}
if (val == -ERESTARTSYS) {
result = -EINTR; /* Interrupted */
usb_kill_urb(&notif_urb);
goto error_notif_wait;
}
result = notif_urb.status; /* How was the ack? */
switch (result) {
case 0:
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
goto error_dev_gone;
default: /* any other? */
usb_kill_urb(&notif_urb); /* Timedout */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_exceeded;
dev_err(dev, "BM-ACK: URB error %d, "
"retrying\n", notif_urb.status);
continue; /* retry */
}
if (notif_urb.actual_length == 0) {
d_printf(6, dev, "ZLP received, retrying\n");
continue;
}
/* Got data, append it to the buffer */
len = min(ack_size - offset, (size_t) notif_urb.actual_length);
memcpy(ack + offset, i2400m->bm_ack_buf, len);
offset += len;
}
result = offset;
error_notif_urb_submit:
error_notif_wait:
error_dev_gone:
out:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
i2400m, ack, ack_size, (long) result);
usb_put_urb(&notif_urb);
return result;
error_exceeded:
dev_err(dev, "bm: maximum errors in notification URB exceeded; "
"resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
goto out;
}

View File

@ -1,258 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m over USB
* Notification handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
*
* The notification endpoint is active when the device is not in boot
* mode; in here we just read and get notifications; based on those,
* we act to either reinitialize the device after a reboot or to
* submit a RX request.
*
* ROADMAP
*
* i2400mu_usb_notification_setup()
*
* i2400mu_usb_notification_release()
*
* i2400mu_usb_notification_cb() Called when a URB is ready
* i2400mu_notif_grok()
* i2400m_is_boot_barker()
* i2400m_dev_reset_handle()
* i2400mu_rx_kick()
*/
#include <linux/usb.h>
#include <linux/slab.h>
#include "i2400m-usb.h"
#define D_SUBMODULE notif
#include "usb-debug-levels.h"
static const
__le32 i2400m_ZERO_BARKER[4] = { 0, 0, 0, 0 };
/*
* Process a received notification
*
* In normal operation mode, we can only receive two types of payloads
* on the notification endpoint:
*
* - a reboot barker, we do a bootstrap (the device has reseted).
*
* - a block of zeroes: there is pending data in the IN endpoint
*/
static
int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,
size_t buf_len)
{
int ret;
struct device *dev = &i2400mu->usb_iface->dev;
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n",
i2400mu, buf, buf_len);
ret = -EIO;
if (buf_len < sizeof(i2400m_ZERO_BARKER))
/* Not a bug, just ignore */
goto error_bad_size;
ret = 0;
if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
i2400mu_rx_kick(i2400mu);
goto out;
}
ret = i2400m_is_boot_barker(i2400m, buf, buf_len);
if (unlikely(ret >= 0))
ret = i2400m_dev_reset_handle(i2400m, "device rebooted");
else /* Unknown or unexpected data in the notif message */
i2400m_unknown_barker(i2400m, buf, buf_len);
error_bad_size:
out:
d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n",
i2400mu, buf, buf_len, ret);
return ret;
}
/*
* URB callback for the notification endpoint
*
* @urb: the urb received from the notification endpoint
*
* This function will just process the USB side of the transaction,
* checking everything is fine, pass the processing to
* i2400m_notification_grok() and resubmit the URB.
*/
static
void i2400mu_notification_cb(struct urb *urb)
{
int ret;
struct i2400mu *i2400mu = urb->context;
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(4, dev, "(urb %p status %d actual_length %d)\n",
urb, urb->status, urb->actual_length);
ret = urb->status;
switch (ret) {
case 0:
ret = i2400mu_notification_grok(i2400mu, urb->transfer_buffer,
urb->actual_length);
if (ret == -EIO && edc_inc(&i2400mu->urb_edc, EDC_MAX_ERRORS,
EDC_ERROR_TIMEFRAME))
goto error_exceeded;
if (ret == -ENOMEM) /* uff...power cycle? shutdown? */
goto error_exceeded;
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* ditto */
case -ESHUTDOWN: /* URB killed */
case -ECONNRESET: /* disconnection */
goto out; /* Notify around */
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_exceeded;
dev_err(dev, "notification: URB error %d, retrying\n",
urb->status);
}
usb_mark_last_busy(i2400mu->usb_dev);
ret = usb_submit_urb(i2400mu->notif_urb, GFP_ATOMIC);
switch (ret) {
case 0:
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* ditto */
case -ESHUTDOWN: /* URB killed */
case -ECONNRESET: /* disconnection */
break; /* just ignore */
default: /* Some error? */
dev_err(dev, "notification: cannot submit URB: %d\n", ret);
goto error_submit;
}
d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
urb, urb->status, urb->actual_length);
return;
error_exceeded:
dev_err(dev, "maximum errors in notification URB exceeded; "
"resetting device\n");
error_submit:
usb_queue_reset_device(i2400mu->usb_iface);
out:
d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
urb, urb->status, urb->actual_length);
}
/*
* setup the notification endpoint
*
* @i2400m: device descriptor
*
* This procedure prepares the notification urb and handler for receiving
* unsolicited barkers from the device.
*/
int i2400mu_notification_setup(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, ret = 0;
struct usb_endpoint_descriptor *epd;
char *buf;
d_fnstart(4, dev, "(i2400m %p)\n", i2400mu);
buf = kmalloc(I2400MU_MAX_NOTIFICATION_LEN, GFP_KERNEL | GFP_DMA);
if (buf == NULL) {
ret = -ENOMEM;
goto error_buf_alloc;
}
i2400mu->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!i2400mu->notif_urb) {
ret = -ENOMEM;
goto error_alloc_urb;
}
epd = usb_get_epd(i2400mu->usb_iface,
i2400mu->endpoint_cfg.notification);
usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe,
buf, I2400MU_MAX_NOTIFICATION_LEN,
i2400mu_notification_cb, i2400mu, epd->bInterval);
ret = usb_submit_urb(i2400mu->notif_urb, GFP_KERNEL);
if (ret != 0) {
dev_err(dev, "notification: cannot submit URB: %d\n", ret);
goto error_submit;
}
d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
return ret;
error_submit:
usb_free_urb(i2400mu->notif_urb);
error_alloc_urb:
kfree(buf);
error_buf_alloc:
d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
return ret;
}
/*
* Tear down of the notification mechanism
*
* @i2400m: device descriptor
*
* Kill the interrupt endpoint urb, free any allocated resources.
*
* We need to check if we have done it before as for example,
* _suspend() call this; if after a suspend() we get a _disconnect()
* (as the case is when hibernating), nothing bad happens.
*/
void i2400mu_notification_release(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
if (i2400mu->notif_urb != NULL) {
usb_kill_urb(i2400mu->notif_urb);
kfree(i2400mu->notif_urb->transfer_buffer);
usb_free_urb(i2400mu->notif_urb);
i2400mu->notif_urb = NULL;
}
d_fnend(4, dev, "(i2400mu %p)\n", i2400mu);
}

View File

@ -1,462 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB RX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Use skb_clone(), break up processing in chunks
* - Split transport/device specific
* - Make buffer size dynamic to exert less memory pressure
*
*
* This handles the RX path on USB.
*
* When a notification is received that says 'there is RX data ready',
* we call i2400mu_rx_kick(); that wakes up the RX kthread, which
* reads a buffer from USB and passes it to i2400m_rx() in the generic
* handling code. The RX buffer has an specific format that is
* described in rx.c.
*
* We use a kernel thread in a loop because:
*
* - we want to be able to call the USB power management get/put
* functions (blocking) before each transaction.
*
* - We might get a lot of notifications and we don't want to submit
* a zillion reads; by serializing, we are throttling.
*
* - RX data processing can get heavy enough so that it is not
* appropriate for doing it in the USB callback; thus we run it in a
* process context.
*
* We provide a read buffer of an arbitrary size (short of a page); if
* the callback reports -EOVERFLOW, it means it was too small, so we
* just double the size and retry (being careful to append, as
* sometimes the device provided some data). Every now and then we
* check if the average packet size is smaller than the current packet
* size and if so, we halve it. At the end, the size of the
* preallocated buffer should be following the average received
* transaction size, adapting dynamically to it.
*
* ROADMAP
*
* i2400mu_rx_kick() Called from notif.c when we get a
* 'data ready' notification
* i2400mu_rxd() Kernel RX daemon
* i2400mu_rx() Receive USB data
* i2400m_rx() Send data to generic i2400m RX handling
*
* i2400mu_rx_setup() called from i2400mu_bus_dev_start()
*
* i2400mu_rx_release() called from i2400mu_bus_dev_stop()
*/
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include "i2400m-usb.h"
#define D_SUBMODULE rx
#include "usb-debug-levels.h"
/*
* Dynamic RX size
*
* We can't let the rx_size be a multiple of 512 bytes (the RX
* endpoint's max packet size). On some USB host controllers (we
* haven't been able to fully characterize which), if the device is
* about to send (for example) X bytes and we only post a buffer to
* receive n*512, it will fail to mark that as babble (so that
* i2400mu_rx() [case -EOVERFLOW] can resize the buffer and get the
* rest).
*
* So on growing or shrinking, if it is a multiple of the
* maxpacketsize, we remove some (instead of incresing some, so in a
* buddy allocator we try to waste less space).
*
* Note we also need a hook for this on i2400mu_rx() -- when we do the
* first read, we are sure we won't hit this spot because
* i240mm->rx_size has been set properly. However, if we have to
* double because of -EOVERFLOW, when we launch the read to get the
* rest of the data, we *have* to make sure that also is not a
* multiple of the max_pkt_size.
*/
static
size_t i2400mu_rx_size_grow(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
size_t rx_size;
const size_t max_pkt_size = 512;
rx_size = 2 * i2400mu->rx_size;
if (rx_size % max_pkt_size == 0) {
rx_size -= 8;
d_printf(1, dev,
"RX: expected size grew to %zu [adjusted -8] "
"from %zu\n",
rx_size, i2400mu->rx_size);
} else
d_printf(1, dev,
"RX: expected size grew to %zu from %zu\n",
rx_size, i2400mu->rx_size);
return rx_size;
}
static
void i2400mu_rx_size_maybe_shrink(struct i2400mu *i2400mu)
{
const size_t max_pkt_size = 512;
struct device *dev = &i2400mu->usb_iface->dev;
if (unlikely(i2400mu->rx_size_cnt >= 100
&& i2400mu->rx_size_auto_shrink)) {
size_t avg_rx_size =
i2400mu->rx_size_acc / i2400mu->rx_size_cnt;
size_t new_rx_size = i2400mu->rx_size / 2;
if (avg_rx_size < new_rx_size) {
if (new_rx_size % max_pkt_size == 0) {
new_rx_size -= 8;
d_printf(1, dev,
"RX: expected size shrank to %zu "
"[adjusted -8] from %zu\n",
new_rx_size, i2400mu->rx_size);
} else
d_printf(1, dev,
"RX: expected size shrank to %zu "
"from %zu\n",
new_rx_size, i2400mu->rx_size);
i2400mu->rx_size = new_rx_size;
i2400mu->rx_size_cnt = 0;
i2400mu->rx_size_acc = i2400mu->rx_size;
}
}
}
/*
* Receive a message with payloads from the USB bus into an skb
*
* @i2400mu: USB device descriptor
* @rx_skb: skb where to place the received message
*
* Deals with all the USB-specifics of receiving, dynamically
* increasing the buffer size if so needed. Returns the payload in the
* skb, ready to process. On a zero-length packet, we retry.
*
* On soft USB errors, we retry (until they become too frequent and
* then are promoted to hard); on hard USB errors, we reset the
* device. On other errors (skb realloacation, we just drop it and
* hope for the next invocation to solve it).
*
* Returns: pointer to the skb if ok, ERR_PTR on error.
* NOTE: this function might realloc the skb (if it is too small),
* so always update with the one returned.
* ERR_PTR() is < 0 on error.
* Will return NULL if it cannot reallocate -- this can be
* considered a transient retryable error.
*/
static
struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)
{
int result = 0;
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, read_size, rx_size, do_autopm;
struct usb_endpoint_descriptor *epd;
const size_t max_pkt_size = 512;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
do_autopm = atomic_read(&i2400mu->do_autopm);
result = do_autopm ?
usb_autopm_get_interface(i2400mu->usb_iface) : 0;
if (result < 0) {
dev_err(dev, "RX: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_in);
usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len;
if (unlikely(rx_size % max_pkt_size == 0)) {
rx_size -= 8;
d_printf(1, dev, "RX: rx_size adapted to %d [-8]\n", rx_size);
}
result = usb_bulk_msg(
i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len,
rx_size, &read_size, 200);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
if (read_size == 0)
goto retry; /* ZLP, just resubmit */
skb_put(rx_skb, read_size);
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
goto do_reset;
}
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
msleep(10); /* give the device some time */
goto retry;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN:
case -ECONNRESET:
break;
case -EOVERFLOW: { /* too small, reallocate */
struct sk_buff *new_skb;
rx_size = i2400mu_rx_size_grow(i2400mu);
if (rx_size <= (1 << 16)) /* cap it */
i2400mu->rx_size = rx_size;
else if (printk_ratelimit()) {
dev_err(dev, "BUG? rx_size up to %d\n", rx_size);
result = -EINVAL;
goto out;
}
skb_put(rx_skb, read_size);
new_skb = skb_copy_expand(rx_skb, 0, rx_size - rx_skb->len,
GFP_KERNEL);
if (new_skb == NULL) {
kfree_skb(rx_skb);
rx_skb = NULL;
goto out; /* drop it...*/
}
kfree_skb(rx_skb);
rx_skb = new_skb;
i2400mu->rx_size_cnt = 0;
i2400mu->rx_size_acc = i2400mu->rx_size;
d_printf(1, dev, "RX: size changed to %d, received %d, "
"copied %d, capacity %ld\n",
rx_size, read_size, rx_skb->len,
(long) skb_end_offset(new_skb));
goto retry;
}
/* In most cases, it happens due to the hardware scheduling a
* read when there was no data - unfortunately, we have no way
* to tell this timeout from a USB timeout. So we just ignore
* it. */
case -ETIMEDOUT:
dev_err(dev, "RX: timeout: %d\n", result);
result = 0;
break;
default: /* Any error */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_reset;
dev_err(dev, "RX: error receiving URB: %d, retrying\n", result);
goto retry;
}
out:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(4, dev, "(i2400mu %p) = %p\n", i2400mu, rx_skb);
return rx_skb;
error_reset:
dev_err(dev, "RX: maximum errors in URB exceeded; "
"resetting device\n");
do_reset:
usb_queue_reset_device(i2400mu->usb_iface);
rx_skb = ERR_PTR(result);
goto out;
}
/*
* Kernel thread for USB reception of data
*
* This thread waits for a kick; once kicked, it will allocate an skb
* and receive a single message to it from USB (using
* i2400mu_rx()). Once received, it is passed to the generic i2400m RX
* code for processing.
*
* When done processing, it runs some dirty statistics to verify if
* the last 100 messages received were smaller than half of the
* current RX buffer size. In that case, the RX buffer size is
* halved. This will helps lowering the pressure on the memory
* allocator.
*
* Hard errors force the thread to exit.
*/
static
int i2400mu_rxd(void *_i2400mu)
{
int result = 0;
struct i2400mu *i2400mu = _i2400mu;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
size_t pending;
int rx_size;
struct sk_buff *rx_skb;
unsigned long flags;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
spin_lock_irqsave(&i2400m->rx_lock, flags);
BUG_ON(i2400mu->rx_kthread != NULL);
i2400mu->rx_kthread = current;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
while (1) {
d_printf(2, dev, "RX: waiting for messages\n");
pending = 0;
wait_event_interruptible(
i2400mu->rx_wq,
(kthread_should_stop() /* check this first! */
|| (pending = atomic_read(&i2400mu->rx_pending_count)))
);
if (kthread_should_stop())
break;
if (pending == 0)
continue;
rx_size = i2400mu->rx_size;
d_printf(2, dev, "RX: reading up to %d bytes\n", rx_size);
rx_skb = __netdev_alloc_skb(net_dev, rx_size, GFP_KERNEL);
if (rx_skb == NULL) {
dev_err(dev, "RX: can't allocate skb [%d bytes]\n",
rx_size);
msleep(50); /* give it some time? */
continue;
}
/* Receive the message with the payloads */
rx_skb = i2400mu_rx(i2400mu, rx_skb);
result = PTR_ERR(rx_skb);
if (IS_ERR(rx_skb))
goto out;
atomic_dec(&i2400mu->rx_pending_count);
if (rx_skb == NULL || rx_skb->len == 0) {
/* some "ignorable" condition */
kfree_skb(rx_skb);
continue;
}
/* Deliver the message to the generic i2400m code */
i2400mu->rx_size_cnt++;
i2400mu->rx_size_acc += rx_skb->len;
result = i2400m_rx(i2400m, rx_skb);
if (result == -EIO
&& edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
goto error_reset;
}
/* Maybe adjust RX buffer size */
i2400mu_rx_size_maybe_shrink(i2400mu);
}
result = 0;
out:
spin_lock_irqsave(&i2400m->rx_lock, flags);
i2400mu->rx_kthread = NULL;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
return result;
error_reset:
dev_err(dev, "RX: maximum errors in received buffer exceeded; "
"resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
goto out;
}
/*
* Start reading from the device
*
* @i2400m: device instance
*
* Notify the RX thread that there is data pending.
*/
void i2400mu_rx_kick(struct i2400mu *i2400mu)
{
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400mu %p)\n", i2400m);
atomic_inc(&i2400mu->rx_pending_count);
wake_up_all(&i2400mu->rx_wq);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
int i2400mu_rx_setup(struct i2400mu *i2400mu)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct task_struct *kthread;
kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx",
wimax_dev->name);
/* the kthread function sets i2400mu->rx_thread */
if (IS_ERR(kthread)) {
result = PTR_ERR(kthread);
dev_err(dev, "RX: cannot start thread: %d\n", result);
}
return result;
}
void i2400mu_rx_release(struct i2400mu *i2400mu)
{
unsigned long flags;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = i2400m_dev(i2400m);
struct task_struct *kthread;
spin_lock_irqsave(&i2400m->rx_lock, flags);
kthread = i2400mu->rx_kthread;
i2400mu->rx_kthread = NULL;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
if (kthread)
kthread_stop(kthread);
else
d_printf(1, dev, "RX: kthread had already exited\n");
}

View File

@ -1,273 +0,0 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB specific TX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Split transport/device specific
*
*
* Takes the TX messages in the i2400m's driver TX FIFO and sends them
* to the device until there are no more.
*
* If we fail sending the message, we just drop it. There isn't much
* we can do at this point. We could also retry, but the USB stack has
* already retried and still failed, so there is not much of a
* point. As well, most of the traffic is network, which has recovery
* methods for dropped packets.
*
* For sending we just obtain a FIFO buffer to send, send it to the
* USB bulk out, tell the TX FIFO code we have sent it; query for
* another one, etc... until done.
*
* We use a thread so we can call usb_autopm_enable() and
* usb_autopm_disable() for each transaction; this way when the device
* goes idle, it will suspend. It also has less overhead than a
* dedicated workqueue, as it is being used for a single task.
*
* ROADMAP
*
* i2400mu_tx_setup()
* i2400mu_tx_release()
*
* i2400mu_bus_tx_kick() - Called by the tx.c code when there
* is new data in the FIFO.
* i2400mu_txd()
* i2400m_tx_msg_get()
* i2400m_tx_msg_sent()
*/
#include "i2400m-usb.h"
#define D_SUBMODULE tx
#include "usb-debug-levels.h"
/*
* Get the next TX message in the TX FIFO and send it to the device
*
* Note that any iteration consumes a message to be sent, no matter if
* it succeeds or fails (we have no real way to retry or complain).
*
* Return: 0 if ok, < 0 errno code on hard error.
*/
static
int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
size_t tx_msg_size)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, sent_size, do_autopm;
struct usb_endpoint_descriptor *epd;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
do_autopm = atomic_read(&i2400mu->do_autopm);
result = do_autopm ?
usb_autopm_get_interface(i2400mu->usb_iface) : 0;
if (result < 0) {
dev_err(dev, "TX: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
tx_msg, tx_msg_size, &sent_size, 200);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
if (sent_size != tx_msg_size) { /* Too short? drop it */
dev_err(dev, "TX: short write (%d B vs %zu "
"expected)\n", sent_size, tx_msg_size);
result = -EIO;
}
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
} else {
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
msleep(10); /* give the device some time */
goto retry;
}
fallthrough;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
break;
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "TX: maximum errors in URB "
"exceeded; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
} else {
dev_err(dev, "TX: cannot send URB; retrying. "
"tx_msg @%zu %zu B [%d sent]: %d\n",
(void *) tx_msg - i2400m->tx_buf,
tx_msg_size, sent_size, result);
goto retry;
}
}
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu);
return result;
}
/*
* Get the next TX message in the TX FIFO and send it to the device
*
* Note we exit the loop if i2400mu_tx() fails; that function only
* fails on hard error (failing to tx a buffer not being one of them,
* see its doc).
*
* Return: 0
*/
static
int i2400mu_txd(void *_i2400mu)
{
struct i2400mu *i2400mu = _i2400mu;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct i2400m_msg_hdr *tx_msg;
size_t tx_msg_size;
unsigned long flags;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
spin_lock_irqsave(&i2400m->tx_lock, flags);
BUG_ON(i2400mu->tx_kthread != NULL);
i2400mu->tx_kthread = current;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
while (1) {
d_printf(2, dev, "TX: waiting for messages\n");
tx_msg = NULL;
wait_event_interruptible(
i2400mu->tx_wq,
(kthread_should_stop() /* check this first! */
|| (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size)))
);
if (kthread_should_stop())
break;
WARN_ON(tx_msg == NULL); /* should not happen...*/
d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
d_dump(5, dev, tx_msg, tx_msg_size);
/* Yeah, we ignore errors ... not much we can do */
i2400mu_tx(i2400mu, tx_msg, tx_msg_size);
i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */
}
spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400mu->tx_kthread = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnend(4, dev, "(i2400mu %p)\n", i2400mu);
return 0;
}
/*
* i2400m TX engine notifies us that there is data in the FIFO ready
* for TX
*
* If there is a URB in flight, don't do anything; when it finishes,
* it will see there is data in the FIFO and send it. Else, just
* submit a write.
*/
void i2400mu_bus_tx_kick(struct i2400m *i2400m)
{
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
wake_up_all(&i2400mu->tx_wq);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
int i2400mu_tx_setup(struct i2400mu *i2400mu)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct task_struct *kthread;
kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
wimax_dev->name);
/* the kthread function sets i2400mu->tx_thread */
if (IS_ERR(kthread)) {
result = PTR_ERR(kthread);
dev_err(dev, "TX: cannot start thread: %d\n", result);
}
return result;
}
void i2400mu_tx_release(struct i2400mu *i2400mu)
{
unsigned long flags;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = i2400m_dev(i2400m);
struct task_struct *kthread;
spin_lock_irqsave(&i2400m->tx_lock, flags);
kthread = i2400mu->tx_kthread;
i2400mu->tx_kthread = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
if (kthread)
kthread_stop(kthread);
else
d_printf(1, dev, "TX: kthread had already exited\n");
}

View File

@ -1,765 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Wireless WiMAX Connection 2400m
* Linux driver model glue for USB device, reset & fw upload
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
*
* See i2400m-usb.h for a general description of this driver.
*
* This file implements driver model glue, and hook ups for the
* generic driver to implement the bus-specific functions (device
* communication setup/tear down, firmware upload and resetting).
*
* ROADMAP
*
* i2400mu_probe()
* alloc_netdev()...
* i2400mu_netdev_setup()
* i2400mu_init()
* i2400m_netdev_setup()
* i2400m_setup()...
*
* i2400mu_disconnect
* i2400m_release()
* free_netdev()
*
* i2400mu_suspend()
* i2400m_cmd_enter_powersave()
* i2400mu_notification_release()
*
* i2400mu_resume()
* i2400mu_notification_setup()
*
* i2400mu_bus_dev_start() Called by i2400m_dev_start() [who is
* i2400mu_tx_setup() called by i2400m_setup()]
* i2400mu_rx_setup()
* i2400mu_notification_setup()
*
* i2400mu_bus_dev_stop() Called by i2400m_dev_stop() [who is
* i2400mu_notification_release() called by i2400m_release()]
* i2400mu_rx_release()
* i2400mu_tx_release()
*
* i2400mu_bus_reset() Called by i2400m_reset
* __i2400mu_reset()
* __i2400mu_send_barker()
* usb_reset_device()
*/
#include "i2400m-usb.h"
#include "linux-wimax-i2400m.h"
#include <linux/debugfs.h>
#include <linux/ethtool.h>
#include <linux/slab.h>
#include <linux/module.h>
#define D_SUBMODULE usb
#include "usb-debug-levels.h"
static char i2400mu_debug_params[128];
module_param_string(debug, i2400mu_debug_params, sizeof(i2400mu_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.");
/* Our firmware file name */
static const char *i2400mu_bus_fw_names_5x50[] = {
#define I2400MU_FW_FILE_NAME_v1_5 "i2400m-fw-usb-1.5.sbcf"
I2400MU_FW_FILE_NAME_v1_5,
#define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf"
I2400MU_FW_FILE_NAME_v1_4,
NULL,
};
static const char *i2400mu_bus_fw_names_6050[] = {
#define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf"
I6050U_FW_FILE_NAME_v1_5,
NULL,
};
static
int i2400mu_bus_dev_start(struct i2400m *i2400m)
{
int result;
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
result = i2400mu_tx_setup(i2400mu);
if (result < 0)
goto error_usb_tx_setup;
result = i2400mu_rx_setup(i2400mu);
if (result < 0)
goto error_usb_rx_setup;
result = i2400mu_notification_setup(i2400mu);
if (result < 0)
goto error_notif_setup;
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
error_notif_setup:
i2400mu_rx_release(i2400mu);
error_usb_rx_setup:
i2400mu_tx_release(i2400mu);
error_usb_tx_setup:
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
return result;
}
static
void i2400mu_bus_dev_stop(struct i2400m *i2400m)
{
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
i2400mu_notification_release(i2400mu);
i2400mu_rx_release(i2400mu);
i2400mu_tx_release(i2400mu);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
/*
* Sends a barker buffer to the device
*
* This helper will allocate a kmalloced buffer and use it to transmit
* (then free it). Reason for this is that other arches cannot use
* stack/vmalloc/text areas for DMA transfers.
*
* Error recovery here is simpler: anything is considered a hard error
* and will move the reset code to use a last-resort bus-based reset.
*/
static
int __i2400mu_send_barker(struct i2400mu *i2400mu,
const __le32 *barker,
size_t barker_size,
unsigned endpoint)
{
struct usb_endpoint_descriptor *epd = NULL;
int pipe, actual_len, ret;
struct device *dev = &i2400mu->usb_iface->dev;
void *buffer;
int do_autopm = 1;
ret = usb_autopm_get_interface(i2400mu->usb_iface);
if (ret < 0) {
dev_err(dev, "RESET: can't get autopm: %d\n", ret);
do_autopm = 0;
}
ret = -ENOMEM;
buffer = kmalloc(barker_size, GFP_KERNEL);
if (buffer == NULL)
goto error_kzalloc;
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
memcpy(buffer, barker, barker_size);
retry:
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
&actual_len, 200);
switch (ret) {
case 0:
if (actual_len != barker_size) { /* Too short? drop it */
dev_err(dev, "E: %s: short write (%d B vs %zu "
"expected)\n",
__func__, actual_len, barker_size);
ret = -EIO;
}
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "E: %s: too many stalls in "
"URB; resetting device\n", __func__);
usb_queue_reset_device(i2400mu->usb_iface);
/* fallthrough */
} else {
usb_clear_halt(i2400mu->usb_dev, pipe);
msleep(10); /* give the device some time */
goto retry;
}
fallthrough;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
ret = -ESHUTDOWN;
break;
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "E: %s: maximum errors in URB "
"exceeded; resetting device\n",
__func__);
usb_queue_reset_device(i2400mu->usb_iface);
} else {
dev_warn(dev, "W: %s: cannot send URB: %d\n",
__func__, ret);
goto retry;
}
}
kfree(buffer);
error_kzalloc:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
return ret;
}
/*
* Reset a device at different levels (warm, cold or bus)
*
* @i2400m: device descriptor
* @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS)
*
* Warm and cold resets get a USB reset if they fail.
*
* Warm reset:
*
* The device will be fully reset internally, but won't be
* disconnected from the USB bus (so no reenumeration will
* happen). Firmware upload will be necessary.
*
* The device will send a reboot barker in the notification endpoint
* that will trigger the driver to reinitialize the state
* automatically from notif.c:i2400m_notification_grok() into
* i2400m_dev_bootstrap_delayed().
*
* Cold and bus (USB) reset:
*
* The device will be fully reset internally, disconnected from the
* USB bus an a reenumeration will happen. Firmware upload will be
* necessary. Thus, we don't do any locking or struct
* reinitialization, as we are going to be fully disconnected and
* reenumerated.
*
* Note we need to return -ENODEV if a warm reset was requested and we
* had to resort to a bus reset. See i2400m_op_reset(), wimax_reset()
* and wimax_dev->op_reset.
*
* WARNING: no driver state saved/fixed
*/
static
int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
{
int result;
struct i2400mu *i2400mu =
container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = i2400m_dev(i2400m);
static const __le32 i2400m_WARM_BOOT_BARKER[4] = {
cpu_to_le32(I2400M_WARM_RESET_BARKER),
cpu_to_le32(I2400M_WARM_RESET_BARKER),
cpu_to_le32(I2400M_WARM_RESET_BARKER),
cpu_to_le32(I2400M_WARM_RESET_BARKER),
};
static const __le32 i2400m_COLD_BOOT_BARKER[4] = {
cpu_to_le32(I2400M_COLD_RESET_BARKER),
cpu_to_le32(I2400M_COLD_RESET_BARKER),
cpu_to_le32(I2400M_COLD_RESET_BARKER),
cpu_to_le32(I2400M_COLD_RESET_BARKER),
};
d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt);
if (rt == I2400M_RT_WARM)
result = __i2400mu_send_barker(
i2400mu, i2400m_WARM_BOOT_BARKER,
sizeof(i2400m_WARM_BOOT_BARKER),
i2400mu->endpoint_cfg.bulk_out);
else if (rt == I2400M_RT_COLD)
result = __i2400mu_send_barker(
i2400mu, i2400m_COLD_BOOT_BARKER,
sizeof(i2400m_COLD_BOOT_BARKER),
i2400mu->endpoint_cfg.reset_cold);
else if (rt == I2400M_RT_BUS) {
result = usb_reset_device(i2400mu->usb_dev);
switch (result) {
case 0:
case -EINVAL: /* device is gone */
case -ENODEV:
case -ENOENT:
case -ESHUTDOWN:
result = 0;
break; /* We assume the device is disconnected */
default:
dev_err(dev, "USB reset failed (%d), giving up!\n",
result);
}
} else {
result = -EINVAL; /* shut gcc up in certain arches */
BUG();
}
if (result < 0
&& result != -EINVAL /* device is gone */
&& rt != I2400M_RT_BUS) {
/*
* Things failed -- resort to lower level reset, that
* we queue in another context; the reason for this is
* that the pre and post reset functionality requires
* the i2400m->init_mutex; RT_WARM and RT_COLD can
* come from areas where i2400m->init_mutex is taken.
*/
dev_err(dev, "%s reset failed (%d); trying USB reset\n",
rt == I2400M_RT_WARM ? "warm" : "cold", result);
usb_queue_reset_device(i2400mu->usb_iface);
result = -ENODEV;
}
d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result);
return result;
}
static void i2400mu_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct usb_device *udev = i2400mu->usb_dev;
strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strscpy(info->fw_version, i2400m->fw_name ? : "",
sizeof(info->fw_version));
usb_make_path(udev, info->bus_info, sizeof(info->bus_info));
}
static const struct ethtool_ops i2400mu_ethtool_ops = {
.get_drvinfo = i2400mu_get_drvinfo,
.get_link = ethtool_op_get_link,
};
static
void i2400mu_netdev_setup(struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
i2400mu_init(i2400mu);
i2400m_netdev_setup(net_dev);
net_dev->ethtool_ops = &i2400mu_ethtool_ops;
}
/*
* Debug levels control; see debug.h
*/
struct d_level D_LEVEL[] = {
D_SUBMODULE_DEFINE(usb),
D_SUBMODULE_DEFINE(fw),
D_SUBMODULE_DEFINE(notif),
D_SUBMODULE_DEFINE(rx),
D_SUBMODULE_DEFINE(tx),
};
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
static
void i2400mu_debugfs_add(struct i2400mu *i2400mu)
{
struct dentry *dentry = i2400mu->i2400m.wimax_dev.debugfs_dentry;
dentry = debugfs_create_dir("i2400m-usb", dentry);
i2400mu->debugfs_dentry = dentry;
d_level_register_debugfs("dl_", usb, dentry);
d_level_register_debugfs("dl_", fw, dentry);
d_level_register_debugfs("dl_", notif, dentry);
d_level_register_debugfs("dl_", rx, dentry);
d_level_register_debugfs("dl_", tx, dentry);
/* Don't touch these if you don't know what you are doing */
debugfs_create_u8("rx_size_auto_shrink", 0600, dentry,
&i2400mu->rx_size_auto_shrink);
debugfs_create_size_t("rx_size", 0600, dentry, &i2400mu->rx_size);
}
static struct device_type i2400mu_type = {
.name = "wimax",
};
/*
* Probe a i2400m interface and register it
*
* @iface: USB interface to link to
* @id: USB class/subclass/protocol id
* @returns: 0 if ok, < 0 errno code on error.
*
* Alloc a net device, initialize the bus-specific details and then
* calls the bus-generic initialization routine. That will register
* the wimax and netdev devices, upload the firmware [using
* _bus_bm_*()], call _bus_dev_start() to finalize the setup of the
* communication with the device and then will start to talk to it to
* finnish setting it up.
*/
static
int i2400mu_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
int result;
struct net_device *net_dev;
struct device *dev = &iface->dev;
struct i2400m *i2400m;
struct i2400mu *i2400mu;
struct usb_device *usb_dev = interface_to_usbdev(iface);
if (iface->cur_altsetting->desc.bNumEndpoints < 4)
return -ENODEV;
if (usb_dev->speed != USB_SPEED_HIGH)
dev_err(dev, "device not connected as high speed\n");
/* Allocate instance [calls i2400m_netdev_setup() on it]. */
result = -ENOMEM;
net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", NET_NAME_UNKNOWN,
i2400mu_netdev_setup);
if (net_dev == NULL) {
dev_err(dev, "no memory for network device instance\n");
goto error_alloc_netdev;
}
SET_NETDEV_DEV(net_dev, dev);
SET_NETDEV_DEVTYPE(net_dev, &i2400mu_type);
i2400m = net_dev_to_i2400m(net_dev);
i2400mu = container_of(i2400m, struct i2400mu, i2400m);
i2400m->wimax_dev.net_dev = net_dev;
i2400mu->usb_dev = usb_get_dev(usb_dev);
i2400mu->usb_iface = iface;
usb_set_intfdata(iface, i2400mu);
i2400m->bus_tx_block_size = I2400MU_BLK_SIZE;
/*
* Room required in the Tx queue for USB message to accommodate
* a smallest payload while allocating header space is 16 bytes.
* Adding this room for the new tx message increases the
* possibilities of including any payload with size <= 16 bytes.
*/
i2400m->bus_tx_room_min = I2400MU_BLK_SIZE;
i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX;
i2400m->bus_setup = NULL;
i2400m->bus_dev_start = i2400mu_bus_dev_start;
i2400m->bus_dev_stop = i2400mu_bus_dev_stop;
i2400m->bus_release = NULL;
i2400m->bus_tx_kick = i2400mu_bus_tx_kick;
i2400m->bus_reset = i2400mu_bus_reset;
i2400m->bus_bm_retries = I2400M_USB_BOOT_RETRIES;
i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack;
i2400m->bus_bm_mac_addr_impaired = 0;
switch (id->idProduct) {
case USB_DEVICE_ID_I6050:
case USB_DEVICE_ID_I6050_2:
case USB_DEVICE_ID_I6150:
case USB_DEVICE_ID_I6150_2:
case USB_DEVICE_ID_I6150_3:
case USB_DEVICE_ID_I6250:
i2400mu->i6050 = 1;
break;
default:
break;
}
if (i2400mu->i6050) {
i2400m->bus_fw_names = i2400mu_bus_fw_names_6050;
i2400mu->endpoint_cfg.bulk_out = 0;
i2400mu->endpoint_cfg.notification = 3;
i2400mu->endpoint_cfg.reset_cold = 2;
i2400mu->endpoint_cfg.bulk_in = 1;
} else {
i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50;
i2400mu->endpoint_cfg.bulk_out = 0;
i2400mu->endpoint_cfg.notification = 1;
i2400mu->endpoint_cfg.reset_cold = 2;
i2400mu->endpoint_cfg.bulk_in = 3;
}
#ifdef CONFIG_PM
iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */
device_init_wakeup(dev, 1);
pm_runtime_set_autosuspend_delay(&usb_dev->dev, 15000);
usb_enable_autosuspend(usb_dev);
#endif
result = i2400m_setup(i2400m, I2400M_BRI_MAC_REINIT);
if (result < 0) {
dev_err(dev, "cannot setup device: %d\n", result);
goto error_setup;
}
i2400mu_debugfs_add(i2400mu);
return 0;
error_setup:
usb_set_intfdata(iface, NULL);
usb_put_dev(i2400mu->usb_dev);
free_netdev(net_dev);
error_alloc_netdev:
return result;
}
/*
* Disconnect a i2400m from the system.
*
* i2400m_stop() has been called before, so al the rx and tx contexts
* have been taken down already. Make sure the queue is stopped,
* unregister netdev and i2400m, free and kill.
*/
static
void i2400mu_disconnect(struct usb_interface *iface)
{
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct i2400m *i2400m = &i2400mu->i2400m;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = &iface->dev;
d_fnstart(3, dev, "(iface %p i2400m %p)\n", iface, i2400m);
debugfs_remove_recursive(i2400mu->debugfs_dentry);
i2400m_release(i2400m);
usb_set_intfdata(iface, NULL);
usb_put_dev(i2400mu->usb_dev);
free_netdev(net_dev);
d_fnend(3, dev, "(iface %p i2400m %p) = void\n", iface, i2400m);
}
/*
* Get the device ready for USB port or system standby and hibernation
*
* USB port and system standby are handled the same.
*
* When the system hibernates, the USB device is powered down and then
* up, so we don't really have to do much here, as it will be seen as
* a reconnect. Still for simplicity we consider this case the same as
* suspend, so that the device has a chance to do notify the base
* station (if connected).
*
* So at the end, the three cases require common handling.
*
* If at the time of this call the device's firmware is not loaded,
* nothing has to be done. Note we can be "loose" about not reading
* i2400m->updown under i2400m->init_mutex. If it happens to change
* inmediately, other parts of the call flow will fail and effectively
* catch it.
*
* If the firmware is loaded, we need to:
*
* - tell the device to go into host interface power save mode, wait
* for it to ack
*
* This is quite more interesting than it is; we need to execute a
* command, but this time, we don't want the code in usb-{tx,rx}.c
* to call the usb_autopm_get/put_interface() barriers as it'd
* deadlock, so we need to decrement i2400mu->do_autopm, that acts
* as a poor man's semaphore. Ugly, but it works.
*
* As well, the device might refuse going to sleep for whichever
* reason. In this case we just fail. For system suspend/hibernate,
* we *can't* fail. We check PMSG_IS_AUTO to see if the
* suspend call comes from the USB stack or from the system and act
* in consequence.
*
* - stop the notification endpoint polling
*/
static
int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
{
int result = 0;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
unsigned is_autosuspend = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
#ifdef CONFIG_PM
if (PMSG_IS_AUTO(pm_msg))
is_autosuspend = 1;
#endif
d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event);
rmb(); /* see i2400m->updown's documentation */
if (i2400m->updown == 0)
goto no_firmware;
if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) {
/* ugh -- the device is connected and this suspend
* request is an autosuspend one (not a system standby
* / hibernate).
*
* The only way the device can go to standby is if the
* link with the base station is in IDLE mode; that
* were the case, we'd be in status
* I2400M_SS_CONNECTED_IDLE. But we are not.
*
* If we *tell* him to go power save now, it'll reset
* as a precautionary measure, so if this is an
* autosuspend thing, say no and it'll come back
* later, when the link is IDLE
*/
result = -EBADF;
d_printf(1, dev, "fw up, link up, not-idle, autosuspend: "
"not entering powersave\n");
goto error_not_now;
}
d_printf(1, dev, "fw up: entering powersave\n");
atomic_dec(&i2400mu->do_autopm);
result = i2400m_cmd_enter_powersave(i2400m);
atomic_inc(&i2400mu->do_autopm);
if (result < 0 && !is_autosuspend) {
/* System suspend, can't fail */
dev_err(dev, "failed to suspend, will reset on resume\n");
result = 0;
}
if (result < 0)
goto error_enter_powersave;
i2400mu_notification_release(i2400mu);
d_printf(1, dev, "powersave requested\n");
error_enter_powersave:
error_not_now:
no_firmware:
d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n",
iface, pm_msg.event, result);
return result;
}
static
int i2400mu_resume(struct usb_interface *iface)
{
int ret = 0;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(3, dev, "(iface %p)\n", iface);
rmb(); /* see i2400m->updown's documentation */
if (i2400m->updown == 0) {
d_printf(1, dev, "fw was down, no resume needed\n");
goto out;
}
d_printf(1, dev, "fw was up, resuming\n");
i2400mu_notification_setup(i2400mu);
/* USB has flow control, so we don't need to give it time to
* come back; otherwise, we'd use something like a get-state
* command... */
out:
d_fnend(3, dev, "(iface %p) = %d\n", iface, ret);
return ret;
}
static
int i2400mu_reset_resume(struct usb_interface *iface)
{
int result;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(3, dev, "(iface %p)\n", iface);
result = i2400m_dev_reset_handle(i2400m, "device reset on resume");
d_fnend(3, dev, "(iface %p) = %d\n", iface, result);
return result < 0 ? result : 0;
}
/*
* Another driver or user space is triggering a reset on the device
* which contains the interface passed as an argument. Cease IO and
* save any device state you need to restore.
*
* If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if
* you are in atomic context.
*/
static
int i2400mu_pre_reset(struct usb_interface *iface)
{
struct i2400mu *i2400mu = usb_get_intfdata(iface);
return i2400m_pre_reset(&i2400mu->i2400m);
}
/*
* The reset has completed. Restore any saved device state and begin
* using the device again.
*
* If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if
* you are in atomic context.
*/
static
int i2400mu_post_reset(struct usb_interface *iface)
{
struct i2400mu *i2400mu = usb_get_intfdata(iface);
return i2400m_post_reset(&i2400mu->i2400m);
}
static
struct usb_device_id i2400mu_id_table[] = {
{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) },
{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6050_2) },
{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150) },
{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_2) },
{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_3) },
{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6250) },
{ USB_DEVICE(0x8086, 0x0181) },
{ USB_DEVICE(0x8086, 0x1403) },
{ USB_DEVICE(0x8086, 0x1405) },
{ USB_DEVICE(0x8086, 0x0180) },
{ USB_DEVICE(0x8086, 0x0182) },
{ USB_DEVICE(0x8086, 0x1406) },
{ USB_DEVICE(0x8086, 0x1403) },
{ },
};
MODULE_DEVICE_TABLE(usb, i2400mu_id_table);
static
struct usb_driver i2400mu_driver = {
.name = KBUILD_MODNAME,
.suspend = i2400mu_suspend,
.resume = i2400mu_resume,
.reset_resume = i2400mu_reset_resume,
.probe = i2400mu_probe,
.disconnect = i2400mu_disconnect,
.pre_reset = i2400mu_pre_reset,
.post_reset = i2400mu_post_reset,
.id_table = i2400mu_id_table,
.supports_autosuspend = 1,
};
static
int __init i2400mu_driver_init(void)
{
d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400mu_debug_params,
"i2400m_usb.debug");
return usb_register(&i2400mu_driver);
}
module_init(i2400mu_driver_init);
static
void __exit i2400mu_driver_exit(void)
{
usb_deregister(&i2400mu_driver);
}
module_exit(i2400mu_driver_exit);
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
MODULE_DESCRIPTION("Driver for USB based Intel Wireless WiMAX Connection 2400M "
"(5x50 & 6050)");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_5);
MODULE_FIRMWARE(I6050U_FW_FILE_NAME_v1_5);

View File

@ -1,130 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*
* 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);
}

View File

@ -1,491 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Linux WiMAX
* Collection of tools to manage debug operations.
*
* Copyright (C) 2005-2007 Intel Corporation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* Don't #include this file directly, read on!
*
* EXECUTING DEBUGGING ACTIONS OR NOT
*
* The main thing this framework provides is decission power to take a
* debug action (like printing a message) if the current debug level
* allows it.
*
* The decission power is at two levels: at compile-time (what does
* not make it is compiled out) and at run-time. The run-time
* selection is done per-submodule (as they are declared by the user
* of the framework).
*
* A call to d_test(L) (L being the target debug level) returns true
* if the action should be taken because the current debug levels
* allow it (both compile and run time).
*
* It follows that a call to d_test() that can be determined to be
* always false at compile time will get the code depending on it
* compiled out by optimization.
*
* DEBUG LEVELS
*
* It is up to the caller to define how much a debugging level is.
*
* Convention sets 0 as "no debug" (so an action marked as debug level 0
* will always be taken). The increasing debug levels are used for
* increased verbosity.
*
* USAGE
*
* Group the code in modules and submodules inside each module [which
* in most cases maps to Linux modules and .c files that compose
* those].
*
* For each module, there is:
*
* - a MODULENAME (single word, legal C identifier)
*
* - a debug-levels.h header file that declares the list of
* submodules and that is included by all .c files that use
* the debugging tools. The file name can be anything.
*
* - some (optional) .c code to manipulate the runtime debug levels
* through debugfs.
*
* The debug-levels.h file would look like:
*
* #ifndef __debug_levels__h__
* #define __debug_levels__h__
*
* #define D_MODULENAME modulename
* #define D_MASTER 10
*
* #include "linux-wimax-debug.h"
*
* enum d_module {
* D_SUBMODULE_DECLARE(submodule_1),
* D_SUBMODULE_DECLARE(submodule_2),
* ...
* D_SUBMODULE_DECLARE(submodule_N)
* };
*
* #endif
*
* D_MASTER is the maximum compile-time debug level; any debug actions
* above this will be out. D_MODULENAME is the module name (legal C
* identifier), which has to be unique for each module (to avoid
* namespace collisions during linkage). Note those #defines need to
* be done before #including debug.h
*
* We declare N different submodules whose debug level can be
* independently controlled during runtime.
*
* In a .c file of the module (and only in one of them), define the
* following code:
*
* struct d_level D_LEVEL[] = {
* D_SUBMODULE_DEFINE(submodule_1),
* D_SUBMODULE_DEFINE(submodule_2),
* ...
* D_SUBMODULE_DEFINE(submodule_N),
* };
* size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
*
* Externs for d_level_MODULENAME and d_level_size_MODULENAME are used
* and declared in this file using the D_LEVEL and D_LEVEL_SIZE macros
* #defined also in this file.
*
* To manipulate from user space the levels, create a debugfs dentry
* and then register each submodule with:
*
* d_level_register_debugfs("PREFIX_", submodule_X, parent);
*
* Where PREFIX_ is a name of your chosing. This will create debugfs
* file with a single numeric value that can be use to tweak it. To
* remove the entires, just use debugfs_remove_recursive() on 'parent'.
*
* NOTE: remember that even if this will show attached to some
* particular instance of a device, the settings are *global*.
*
* On each submodule (for example, .c files), the debug infrastructure
* should be included like this:
*
* #define D_SUBMODULE submodule_x // matches one in debug-levels.h
* #include "debug-levels.h"
*
* after #including all your include files.
*
* Now you can use the d_*() macros below [d_test(), d_fnstart(),
* d_fnend(), d_printf(), d_dump()].
*
* If their debug level is greater than D_MASTER, they will be
* compiled out.
*
* If their debug level is lower or equal than D_MASTER but greater
* than the current debug level of their submodule, they'll be
* ignored.
*
* Otherwise, the action will be performed.
*/
#ifndef __debug__h__
#define __debug__h__
#include <linux/types.h>
#include <linux/slab.h>
struct device;
/* Backend stuff */
/*
* Debug backend: generate a message header from a 'struct device'
*
* @head: buffer where to place the header
* @head_size: length of @head
* @dev: pointer to device used to generate a header from. If NULL,
* an empty ("") header is generated.
*/
static inline
void __d_head(char *head, size_t head_size,
struct device *dev)
{
if (dev == NULL)
head[0] = 0;
else if ((unsigned long)dev < 4096) {
printk(KERN_ERR "E: Corrupt dev %p\n", dev);
WARN_ON(1);
} else
snprintf(head, head_size, "%s %s: ",
dev_driver_string(dev), dev_name(dev));
}
/*
* Debug backend: log some message if debugging is enabled
*
* @l: intended debug level
* @tag: tag to prefix the message with
* @dev: 'struct device' associated to this message
* @f: printf-like format and arguments
*
* Note this is optimized out if it doesn't pass the compile-time
* check; however, it is *always* compiled. This is useful to make
* sure the printf-like formats and variables are always checked and
* they don't get bit rot if you have all the debugging disabled.
*/
#define _d_printf(l, tag, dev, f, a...) \
do { \
char head[64]; \
if (!d_test(l)) \
break; \
__d_head(head, sizeof(head), dev); \
printk(KERN_ERR "%s%s%s: " f, head, __func__, tag, ##a); \
} while (0)
/*
* CPP syntactic sugar to generate A_B like symbol names when one of
* the arguments is a preprocessor #define.
*/
#define __D_PASTE__(varname, modulename) varname##_##modulename
#define __D_PASTE(varname, modulename) (__D_PASTE__(varname, modulename))
#define _D_SUBMODULE_INDEX(_name) (D_SUBMODULE_DECLARE(_name))
/*
* Store a submodule's runtime debug level and name
*/
struct d_level {
u8 level;
const char *name;
};
/*
* List of available submodules and their debug levels
*
* We call them d_level_MODULENAME and d_level_size_MODULENAME; the
* macros D_LEVEL and D_LEVEL_SIZE contain the name already for
* convenience.
*
* This array and the size are defined on some .c file that is part of
* the current module.
*/
#define D_LEVEL __D_PASTE(d_level, D_MODULENAME)
#define D_LEVEL_SIZE __D_PASTE(d_level_size, D_MODULENAME)
extern struct d_level D_LEVEL[];
extern size_t D_LEVEL_SIZE;
/*
* Frontend stuff
*
*
* Stuff you need to declare prior to using the actual "debug" actions
* (defined below).
*/
#ifndef D_MODULENAME
#error D_MODULENAME is not defined in your debug-levels.h file
/**
* D_MODULE - Name of the current module
*
* #define in your module's debug-levels.h, making sure it is
* unique. This has to be a legal C identifier.
*/
#define D_MODULENAME undefined_modulename
#endif
#ifndef D_MASTER
#warning D_MASTER not defined, but debug.h included! [see docs]
/**
* D_MASTER - Compile time maximum debug level
*
* #define in your debug-levels.h file to the maximum debug level the
* runtime code will be allowed to have. This allows you to provide a
* main knob.
*
* Anything above that level will be optimized out of the compile.
*
* Defaults to zero (no debug code compiled in).
*
* Maximum one definition per module (at the debug-levels.h file).
*/
#define D_MASTER 0
#endif
#ifndef D_SUBMODULE
#error D_SUBMODULE not defined, but debug.h included! [see docs]
/**
* D_SUBMODULE - Name of the current submodule
*
* #define in your submodule .c file before #including debug-levels.h
* to the name of the current submodule as previously declared and
* defined with D_SUBMODULE_DECLARE() (in your module's
* debug-levels.h) and D_SUBMODULE_DEFINE().
*
* This is used to provide runtime-control over the debug levels.
*
* Maximum one per .c file! Can be shared among different .c files
* (meaning they belong to the same submodule categorization).
*/
#define D_SUBMODULE undefined_module
#endif
/**
* D_SUBMODULE_DECLARE - Declare a submodule for runtime debug level control
*
* @_name: name of the submodule, restricted to the chars that make up a
* valid C identifier ([a-zA-Z0-9_]).
*
* Declare in the module's debug-levels.h header file as:
*
* enum d_module {
* D_SUBMODULE_DECLARE(submodule_1),
* D_SUBMODULE_DECLARE(submodule_2),
* D_SUBMODULE_DECLARE(submodule_3),
* };
*
* Some corresponding .c file needs to have a matching
* D_SUBMODULE_DEFINE().
*/
#define D_SUBMODULE_DECLARE(_name) __D_SUBMODULE_##_name
/**
* D_SUBMODULE_DEFINE - Define a submodule for runtime debug level control
*
* @_name: name of the submodule, restricted to the chars that make up a
* valid C identifier ([a-zA-Z0-9_]).
*
* Use once per module (in some .c file) as:
*
* static
* struct d_level d_level_SUBMODULENAME[] = {
* D_SUBMODULE_DEFINE(submodule_1),
* D_SUBMODULE_DEFINE(submodule_2),
* D_SUBMODULE_DEFINE(submodule_3),
* };
* size_t d_level_size_SUBDMODULENAME = ARRAY_SIZE(d_level_SUBDMODULENAME);
*
* Matching D_SUBMODULE_DECLARE()s have to be present in a
* debug-levels.h header file.
*/
#define D_SUBMODULE_DEFINE(_name) \
[__D_SUBMODULE_##_name] = { \
.level = 0, \
.name = #_name \
}
/* The actual "debug" operations */
/**
* d_test - Returns true if debugging should be enabled
*
* @l: intended debug level (unsigned)
*
* If the master debug switch is enabled and the current settings are
* higher or equal to the requested level, then debugging
* output/actions should be enabled.
*
* NOTE:
*
* This needs to be coded so that it can be evaluated in compile
* time; this is why the ugly BUG_ON() is placed in there, so the
* D_MASTER evaluation compiles all out if it is compile-time false.
*/
#define d_test(l) \
({ \
unsigned __l = l; /* type enforcer */ \
(D_MASTER) >= __l \
&& ({ \
BUG_ON(_D_SUBMODULE_INDEX(D_SUBMODULE) >= D_LEVEL_SIZE);\
D_LEVEL[_D_SUBMODULE_INDEX(D_SUBMODULE)].level >= __l; \
}); \
})
/**
* d_fnstart - log message at function start if debugging enabled
*
* @l: intended debug level
* @_dev: 'struct device' pointer, NULL if none (for context)
* @f: printf-like format and arguments
*/
#define d_fnstart(l, _dev, f, a...) _d_printf(l, " FNSTART", _dev, f, ## a)
/**
* d_fnend - log message at function end if debugging enabled
*
* @l: intended debug level
* @_dev: 'struct device' pointer, NULL if none (for context)
* @f: printf-like format and arguments
*/
#define d_fnend(l, _dev, f, a...) _d_printf(l, " FNEND", _dev, f, ## a)
/**
* d_printf - log message if debugging enabled
*
* @l: intended debug level
* @_dev: 'struct device' pointer, NULL if none (for context)
* @f: printf-like format and arguments
*/
#define d_printf(l, _dev, f, a...) _d_printf(l, "", _dev, f, ## a)
/**
* d_dump - log buffer hex dump if debugging enabled
*
* @l: intended debug level
* @_dev: 'struct device' pointer, NULL if none (for context)
* @f: printf-like format and arguments
*/
#define d_dump(l, dev, ptr, size) \
do { \
char head[64]; \
if (!d_test(l)) \
break; \
__d_head(head, sizeof(head), dev); \
print_hex_dump(KERN_ERR, head, 0, 16, 1, \
((void *) ptr), (size), 0); \
} while (0)
/**
* Export a submodule's debug level over debugfs as PREFIXSUBMODULE
*
* @prefix: string to prefix the name with
* @submodule: name of submodule (not a string, just the name)
* @dentry: debugfs parent dentry
*
* For removing, just use debugfs_remove_recursive() on the parent.
*/
#define d_level_register_debugfs(prefix, name, parent) \
({ \
debugfs_create_u8( \
prefix #name, 0600, parent, \
&(D_LEVEL[__D_SUBMODULE_ ## name].level)); \
})
static inline
void d_submodule_set(struct d_level *d_level, size_t d_level_size,
const char *submodule, u8 level, const char *tag)
{
struct d_level *itr, *top;
int index = -1;
for (itr = d_level, top = itr + d_level_size; itr < top; itr++) {
index++;
if (itr->name == NULL) {
printk(KERN_ERR "%s: itr->name NULL?? (%p, #%d)\n",
tag, itr, index);
continue;
}
if (!strcmp(itr->name, submodule)) {
itr->level = level;
return;
}
}
printk(KERN_ERR "%s: unknown submodule %s\n", tag, submodule);
}
/**
* d_parse_params - Parse a string with debug parameters from the
* command line
*
* @d_level: level structure (D_LEVEL)
* @d_level_size: number of items in the level structure
* (D_LEVEL_SIZE).
* @_params: string with the parameters; this is a space (not tab!)
* separated list of NAME:VALUE, where value is the debug level
* and NAME is the name of the submodule.
* @tag: string for error messages (example: MODULE.ARGNAME).
*/
static inline
void d_parse_params(struct d_level *d_level, size_t d_level_size,
const char *_params, const char *tag)
{
char submodule[130], *params, *params_orig, *token, *colon;
unsigned level, tokens;
if (_params == NULL)
return;
params_orig = kstrdup(_params, GFP_KERNEL);
params = params_orig;
while (1) {
token = strsep(&params, " ");
if (token == NULL)
break;
if (*token == '\0') /* eat joint spaces */
continue;
/* kernel's sscanf %s eats until whitespace, so we
* replace : by \n so it doesn't get eaten later by
* strsep */
colon = strchr(token, ':');
if (colon != NULL)
*colon = '\n';
tokens = sscanf(token, "%s\n%u", submodule, &level);
if (colon != NULL)
*colon = ':'; /* set back, for error messages */
if (tokens == 2)
d_submodule_set(d_level, d_level_size,
submodule, level, tag);
else
printk(KERN_ERR "%s: can't parse '%s' as a "
"SUBMODULE:LEVEL (%d tokens)\n",
tag, token, tokens);
}
kfree(params_orig);
}
#endif /* #ifndef __debug__h__ */

View File

@ -1,239 +0,0 @@
/*
* Linux WiMax
* API for user space
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
*
* This file declares the user/kernel protocol that is spoken over
* Generic Netlink, as well as any type declaration that is to be used
* by kernel and user space.
*
* It is intended for user space to clone it verbatim to use it as a
* primary reference for definitions.
*
* Stuff intended for kernel usage as well as full protocol and stack
* documentation is rooted in include/net/wimax.h.
*/
#ifndef __LINUX__WIMAX_H__
#define __LINUX__WIMAX_H__
#include <linux/types.h>
enum {
/**
* Version of the interface (unsigned decimal, MMm, max 25.5)
* M - Major: change if removing or modifying an existing call.
* m - minor: change when adding a new call
*/
WIMAX_GNL_VERSION = 01,
/* Generic NetLink attributes */
WIMAX_GNL_ATTR_INVALID = 0x00,
WIMAX_GNL_ATTR_MAX = 10,
};
/*
* Generic NetLink operations
*
* Most of these map to an API call; _OP_ stands for operation, _RP_
* for reply and _RE_ for report (aka: signal).
*/
enum {
WIMAX_GNL_OP_MSG_FROM_USER, /* User to kernel message */
WIMAX_GNL_OP_MSG_TO_USER, /* Kernel to user message */
WIMAX_GNL_OP_RFKILL, /* Run wimax_rfkill() */
WIMAX_GNL_OP_RESET, /* Run wimax_rfkill() */
WIMAX_GNL_RE_STATE_CHANGE, /* Report: status change */
WIMAX_GNL_OP_STATE_GET, /* Request for current state */
};
/* Message from user / to user */
enum {
WIMAX_GNL_MSG_IFIDX = 1,
WIMAX_GNL_MSG_PIPE_NAME,
WIMAX_GNL_MSG_DATA,
};
/*
* wimax_rfkill()
*
* The state of the radio (ON/OFF) is mapped to the rfkill subsystem's
* switch state (DISABLED/ENABLED).
*/
enum wimax_rf_state {
WIMAX_RF_OFF = 0, /* Radio is off, rfkill on/enabled */
WIMAX_RF_ON = 1, /* Radio is on, rfkill off/disabled */
WIMAX_RF_QUERY = 2,
};
/* Attributes */
enum {
WIMAX_GNL_RFKILL_IFIDX = 1,
WIMAX_GNL_RFKILL_STATE,
};
/* Attributes for wimax_reset() */
enum {
WIMAX_GNL_RESET_IFIDX = 1,
};
/* Attributes for wimax_state_get() */
enum {
WIMAX_GNL_STGET_IFIDX = 1,
};
/*
* Attributes for the Report State Change
*
* For now we just have the old and new states; new attributes might
* be added later on.
*/
enum {
WIMAX_GNL_STCH_IFIDX = 1,
WIMAX_GNL_STCH_STATE_OLD,
WIMAX_GNL_STCH_STATE_NEW,
};
/**
* enum wimax_st - The different states of a WiMAX device
* @__WIMAX_ST_NULL: The device structure has been allocated and zeroed,
* but still wimax_dev_add() hasn't been called. There is no state.
*
* @WIMAX_ST_DOWN: The device has been registered with the WiMAX and
* networking stacks, but it is not initialized (normally that is
* done with 'ifconfig DEV up' [or equivalent], which can upload
* firmware and enable communications with the device).
* In this state, the device is powered down and using as less
* power as possible.
* This state is the default after a call to wimax_dev_add(). It
* is ok to have drivers move directly to %WIMAX_ST_UNINITIALIZED
* or %WIMAX_ST_RADIO_OFF in _probe() after the call to
* wimax_dev_add().
* It is recommended that the driver leaves this state when
* calling 'ifconfig DEV up' and enters it back on 'ifconfig DEV
* down'.
*
* @__WIMAX_ST_QUIESCING: The device is being torn down, so no API
* operations are allowed to proceed except the ones needed to
* complete the device clean up process.
*
* @WIMAX_ST_UNINITIALIZED: [optional] Communication with the device
* is setup, but the device still requires some configuration
* before being operational.
* Some WiMAX API calls might work.
*
* @WIMAX_ST_RADIO_OFF: The device is fully up; radio is off (wether
* by hardware or software switches).
* It is recommended to always leave the device in this state
* after initialization.
*
* @WIMAX_ST_READY: The device is fully up and radio is on.
*
* @WIMAX_ST_SCANNING: [optional] The device has been instructed to
* scan. In this state, the device cannot be actively connected to
* a network.
*
* @WIMAX_ST_CONNECTING: The device is connecting to a network. This
* state exists because in some devices, the connect process can
* include a number of negotiations between user space, kernel
* space and the device. User space needs to know what the device
* is doing. If the connect sequence in a device is atomic and
* fast, the device can transition directly to CONNECTED
*
* @WIMAX_ST_CONNECTED: The device is connected to a network.
*
* @__WIMAX_ST_INVALID: This is an invalid state used to mark the
* maximum numeric value of states.
*
* Description:
*
* Transitions from one state to another one are atomic and can only
* be caused in kernel space with wimax_state_change(). To read the
* state, use wimax_state_get().
*
* States starting with __ are internal and shall not be used or
* referred to by drivers or userspace. They look ugly, but that's the
* point -- if any use is made non-internal to the stack, it is easier
* to catch on review.
*
* All API operations [with well defined exceptions] will take the
* device mutex before starting and then check the state. If the state
* is %__WIMAX_ST_NULL, %WIMAX_ST_DOWN, %WIMAX_ST_UNINITIALIZED or
* %__WIMAX_ST_QUIESCING, it will drop the lock and quit with
* -%EINVAL, -%ENOMEDIUM, -%ENOTCONN or -%ESHUTDOWN.
*
* The order of the definitions is important, so we can do numerical
* comparisons (eg: < %WIMAX_ST_RADIO_OFF means the device is not ready
* to operate).
*/
/*
* The allowed state transitions are described in the table below
* (states in rows can go to states in columns where there is an X):
*
* UNINI RADIO READY SCAN CONNEC CONNEC
* NULL DOWN QUIESCING TIALIZED OFF NING TING TED
* NULL - x
* DOWN - x x x
* QUIESCING x -
* UNINITIALIZED x - x
* RADIO_OFF x - x
* READY x x - x x x
* SCANNING x x x - x x
* CONNECTING x x x x - x
* CONNECTED x x x -
*
* This table not available in kernel-doc because the formatting messes it up.
*/
enum wimax_st {
__WIMAX_ST_NULL = 0,
WIMAX_ST_DOWN,
__WIMAX_ST_QUIESCING,
WIMAX_ST_UNINITIALIZED,
WIMAX_ST_RADIO_OFF,
WIMAX_ST_READY,
WIMAX_ST_SCANNING,
WIMAX_ST_CONNECTING,
WIMAX_ST_CONNECTED,
__WIMAX_ST_INVALID /* Always keep last */
};
#endif /* #ifndef __LINUX__WIMAX_H__ */

View File

@ -1,503 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Linux WiMAX
* Kernel space API for accessing WiMAX devices
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* The WiMAX stack provides an API for controlling and managing the
* system's WiMAX devices. This API affects the control plane; the
* data plane is accessed via the network stack (netdev).
*
* Parts of the WiMAX stack API and notifications are exported to
* user space via Generic Netlink. In user space, libwimax (part of
* the wimax-tools package) provides a shim layer for accessing those
* calls.
*
* The API is standarized for all WiMAX devices and different drivers
* implement the backend support for it. However, device-specific
* messaging pipes are provided that can be used to issue commands and
* receive notifications in free form.
*
* Currently the messaging pipes are the only means of control as it
* is not known (due to the lack of more devices in the market) what
* will be a good abstraction layer. Expect this to change as more
* devices show in the market. This API is designed to be growable in
* order to address this problem.
*
* USAGE
*
* Embed a `struct wimax_dev` at the beginning of the device's
* private structure, initialize and register it. For details, see
* `struct wimax_dev`s documentation.
*
* Once this is done, wimax-tools's libwimaxll can be used to
* communicate with the driver from user space. You user space
* application does not have to forcibily use libwimaxll and can talk
* the generic netlink protocol directly if desired.
*
* Remember this is a very low level API that will to provide all of
* WiMAX features. Other daemons and services running in user space
* are the expected clients of it. They offer a higher level API that
* applications should use (an example of this is the Intel's WiMAX
* Network Service for the i2400m).
*
* DESIGN
*
* Although not set on final stone, this very basic interface is
* mostly completed. Remember this is meant to grow as new common
* operations are decided upon. New operations will be added to the
* interface, intent being on keeping backwards compatibility as much
* as possible.
*
* This layer implements a set of calls to control a WiMAX device,
* exposing a frontend to the rest of the kernel and user space (via
* generic netlink) and a backend implementation in the driver through
* function pointers.
*
* WiMAX devices have a state, and a kernel-only API allows the
* drivers to manipulate that state. State transitions are atomic, and
* only some of them are allowed (see `enum wimax_st`).
*
* Most API calls will set the state automatically; in most cases
* drivers have to only report state changes due to external
* conditions.
*
* All API operations are 'atomic', serialized through a mutex in the
* `struct wimax_dev`.
*
* EXPORTING TO USER SPACE THROUGH GENERIC NETLINK
*
* The API is exported to user space using generic netlink (other
* methods can be added as needed).
*
* There is a Generic Netlink Family named "WiMAX", where interfaces
* supporting the WiMAX interface receive commands and broadcast their
* signals over a multicast group named "msg".
*
* Mapping to the source/destination interface is done by an interface
* index attribute.
*
* For user-to-kernel traffic (commands) we use a function call
* marshalling mechanism, where a message X with attributes A, B, C
* sent from user space to kernel space means executing the WiMAX API
* call wimax_X(A, B, C), sending the results back as a message.
*
* Kernel-to-user (notifications or signals) communication is sent
* over multicast groups. This allows to have multiple applications
* monitoring them.
*
* Each command/signal gets assigned it's own attribute policy. This
* way the validator will verify that all the attributes in there are
* only the ones that should be for each command/signal. Thing of an
* attribute mapping to a type+argumentname for each command/signal.
*
* If we had a single policy for *all* commands/signals, after running
* the validator we'd have to check "does this attribute belong in
* here"? for each one. It can be done manually, but it's just easier
* to have the validator do that job with multiple policies. As well,
* it makes it easier to later expand each command/signal signature
* without affecting others and keeping the namespace more or less
* sane. Not that it is too complicated, but it makes it even easier.
*
* No state information is maintained in the kernel for each user
* space connection (the connection is stateless).
*
* TESTING FOR THE INTERFACE AND VERSIONING
*
* If network interface X is a WiMAX device, there will be a Generic
* Netlink family named "WiMAX X" and the device will present a
* "wimax" directory in it's network sysfs directory
* (/sys/class/net/DEVICE/wimax) [used by HAL].
*
* The inexistence of any of these means the device does not support
* this WiMAX API.
*
* By querying the generic netlink controller, versioning information
* and the multicast groups available can be found. Applications using
* the interface can either rely on that or use the generic netlink
* controller to figure out which generic netlink commands/signals are
* supported.
*
* NOTE: this versioning is a last resort to avoid hard
* incompatibilities. It is the intention of the design of this
* stack not to introduce backward incompatible changes.
*
* The version code has to fit in one byte (restrictions imposed by
* generic netlink); we use `version / 10` for the major version and
* `version % 10` for the minor. This gives 9 minors for each major
* and 25 majors.
*
* The version change protocol is as follow:
*
* - Major versions: needs to be increased if an existing message/API
* call is changed or removed. Doesn't need to be changed if a new
* message is added.
*
* - Minor version: needs to be increased if new messages/API calls are
* being added or some other consideration that doesn't impact the
* user-kernel interface too much (like some kind of bug fix) and
* that is kind of left up in the air to common sense.
*
* User space code should not try to work if the major version it was
* compiled for differs from what the kernel offers. As well, if the
* minor version of the kernel interface is lower than the one user
* space is expecting (the one it was compiled for), the kernel
* might be missing API calls; user space shall be ready to handle
* said condition. Use the generic netlink controller operations to
* find which ones are supported and which not.
*
* libwimaxll:wimaxll_open() takes care of checking versions.
*
* THE OPERATIONS:
*
* Each operation is defined in its on file (drivers/net/wimax/op-*.c)
* for clarity. The parts needed for an operation are:
*
* - a function pointer in `struct wimax_dev`: optional, as the
* operation might be implemented by the stack and not by the
* driver.
*
* All function pointers are named wimax_dev->op_*(), and drivers
* must implement them except where noted otherwise.
*
* - When exported to user space, a `struct nla_policy` to define the
* attributes of the generic netlink command and a `struct genl_ops`
* to define the operation.
*
* All the declarations for the operation codes (WIMAX_GNL_OP_<NAME>)
* and generic netlink attributes (WIMAX_GNL_<NAME>_*) are declared in
* include/linux/wimax.h; this file is intended to be cloned by user
* space to gain access to those declarations.
*
* A few caveats to remember:
*
* - Need to define attribute numbers starting in 1; otherwise it
* fails.
*
* - the `struct genl_family` requires a maximum attribute id; when
* defining the `struct nla_policy` for each message, it has to have
* an array size of WIMAX_GNL_ATTR_MAX+1.
*
* The op_*() function pointers will not be called if the wimax_dev is
* in a state <= %WIMAX_ST_UNINITIALIZED. The exception is:
*
* - op_reset: can be called at any time after wimax_dev_add() has
* been called.
*
* THE PIPE INTERFACE:
*
* This interface is kept intentionally simple. The driver can send
* and receive free-form messages to/from user space through a
* pipe. See drivers/net/wimax/op-msg.c for details.
*
* The kernel-to-user messages are sent with
* wimax_msg(). user-to-kernel messages are delivered via
* wimax_dev->op_msg_from_user().
*
* RFKILL:
*
* RFKILL support is built into the wimax_dev layer; the driver just
* needs to call wimax_report_rfkill_{hw,sw}() to inform of changes in
* the hardware or software RF kill switches. When the stack wants to
* turn the radio off, it will call wimax_dev->op_rfkill_sw_toggle(),
* which the driver implements.
*
* User space can set the software RF Kill switch by calling
* wimax_rfkill().
*
* The code for now only supports devices that don't require polling;
* If the device needs to be polled, create a self-rearming delayed
* work struct for polling or look into adding polled support to the
* WiMAX stack.
*
* When initializing the hardware (_probe), after calling
* wimax_dev_add(), query the device for it's RF Kill switches status
* and feed it back to the WiMAX stack using
* wimax_report_rfkill_{hw,sw}(). If any switch is missing, always
* report it as ON.
*
* NOTE: the wimax stack uses an inverted terminology to that of the
* RFKILL subsystem:
*
* - ON: radio is ON, RFKILL is DISABLED or OFF.
* - OFF: radio is OFF, RFKILL is ENABLED or ON.
*
* MISCELLANEOUS OPS:
*
* wimax_reset() can be used to reset the device to power on state; by
* default it issues a warm reset that maintains the same device
* node. If that is not possible, it falls back to a cold reset
* (device reconnect). The driver implements the backend to this
* through wimax_dev->op_reset().
*/
#ifndef __NET__WIMAX_H__
#define __NET__WIMAX_H__
#include "linux-wimax.h"
#include <net/genetlink.h>
#include <linux/netdevice.h>
struct net_device;
struct genl_info;
struct wimax_dev;
/**
* struct wimax_dev - Generic WiMAX device
*
* @net_dev: [fill] Pointer to the &struct net_device this WiMAX
* device implements.
*
* @op_msg_from_user: [fill] Driver-specific operation to
* handle a raw message from user space to the driver. The
* driver can send messages to user space using with
* wimax_msg_to_user().
*
* @op_rfkill_sw_toggle: [fill] Driver-specific operation to act on
* userspace (or any other agent) requesting the WiMAX device to
* change the RF Kill software switch (WIMAX_RF_ON or
* WIMAX_RF_OFF).
* If such hardware support is not present, it is assumed the
* radio cannot be switched off and it is always on (and the stack
* will error out when trying to switch it off). In such case,
* this function pointer can be left as NULL.
*
* @op_reset: [fill] Driver specific operation to reset the
* device.
* This operation should always attempt first a warm reset that
* does not disconnect the device from the bus and return 0.
* If that fails, it should resort to some sort of cold or bus
* reset (even if it implies a bus disconnection and device
* disappearance). In that case, -ENODEV should be returned to
* indicate the device is gone.
* This operation has to be synchronous, and return only when the
* reset is complete. In case of having had to resort to bus/cold
* reset implying a device disconnection, the call is allowed to
* return immediately.
* NOTE: wimax_dev->mutex is NOT locked when this op is being
* called; however, wimax_dev->mutex_reset IS locked to ensure
* serialization of calls to wimax_reset().
* See wimax_reset()'s documentation.
*
* @name: [fill] A way to identify this device. We need to register a
* name with many subsystems (rfkill, workqueue creation, etc).
* We can't use the network device name as that
* might change and in some instances we don't know it yet (until
* we don't call register_netdev()). So we generate an unique one
* using the driver name and device bus id, place it here and use
* it across the board. Recommended naming:
* DRIVERNAME-BUSNAME:BUSID (dev->bus->name, dev->bus_id).
*
* @id_table_node: [private] link to the list of wimax devices kept by
* id-table.c. Protected by it's own spinlock.
*
* @mutex: [private] Serializes all concurrent access and execution of
* operations.
*
* @mutex_reset: [private] Serializes reset operations. Needs to be a
* different mutex because as part of the reset operation, the
* driver has to call back into the stack to do things such as
* state change, that require wimax_dev->mutex.
*
* @state: [private] Current state of the WiMAX device.
*
* @rfkill: [private] integration into the RF-Kill infrastructure.
*
* @rf_sw: [private] State of the software radio switch (OFF/ON)
*
* @rf_hw: [private] State of the hardware radio switch (OFF/ON)
*
* @debugfs_dentry: [private] Used to hook up a debugfs entry. This
* shows up in the debugfs root as wimax\:DEVICENAME.
*
* Description:
* This structure defines a common interface to access all WiMAX
* devices from different vendors and provides a common API as well as
* a free-form device-specific messaging channel.
*
* Usage:
* 1. Embed a &struct wimax_dev at *the beginning* the network
* device structure so that netdev_priv() points to it.
*
* 2. memset() it to zero
*
* 3. Initialize with wimax_dev_init(). This will leave the WiMAX
* device in the %__WIMAX_ST_NULL state.
*
* 4. Fill all the fields marked with [fill]; once called
* wimax_dev_add(), those fields CANNOT be modified.
*
* 5. Call wimax_dev_add() *after* registering the network
* device. This will leave the WiMAX device in the %WIMAX_ST_DOWN
* state.
* Protect the driver's net_device->open() against succeeding if
* the wimax device state is lower than %WIMAX_ST_DOWN.
*
* 6. Select when the device is going to be turned on/initialized;
* for example, it could be initialized on 'ifconfig up' (when the
* netdev op 'open()' is called on the driver).
*
* When the device is initialized (at `ifconfig up` time, or right
* after calling wimax_dev_add() from _probe(), make sure the
* following steps are taken
*
* a. Move the device to %WIMAX_ST_UNINITIALIZED. This is needed so
* some API calls that shouldn't work until the device is ready
* can be blocked.
*
* b. Initialize the device. Make sure to turn the SW radio switch
* off and move the device to state %WIMAX_ST_RADIO_OFF when
* done. When just initialized, a device should be left in RADIO
* OFF state until user space devices to turn it on.
*
* c. Query the device for the state of the hardware rfkill switch
* and call wimax_rfkill_report_hw() and wimax_rfkill_report_sw()
* as needed. See below.
*
* wimax_dev_rm() undoes before unregistering the network device. Once
* wimax_dev_add() is called, the driver can get called on the
* wimax_dev->op_* function pointers
*
* CONCURRENCY:
*
* The stack provides a mutex for each device that will disallow API
* calls happening concurrently; thus, op calls into the driver
* through the wimax_dev->op*() function pointers will always be
* serialized and *never* concurrent.
*
* For locking, take wimax_dev->mutex is taken; (most) operations in
* the API have to check for wimax_dev_is_ready() to return 0 before
* continuing (this is done internally).
*
* REFERENCE COUNTING:
*
* The WiMAX device is reference counted by the associated network
* device. The only operation that can be used to reference the device
* is wimax_dev_get_by_genl_info(), and the reference it acquires has
* to be released with dev_put(wimax_dev->net_dev).
*
* RFKILL:
*
* At startup, both HW and SW radio switchess are assumed to be off.
*
* At initialization time [after calling wimax_dev_add()], have the
* driver query the device for the status of the software and hardware
* RF kill switches and call wimax_report_rfkill_hw() and
* wimax_rfkill_report_sw() to indicate their state. If any is
* missing, just call it to indicate it is ON (radio always on).
*
* Whenever the driver detects a change in the state of the RF kill
* switches, it should call wimax_report_rfkill_hw() or
* wimax_report_rfkill_sw() to report it to the stack.
*/
struct wimax_dev {
struct net_device *net_dev;
struct list_head id_table_node;
struct mutex mutex; /* Protects all members and API calls */
struct mutex mutex_reset;
enum wimax_st state;
int (*op_msg_from_user)(struct wimax_dev *wimax_dev,
const char *,
const void *, size_t,
const struct genl_info *info);
int (*op_rfkill_sw_toggle)(struct wimax_dev *wimax_dev,
enum wimax_rf_state);
int (*op_reset)(struct wimax_dev *wimax_dev);
struct rfkill *rfkill;
unsigned int rf_hw;
unsigned int rf_sw;
char name[32];
struct dentry *debugfs_dentry;
};
/*
* WiMAX stack public API for device drivers
* -----------------------------------------
*
* These functions are not exported to user space.
*/
void wimax_dev_init(struct wimax_dev *);
int wimax_dev_add(struct wimax_dev *, struct net_device *);
void wimax_dev_rm(struct wimax_dev *);
static inline
struct wimax_dev *net_dev_to_wimax(struct net_device *net_dev)
{
return netdev_priv(net_dev);
}
static inline
struct device *wimax_dev_to_dev(struct wimax_dev *wimax_dev)
{
return wimax_dev->net_dev->dev.parent;
}
void wimax_state_change(struct wimax_dev *, enum wimax_st);
enum wimax_st wimax_state_get(struct wimax_dev *);
/*
* Radio Switch state reporting.
*
* enum wimax_rf_state is declared in linux/wimax.h so the exports
* to user space can use it.
*/
void wimax_report_rfkill_hw(struct wimax_dev *, enum wimax_rf_state);
void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state);
/*
* Free-form messaging to/from user space
*
* Sending a message:
*
* wimax_msg(wimax_dev, pipe_name, buf, buf_size, GFP_KERNEL);
*
* Broken up:
*
* skb = wimax_msg_alloc(wimax_dev, pipe_name, buf_size, GFP_KERNEL);
* ...fill up skb...
* wimax_msg_send(wimax_dev, pipe_name, skb);
*
* Be sure not to modify skb->data in the middle (ie: don't use
* skb_push()/skb_pull()/skb_reserve() on the skb).
*
* "pipe_name" is any string, that can be interpreted as the name of
* the pipe or recipient; the interpretation of it is driver
* specific, so the recipient can multiplex it as wished. It can be
* NULL, it won't be used - an example is using a "diagnostics" tag to
* send diagnostics information that a device-specific diagnostics
* tool would be interested in.
*/
struct sk_buff *wimax_msg_alloc(struct wimax_dev *, const char *, const void *,
size_t, gfp_t);
int wimax_msg_send(struct wimax_dev *, struct sk_buff *);
int wimax_msg(struct wimax_dev *, const char *, const void *, size_t, gfp_t);
const void *wimax_msg_data_len(struct sk_buff *, size_t *);
const void *wimax_msg_data(struct sk_buff *);
ssize_t wimax_msg_len(struct sk_buff *);
/*
* WiMAX stack user space API
* --------------------------
*
* This API is what gets exported to user space for general
* operations. As well, they can be called from within the kernel,
* (with a properly referenced `struct wimax_dev`).
*
* Properly referenced means: the 'struct net_device' that embeds the
* device's control structure and (as such) the 'struct wimax_dev' is
* referenced by the caller.
*/
int wimax_rfkill(struct wimax_dev *, enum wimax_rf_state);
int wimax_reset(struct wimax_dev *);
#endif /* #ifndef __NET__WIMAX_H__ */

View File

@ -1,391 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 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;
}

View File

@ -1,108 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 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;
}

View File

@ -1,432 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Linux WiMAX
* RF-kill framework integration
*
* Copyright (C) 2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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 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;
}

View File

@ -1,52 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*/
#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;
}

View File

@ -1,604 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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 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.");
/*
* 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);
/*
* There are multiple enums reusing the same values, adding
* others is only possible if they use a compatible policy.
*/
static const struct nla_policy wimax_gnl_policy[WIMAX_GNL_ATTR_MAX + 1] = {
/*
* WIMAX_GNL_RESET_IFIDX, WIMAX_GNL_RFKILL_IFIDX,
* WIMAX_GNL_STGET_IFIDX, WIMAX_GNL_MSG_IFIDX
*/
[1] = { .type = NLA_U32, },
/*
* WIMAX_GNL_RFKILL_STATE, WIMAX_GNL_MSG_PIPE_NAME
*/
[2] = { .type = NLA_U32, }, /* enum wimax_rf_state */
/*
* WIMAX_GNL_MSG_DATA
*/
[3] = { .type = NLA_UNSPEC, }, /* libnl doesn't grok BINARY yet */
};
static const struct genl_small_ops wimax_gnl_ops[] = {
{
.cmd = WIMAX_GNL_OP_MSG_FROM_USER,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.flags = GENL_ADMIN_PERM,
.doit = wimax_gnl_doit_msg_from_user,
},
{
.cmd = WIMAX_GNL_OP_RESET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.flags = GENL_ADMIN_PERM,
.doit = wimax_gnl_doit_reset,
},
{
.cmd = WIMAX_GNL_OP_RFKILL,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.flags = GENL_ADMIN_PERM,
.doit = wimax_gnl_doit_rfkill,
},
{
.cmd = WIMAX_GNL_OP_STATE_GET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.flags = GENL_ADMIN_PERM,
.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);
wimax_debugfs_add(wimax_dev);
__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_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);
static const struct genl_multicast_group wimax_gnl_mcgrps[] = {
{ .name = "msg", },
};
struct genl_family wimax_gnl_family __ro_after_init = {
.name = "WiMAX",
.version = WIMAX_GNL_VERSION,
.hdrsize = 0,
.maxattr = WIMAX_GNL_ATTR_MAX,
.policy = wimax_gnl_policy,
.module = THIS_MODULE,
.small_ops = wimax_gnl_ops,
.n_small_ops = ARRAY_SIZE(wimax_gnl_ops),
.mcgrps = wimax_gnl_mcgrps,
.n_mcgrps = ARRAY_SIZE(wimax_gnl_mcgrps),
};
/* 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");
result = genl_register_family(&wimax_gnl_family);
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");

View File

@ -1,85 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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 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
void wimax_debugfs_add(struct wimax_dev *);
void wimax_debugfs_rm(struct wimax_dev *);
#else
static inline void wimax_debugfs_add(struct wimax_dev *wimax_dev) {}
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__ */