diff options
author | David S. Miller <davem@davemloft.net> | 2017-04-17 15:00:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-04-17 15:00:57 -0400 |
commit | 450cc8cce23d0db11b19fa89973ddb6d5edd010c (patch) | |
tree | be70a3831b21b77f17e236bb452a2ced6066bf96 /drivers/net | |
parent | d584fec634a4426ad476657383d3a9049565f884 (diff) | |
parent | 019aa56b7df8a796b2c01a56269a370ad3442ec7 (diff) |
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says:
====================
pull request: bluetooth-next 2017-04-14
Here's the main batch of Bluetooth & 802.15.4 patches for the 4.12
kernel.
- Many fixes to 6LoWPAN, in particular for BLE
- New CA8210 IEEE 802.15.4 device driver (accounting for most of the
lines of code added in this pull request)
- Added Nokia Bluetooth (UART) HCI driver
- Some serdev & TTY changes that are dependencies for the Nokia
driver (with acks from relevant maintainers and an agreement that
these come through the bluetooth tree)
- Support for new Intel Bluetooth device
- Various other minor cleanups/fixes here and there
Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ieee802154/Kconfig | 22 | ||||
-rw-r--r-- | drivers/net/ieee802154/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ieee802154/ca8210.c | 3242 |
3 files changed, 3265 insertions, 0 deletions
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 3057a8df4ce9..ce4864dc3c6e 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -82,3 +82,25 @@ config IEEE802154_ADF7242 This driver can also be built as a module. To do so, say M here. the module will be called 'adf7242'. + +config IEEE802154_CA8210 + tristate "Cascoda CA8210 transceiver driver" + depends on IEEE802154_DRIVERS && MAC802154 + depends on SPI + select COMMON_CLK + ---help--- + Say Y here to enable the CA8210 SPI 802.15.4 wireless + controller. + + This driver can also be built as a module. To do so, say M here. + the module will be called 'ca8210'. + +config IEEE802154_CA8210_DEBUGFS + bool "CA8210 debugfs interface" + depends on IEEE802154_CA8210 + depends on DEBUG_FS + ---help--- + This option compiles debugfs code for the ca8210 driver. This + exposes a debugfs node for each CA8210 instance which allows + direct use of the Cascoda API, exposing the 802.15.4 MAC + management entities. diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index 3a923d339497..8374bb44a145 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o +obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c new file mode 100644 index 000000000000..25fd3b04b3c0 --- /dev/null +++ b/drivers/net/ieee802154/ca8210.c @@ -0,0 +1,3242 @@ +/* + * http://www.cascoda.com/products/ca-821x/ + * Copyright (c) 2016, Cascoda, Ltd. + * All rights reserved. + * + * This code is dual-licensed under both GPLv2 and 3-clause BSD. What follows is + * the license notice for both respectively. + * + ******************************************************************************* + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ******************************************************************************* + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDERS 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. + */ + +#include <linux/cdev.h> +#include <linux/clk-provider.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/ieee802154.h> +#include <linux/kfifo.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/workqueue.h> + +#include <net/ieee802154_netdev.h> +#include <net/mac802154.h> + +#define DRIVER_NAME "ca8210" + +/* external clock frequencies */ +#define ONE_MHZ 1000000 +#define TWO_MHZ (2 * ONE_MHZ) +#define FOUR_MHZ (4 * ONE_MHZ) +#define EIGHT_MHZ (8 * ONE_MHZ) +#define SIXTEEN_MHZ (16 * ONE_MHZ) + +/* spi constants */ +#define CA8210_SPI_BUF_SIZE 256 +#define CA8210_SYNC_TIMEOUT 1000 /* Timeout for synchronous commands [ms] */ + +/* test interface constants */ +#define CA8210_TEST_INT_FILE_NAME "ca8210_test" +#define CA8210_TEST_INT_FIFO_SIZE 256 + +/* MAC status enumerations */ +#define MAC_SUCCESS (0x00) +#define MAC_ERROR (0x01) +#define MAC_CANCELLED (0x02) +#define MAC_READY_FOR_POLL (0x03) +#define MAC_COUNTER_ERROR (0xDB) +#define MAC_IMPROPER_KEY_TYPE (0xDC) +#define MAC_IMPROPER_SECURITY_LEVEL (0xDD) +#define MAC_UNSUPPORTED_LEGACY (0xDE) +#define MAC_UNSUPPORTED_SECURITY (0xDF) +#define MAC_BEACON_LOST (0xE0) +#define MAC_CHANNEL_ACCESS_FAILURE (0xE1) +#define MAC_DENIED (0xE2) +#define MAC_DISABLE_TRX_FAILURE (0xE3) +#define MAC_SECURITY_ERROR (0xE4) +#define MAC_FRAME_TOO_LONG (0xE5) +#define MAC_INVALID_GTS (0xE6) +#define MAC_INVALID_HANDLE (0xE7) +#define MAC_INVALID_PARAMETER (0xE8) +#define MAC_NO_ACK (0xE9) +#define MAC_NO_BEACON (0xEA) +#define MAC_NO_DATA (0xEB) +#define MAC_NO_SHORT_ADDRESS (0xEC) +#define MAC_OUT_OF_CAP (0xED) +#define MAC_PAN_ID_CONFLICT (0xEE) +#define MAC_REALIGNMENT (0xEF) +#define MAC_TRANSACTION_EXPIRED (0xF0) +#define MAC_TRANSACTION_OVERFLOW (0xF1) +#define MAC_TX_ACTIVE (0xF2) +#define MAC_UNAVAILABLE_KEY (0xF3) +#define MAC_UNSUPPORTED_ATTRIBUTE (0xF4) +#define MAC_INVALID_ADDRESS (0xF5) +#define MAC_ON_TIME_TOO_LONG (0xF6) +#define MAC_PAST_TIME (0xF7) +#define MAC_TRACKING_OFF (0xF8) +#define MAC_INVALID_INDEX (0xF9) +#define MAC_LIMIT_REACHED (0xFA) +#define MAC_READ_ONLY (0xFB) +#define MAC_SCAN_IN_PROGRESS (0xFC) +#define MAC_SUPERFRAME_OVERLAP (0xFD) +#define MAC_SYSTEM_ERROR (0xFF) + +/* HWME attribute IDs */ +#define HWME_EDTHRESHOLD (0x04) +#define HWME_EDVALUE (0x06) +#define HWME_SYSCLKOUT (0x0F) +#define HWME_LQILIMIT (0x11) + +/* TDME attribute IDs */ +#define TDME_CHANNEL (0x00) +#define TDME_ATM_CONFIG (0x06) + +#define MAX_HWME_ATTRIBUTE_SIZE 16 +#define MAX_TDME_ATTRIBUTE_SIZE 2 + +/* PHY/MAC PIB Attribute Enumerations */ +#define PHY_CURRENT_CHANNEL (0x00) +#define PHY_TRANSMIT_POWER (0x02) +#define PHY_CCA_MODE (0x03) +#define MAC_ASSOCIATION_PERMIT (0x41) +#define MAC_AUTO_REQUEST (0x42) +#define MAC_BATT_LIFE_EXT (0x43) +#define MAC_BATT_LIFE_EXT_PERIODS (0x44) +#define MAC_BEACON_PAYLOAD (0x45) +#define MAC_BEACON_PAYLOAD_LENGTH (0x46) +#define MAC_BEACON_ORDER (0x47) +#define MAC_GTS_PERMIT (0x4d) +#define MAC_MAX_CSMA_BACKOFFS (0x4e) +#define MAC_MIN_BE (0x4f) +#define MAC_PAN_ID (0x50) +#define MAC_PROMISCUOUS_MODE (0x51) +#define MAC_RX_ON_WHEN_IDLE (0x52) +#define MAC_SHORT_ADDRESS (0x53) +#define MAC_SUPERFRAME_ORDER (0x54) +#define MAC_ASSOCIATED_PAN_COORD (0x56) +#define MAC_MAX_BE (0x57) +#define MAC_MAX_FRAME_RETRIES (0x59) +#define MAC_RESPONSE_WAIT_TIME (0x5A) +#define MAC_SECURITY_ENABLED (0x5D) + +#define MAC_AUTO_REQUEST_SECURITY_LEVEL (0x78) +#define MAC_AUTO_REQUEST_KEY_ID_MODE (0x79) + +#define NS_IEEE_ADDRESS (0xFF) /* Non-standard IEEE address */ + +/* MAC Address Mode Definitions */ +#define MAC_MODE_NO_ADDR (0x00) +#define MAC_MODE_SHORT_ADDR (0x02) +#define MAC_MODE_LONG_ADDR (0x03) + +/* MAC constants */ +#define MAX_BEACON_OVERHEAD (75) +#define MAX_BEACON_PAYLOAD_LENGTH (IEEE802154_MTU - MAX_BEACON_OVERHEAD) + +#define MAX_ATTRIBUTE_SIZE (122) +#define MAX_DATA_SIZE (114) + +#define CA8210_VALID_CHANNELS (0x07FFF800) + +/* MAC workarounds for V1.1 and MPW silicon (V0.x) */ +#define CA8210_MAC_WORKAROUNDS (0) +#define CA8210_MAC_MPW (0) + +/* memory manipulation macros */ +#define LS_BYTE(x) ((u8)((x) & 0xFF)) +#define MS_BYTE(x) ((u8)(((x) >> 8) & 0xFF)) + +/* message ID codes in SPI commands */ +/* downstream */ +#define MCPS_DATA_REQUEST (0x00) +#define MLME_ASSOCIATE_REQUEST (0x02) +#define MLME_ASSOCIATE_RESPONSE (0x03) +#define MLME_DISASSOCIATE_REQUEST (0x04) +#define MLME_GET_REQUEST (0x05) +#define MLME_ORPHAN_RESPONSE (0x06) +#define MLME_RESET_REQUEST (0x07) +#define MLME_RX_ENABLE_REQUEST (0x08) +#define MLME_SCAN_REQUEST (0x09) +#define MLME_SET_REQUEST (0x0A) +#define MLME_START_REQUEST (0x0B) +#define MLME_POLL_REQUEST (0x0D) +#define HWME_SET_REQUEST (0x0E) +#define HWME_GET_REQUEST (0x0F) +#define TDME_SETSFR_REQUEST (0x11) +#define TDME_GETSFR_REQUEST (0x12) +#define TDME_SET_REQUEST (0x14) +/* upstream */ +#define MCPS_DATA_INDICATION (0x00) +#define MCPS_DATA_CONFIRM (0x01) +#define MLME_RESET_CONFIRM (0x0A) +#define MLME_SET_CONFIRM (0x0E) +#define MLME_START_CONFIRM (0x0F) +#define HWME_SET_CONFIRM (0x12) +#define HWME_GET_CONFIRM (0x13) +#define HWME_WAKEUP_INDICATION (0x15) +#define TDME_SETSFR_CONFIRM (0x17) + +/* SPI command IDs */ +/* bit indicating a confirm or indication from slave to master */ +#define SPI_S2M (0x20) +/* bit indicating a synchronous message */ +#define SPI_SYN (0x40) + +/* SPI command definitions */ +#define SPI_IDLE (0xFF) +#define SPI_NACK (0xF0) + +#define SPI_MCPS_DATA_REQUEST (MCPS_DATA_REQUEST) +#define SPI_MCPS_DATA_INDICATION (MCPS_DATA_INDICATION + SPI_S2M) +#define SPI_MCPS_DATA_CONFIRM (MCPS_DATA_CONFIRM + SPI_S2M) + +#define SPI_MLME_ASSOCIATE_REQUEST (MLME_ASSOCIATE_REQUEST) +#define SPI_MLME_RESET_REQUEST (MLME_RESET_REQUEST + SPI_SYN) +#define SPI_MLME_SET_REQUEST (MLME_SET_REQUEST + SPI_SYN) +#define SPI_MLME_START_REQUEST (MLME_START_REQUEST + SPI_SYN) +#define SPI_MLME_RESET_CONFIRM (MLME_RESET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_MLME_SET_CONFIRM (MLME_SET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_MLME_START_CONFIRM (MLME_START_CONFIRM + SPI_S2M + SPI_SYN) + +#define SPI_HWME_SET_REQUEST (HWME_SET_REQUEST + SPI_SYN) +#define SPI_HWME_GET_REQUEST (HWME_GET_REQUEST + SPI_SYN) +#define SPI_HWME_SET_CONFIRM (HWME_SET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_HWME_GET_CONFIRM (HWME_GET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_HWME_WAKEUP_INDICATION (HWME_WAKEUP_INDICATION + SPI_S2M) + +#define SPI_TDME_SETSFR_REQUEST (TDME_SETSFR_REQUEST + SPI_SYN) +#define SPI_TDME_SET_REQUEST (TDME_SET_REQUEST + SPI_SYN) +#define SPI_TDME_SETSFR_CONFIRM (TDME_SETSFR_CONFIRM + SPI_S2M + SPI_SYN) + +/* TDME SFR addresses */ +/* Page 0 */ +#define CA8210_SFR_PACFG (0xB1) +#define CA8210_SFR_MACCON (0xD8) +#define CA8210_SFR_PACFGIB (0xFE) +/* Page 1 */ +#define CA8210_SFR_LOTXCAL (0xBF) +#define CA8210_SFR_PTHRH (0xD1) +#define CA8210_SFR_PRECFG (0xD3) +#define CA8210_SFR_LNAGX40 (0xE1) +#define CA8210_SFR_LNAGX41 (0xE2) +#define CA8210_SFR_LNAGX42 (0xE3) +#define CA8210_SFR_LNAGX43 (0xE4) +#define CA8210_SFR_LNAGX44 (0xE5) +#define CA8210_SFR_LNAGX45 (0xE6) +#define CA8210_SFR_LNAGX46 (0xE7) +#define CA8210_SFR_LNAGX47 (0xE9) + +#define PACFGIB_DEFAULT_CURRENT (0x3F) +#define PTHRH_DEFAULT_THRESHOLD (0x5A) +#define LNAGX40_DEFAULT_GAIN (0x29) /* 10dB */ +#define LNAGX41_DEFAULT_GAIN (0x54) /* 21dB */ +#define LNAGX42_DEFAULT_GAIN (0x6C) /* 27dB */ +#define LNAGX43_DEFAULT_GAIN (0x7A) /* 30dB */ +#define LNAGX44_DEFAULT_GAIN (0x84) /* 33dB */ +#define LNAGX45_DEFAULT_GAIN (0x8B) /* 34dB */ +#define LNAGX46_DEFAULT_GAIN (0x92) /* 36dB */ +#define LNAGX47_DEFAULT_GAIN (0x96) /* 37dB */ + +#define CA8210_IOCTL_HARD_RESET (0x00) + +/* Structs/Enums */ + +/** + * struct cas_control - spi transfer structure + * @msg: spi_message for each exchange + * @transfer: spi_transfer for each exchange + * @tx_buf: source array for transmission + * @tx_in_buf: array storing bytes received during transmission + * @priv: pointer to private data + * + * This structure stores all the necessary data passed around during a single + * spi exchange. + */ +struct cas_control { + struct spi_message msg; + struct spi_transfer transfer; + + u8 tx_buf[CA8210_SPI_BUF_SIZE]; + u8 tx_in_buf[CA8210_SPI_BUF_SIZE]; + + struct ca8210_priv *priv; +}; + +/** + * struct ca8210_test - ca8210 test interface structure + * @ca8210_dfs_spi_int: pointer to the entry in the debug fs for this device + * @up_fifo: fifo for upstream messages + * + * This structure stores all the data pertaining to the debug interface + */ +struct ca8210_test { + struct dentry *ca8210_dfs_spi_int; + struct kfifo up_fifo; + wait_queue_head_t readq; +}; + +/** + * struct ca8210_priv - ca8210 private data structure + * @spi: pointer to the ca8210 spi device object + * @hw: pointer to the ca8210 ieee802154_hw object + * @hw_registered: true if hw has been registered with ieee802154 + * @lock: spinlock protecting the private data area + * @mlme_workqueue: workqueue for triggering MLME Reset + * @irq_workqueue: workqueue for irq processing + * @tx_skb: current socket buffer to transmit + * @nextmsduhandle: msdu handle to pass to the 15.4 MAC layer for the + * next transmission + * @clk: external clock provided by the ca8210 + * @last_dsn: sequence number of last data packet received, for + * resend detection + * @test: test interface data section for this instance + * @async_tx_pending: true if an asynchronous transmission was started and + * is not complete + * @sync_command_response: pointer to buffer to fill with sync response + * @ca8210_is_awake: nonzero if ca8210 is initialised, ready for comms + * @sync_down: counts number of downstream synchronous commands + * @sync_up: counts number of upstream synchronous commands + * @spi_transfer_complete completion object for a single spi_transfer + * @sync_exchange_complete completion object for a complete synchronous API + * exchange + * @promiscuous whether the ca8210 is in promiscuous mode or not + * @retries: records how many times the current pending spi + * transfer has been retried + */ +struct ca8210_priv { + struct spi_device *spi; + struct ieee802154_hw *hw; + bool hw_registered; + spinlock_t lock; + struct workqueue_struct *mlme_workqueue; + struct workqueue_struct *irq_workqueue; + struct sk_buff *tx_skb; + u8 nextmsduhandle; + struct clk *clk; + int last_dsn; + struct ca8210_test test; + bool async_tx_pending; + u8 *sync_command_response; + struct completion ca8210_is_awake; + int sync_down, sync_up; + struct completion spi_transfer_complete, sync_exchange_complete; + bool promiscuous; + int retries; +}; + +/** + * struct work_priv_container - link between a work object and the relevant + * device's private data + * @work: work object being executed + * @priv: device's private data section + * + */ +struct work_priv_container { + struct work_struct work; + struct ca8210_priv *priv; +}; + +/** + * struct ca8210_platform_data - ca8210 platform data structure + * @extclockenable: true if the external clock is to be enabled + * @extclockfreq: frequency of the external clock + * @extclockgpio: ca8210 output gpio of the external clock + * @gpio_reset: gpio number of ca8210 reset line + * @gpio_irq: gpio number of ca8210 interrupt line + * @irq_id: identifier for the ca8210 irq + * + */ +struct ca8210_platform_data { + bool extclockenable; + unsigned int extclockfreq; + unsigned int extclockgpio; + int gpio_reset; + int gpio_irq; + int irq_id; +}; + +/** + * struct fulladdr - full MAC addressing information structure + * @mode: address mode (none, short, extended) + * @pan_id: 16-bit LE pan id + * @address: LE address, variable length as specified by mode + * + */ +struct fulladdr { + u8 mode; + u8 pan_id[2]; + u8 address[8]; +}; + +/** + * union macaddr: generic MAC address container + * @short_addr: 16-bit short address + * @ieee_address: 64-bit extended address as LE byte array + * + */ +union macaddr { + u16 short_address; + u8 ieee_address[8]; +}; + +/** + * struct secspec: security specification for SAP commands + * @security_level: 0-7, controls level of authentication & encryption + * @key_id_mode: 0-3, specifies how to obtain key + * @key_source: extended key retrieval data + * @key_index: single-byte key identifier + * + */ +struct secspec { + u8 security_level; + u8 key_id_mode; + u8 key_source[8]; + u8 key_index; +}; + +/* downlink functions parameter set definitions */ +struct mcps_data_request_pset { + u8 src_addr_mode; + struct fulladdr dst; + u8 msdu_length; + u8 msdu_handle; + u8 tx_options; + u8 msdu[MAX_DATA_SIZE]; +}; + +struct mlme_set_request_pset { + u8 pib_attribute; + u8 pib_attribute_index; + u8 pib_attribute_length; + u8 pib_attribute_value[MAX_ATTRIBUTE_SIZE]; +}; + +struct hwme_set_request_pset { + u8 hw_attribute; + u8 hw_attribute_length; + u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; +}; + +struct hwme_get_request_pset { + u8 hw_attribute; +}; + +struct tdme_setsfr_request_pset { + u8 sfr_page; + u8 sfr_address; + u8 sfr_value; +}; + +/* uplink functions parameter set definitions */ +struct hwme_set_confirm_pset { + u8 status; + u8 hw_attribute; +}; + +struct hwme_get_confirm_pset { + u8 status; + u8 hw_attribute; + u8 hw_attribute_length; + u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; +}; + +struct tdme_setsfr_confirm_pset { + u8 status; + u8 sfr_page; + u8 sfr_address; +}; + +struct mac_message { + u8 command_id; + u8 length; + union { + struct mcps_data_request_pset data_req; + struct mlme_set_request_pset set_req; + struct hwme_set_request_pset hwme_set_req; + struct hwme_get_request_pset hwme_get_req; + struct tdme_setsfr_request_pset tdme_set_sfr_req; + struct hwme_set_confirm_pset hwme_set_cnf; + struct hwme_get_confirm_pset hwme_get_cnf; + struct tdme_setsfr_confirm_pset tdme_set_sfr_cnf; + u8 u8param; + u8 status; + u8 payload[148]; + } pdata; +}; + +union pa_cfg_sfr { + struct { + u8 bias_current_trim : 3; + u8 /* reserved */ : 1; + u8 buffer_capacitor_trim : 3; + u8 boost : 1; + }; + u8 paib; +}; + +struct preamble_cfg_sfr { + u8 timeout_symbols : 3; + u8 acquisition_symbols : 3; + u8 search_symbols : 2; +}; + +static int (*cascoda_api_upstream)( + const u8 *buf, + size_t len, + void *device_ref +); + +/** + * link_to_linux_err() - Translates an 802.15.4 return code into the closest + * linux error + * @link_status: 802.15.4 status code + * + * Return: 0 or Linux error code + */ +static int link_to_linux_err(int link_status) +{ + if (link_status < 0) { + /* status is already a Linux code */ + return link_status; + } + switch (link_status) { + case MAC_SUCCESS: + case MAC_REALIGNMENT: + return 0; + case MAC_IMPROPER_KEY_TYPE: + return -EKEYREJECTED; + case MAC_IMPROPER_SECURITY_LEVEL: + case MAC_UNSUPPORTED_LEGACY: + case MAC_DENIED: + return -EACCES; + case MAC_BEACON_LOST: + case MAC_NO_ACK: + case MAC_NO_BEACON: + return -ENETUNREACH; + case MAC_CHANNEL_ACCESS_FAILURE: + case MAC_TX_ACTIVE: + case MAC_SCAN_IN_PROGRESS: + return -EBUSY; + case MAC_DISABLE_TRX_FAILURE: + case MAC_OUT_OF_CAP: + return -EAGAIN; + case MAC_FRAME_TOO_LONG: + return -EMSGSIZE; + case MAC_INVALID_GTS: + case MAC_PAST_TIME: + return -EBADSLT; + case MAC_INVALID_HANDLE: + return -EBADMSG; + case MAC_INVALID_PARAMETER: + case MAC_UNSUPPORTED_ATTRIBUTE: + case MAC_ON_TIME_TOO_LONG: + case MAC_INVALID_INDEX: + return -EINVAL; + case MAC_NO_DATA: + return -ENODATA; + case MAC_NO_SHORT_ADDRESS: + return -EFAULT; + case MAC_PAN_ID_CONFLICT: + return -EADDRINUSE; + case MAC_TRANSACTION_EXPIRED: + return -ETIME; + case MAC_TRANSACTION_OVERFLOW: + return -ENOBUFS; + case MAC_UNAVAILABLE_KEY: + return -ENOKEY; + case MAC_INVALID_ADDRESS: + return -ENXIO; + case MAC_TRACKING_OFF: + case MAC_SUPERFRAME_OVERLAP: + return -EREMOTEIO; + case MAC_LIMIT_REACHED: + return -EDQUOT; + case MAC_READ_ONLY: + return -EROFS; + default: + return -EPROTO; + } +} + +/** + * ca8210_test_int_driver_write() - Writes a message to the test interface to be + * read by the userspace + * @buf: Buffer containing upstream message + * @len: length of message to write + * @spi: SPI device of message originator + * + * Return: 0 or linux error code + */ +static int ca8210_test_int_driver_write( + const u8 *buf, + size_t len, + void *spi +) +{ + struct ca8210_priv *priv = spi_get_drvdata(spi); + struct ca8210_test *test = &priv->test; + char *fifo_buffer; + int i; + + dev_dbg( + &priv->spi->dev, + "test_interface: Buffering upstream message:\n" + ); + for (i = 0; i < len; i++) + dev_dbg(&priv->spi->dev, "%#03x\n", buf[i]); + + fifo_buffer = kmalloc(len, GFP_KERNEL); + if (!fifo_buffer) + return -ENOMEM; + memcpy(fifo_buffer, buf, len); + kfifo_in(&test->up_fifo, &fifo_buffer, 4); + wake_up_interruptible(&priv->test.readq); + + return 0; +} + +/* SPI Operation */ + +static int ca8210_net_rx( + struct ieee802154_hw *hw, + u8 *command, + size_t len +); +static u8 mlme_reset_request_sync( + u8 set_default_pib, + void *device_ref +); +static int ca8210_spi_transfer( + struct spi_device *spi, + const u8 *buf, + size_t len +); + +/** + * ca8210_reset_send() - Hard resets the ca8210 for a given time + * @spi: Pointer to target ca8210 spi device + * @ms: Milliseconds to hold the reset line low for + */ +static void ca8210_reset_send(struct spi_device *spi, unsigned int ms) +{ + struct ca8210_platform_data *pdata = spi->dev.platform_data; + struct ca8210_priv *priv = spi_get_drvdata(spi); + long status; + + gpio_set_value(pdata->gpio_reset, 0); + reinit_completion(&priv->ca8210_is_awake); + msleep(ms); + gpio_set_value(pdata->gpio_reset, 1); + priv->promiscuous = false; + + /* Wait until wakeup indication seen */ + status = wait_for_completion_interruptible_timeout( + &priv->ca8210_is_awake, + msecs_to_jiffies(CA8210_SYNC_TIMEOUT) + ); + if (status == 0) { + dev_crit( + &spi->dev, + "Fatal: No wakeup from ca8210 after reset!\n" + ); + } + + dev_dbg(&spi->dev, "Reset the device\n"); +} + +/** + * ca8210_mlme_reset_worker() - Resets the MLME, Called when the MAC OVERFLOW + * condition happens. + * @work: Pointer to work being executed + */ +static void ca8210_mlme_reset_worker(struct work_struct *work) +{ + struct work_priv_container *wpc = container_of( + work, + struct work_priv_container, + work + ); + struct ca8210_priv *priv = wpc->priv; + + mlme_reset_request_sync(0, priv->spi); + kfree(wpc); +} + +/** + * ca8210_rx_done() - Calls various message dispatches responding to a received + * command + * @arg: Pointer to the cas_control object for the relevant spi transfer + * + * Presents a received SAP command from the ca8210 to the Cascoda EVBME, test + * interface and network driver. + */ +static void ca8210_rx_done(struct cas_control *cas_ctl) +{ + u8 *buf; + u8 len; + struct work_priv_container *mlme_reset_wpc; + struct ca8210_priv *priv = cas_ctl->priv; + + buf = cas_ctl->tx_in_buf; + len = buf[1] + 2; + if (len > CA8210_SPI_BUF_SIZE) { + dev_crit( + &priv->spi->dev, + "Received packet len (%d) erroneously long\n", + len + ); + goto finish; + } + + if (buf[0] & SPI_SYN) { + if (priv->sync_command_response) { + memcpy(priv->sync_command_response, buf, len); + complete(&priv->sync_exchange_complete); + } else { + if (cascoda_api_upstream) + cascoda_api_upstream(buf, len, priv->spi); + priv->sync_up++; + } + } else { + if (cascoda_api_upstream) + cascoda_api_upstream(buf, len, priv->spi); + } + + ca8210_net_rx(priv->hw, buf, len); + if (buf[0] == SPI_MCPS_DATA_CONFIRM) { + if (buf[3] == MAC_TRANSACTION_OVERFLOW) { + dev_info( + &priv->spi->dev, + "Waiting for transaction overflow to stabilise...\n"); + msleep(2000); + dev_info( + &priv->spi->dev, + "Resetting MAC...\n"); + + mlme_reset_wpc = kmalloc(sizeof(*mlme_reset_wpc), + GFP_KERNEL); + if (!mlme_reset_wpc) + goto finish; + INIT_WORK( + &mlme_reset_wpc->work, + ca8210_mlme_reset_worker + ); + mlme_reset_wpc->priv = priv; + queue_work(priv->mlme_workqueue, &mlme_reset_wpc->work); + } + } else if (buf[0] == SPI_HWME_WAKEUP_INDICATION) { + dev_notice( + &priv->spi->dev, + "Wakeup indication received, reason:\n" + ); + switch (buf[2]) { + case 0: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power Up / System Reset\n" + ); + break; + case 1: + dev_notice( + &priv->spi->dev, + "Watchdog Timer Time-Out\n" + ); + break; + case 2: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power-Off by Sleep Timer Time-Out\n"); + break; + case 3: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power-Off by GPIO Activity\n" + ); + break; + case 4: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Standby by Sleep Timer Time-Out\n" + ); + break; + case 5: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Standby by GPIO Activity\n" + ); + break; + case 6: + dev_notice( + &priv->spi->dev, + "Sleep-Timer Time-Out in Active Mode\n" + ); + break; + default: + dev_warn(&priv->spi->dev, "Wakeup reason unknown\n"); + break; + } + complete(&priv->ca8210_is_awake); + } + +finish:; +} + +static int ca8210_remove(struct spi_device *spi_device); + +/** + * ca8210_spi_transfer_complete() - Called when a single spi transfer has + * completed + * @context: Pointer to the cas_control object for the finished transfer + */ +static void ca8210_spi_transfer_complete(void *context) +{ + struct cas_control *cas_ctl = context; + struct ca8210_priv *priv = cas_ctl->priv; + bool duplex_rx = false; + int i; + u8 retry_buffer[CA8210_SPI_BUF_SIZE]; + + if ( + cas_ctl->tx_in_buf[0] == SPI_NACK || + (cas_ctl->tx_in_buf[0] == SPI_IDLE && + cas_ctl->tx_in_buf[1] == SPI_NACK) + ) { + /* ca8210 is busy */ + dev_info(&priv->spi->dev, "ca8210 was busy during attempted write\n"); + if (cas_ctl->tx_buf[0] == SPI_IDLE) { + dev_warn( + &priv->spi->dev, + "IRQ servicing NACKd, dropping transfer\n" + ); + kfree(cas_ctl); + return; + } + if (priv->retries > 3) { + dev_err(&priv->spi->dev, "too many retries!\n"); + kfree(cas_ctl); + ca8210_remove(priv->spi); + return; + } + memcpy(retry_buffer, cas_ctl->tx_buf, CA8210_SPI_BUF_SIZE); + kfree(cas_ctl); + ca8210_spi_transfer( + priv->spi, + retry_buffer, + CA8210_SPI_BUF_SIZE + ); + priv->retries++; + dev_info(&priv->spi->dev, "retried spi write\n"); + return; + } else if ( + cas_ctl->tx_in_buf[0] != SPI_IDLE && + cas_ctl->tx_in_buf[0] != SPI_NACK + ) { + duplex_rx = true; + } + + if (duplex_rx) { + dev_dbg(&priv->spi->dev, "READ CMD DURING TX\n"); + for (i = 0; i < cas_ctl->tx_in_buf[1] + 2; i++) + dev_dbg( + &priv->spi->dev, + "%#03x\n", + cas_ctl->tx_in_buf[i] + ); + ca8210_rx_done(cas_ctl); + } + complete(&priv->spi_transfer_complete); + kfree(cas_ctl); + priv->retries = 0; +} + +/** + * ca8210_spi_transfer() - Initiate duplex spi transfer with ca8210 + * @spi: Pointer to spi device for transfer + * @buf: Octet array to send + * @len: length of the buffer being sent + * + * Return: 0 or linux error code + */ +static int ca8210_spi_transfer( + struct spi_device *spi, + const u8 *buf, + size_t len +) +{ + int i, status = 0; + struct ca8210_priv *priv = spi_get_drvdata(spi); + struct cas_control *cas_ctl; + + if (!spi) { + dev_crit( + &spi->dev, + "NULL spi device passed to ca8210_spi_transfer\n" + ); + return -ENODEV; + } + + reinit_completion(&priv->spi_transfer_complete); + + dev_dbg(&spi->dev, "ca8210_spi_transfer called\n"); + + cas_ctl = kmalloc(sizeof(*cas_ctl), GFP_ATOMIC); + if (!cas_ctl) + return -ENOMEM; + + cas_ctl->priv = priv; + memset(cas_ctl->tx_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); + memset(cas_ctl->tx_in_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); + memcpy(cas_ctl->tx_buf, buf, len); + + for (i = 0; i < len; i++) + dev_dbg(&spi->dev, "%#03x\n", cas_ctl->tx_buf[i]); + + spi_message_init(&cas_ctl->msg); + + cas_ctl->transfer.tx_nbits = 1; /* 1 MOSI line */ + cas_ctl->transfer.rx_nbits = 1; /* 1 MISO line */ + cas_ctl->transfer.speed_hz = 0; /* Use device setting */ + cas_ctl->transfer.bits_per_word = 0; /* Use device setting */ + cas_ctl->transfer.tx_buf = cas_ctl->tx_buf; + cas_ctl->transfer.rx_buf = cas_ctl->tx_in_buf; + cas_ctl->transfer.delay_usecs = 0; + cas_ctl->transfer.cs_change = 0; + cas_ctl->transfer.len = sizeof(struct mac_message); + cas_ctl->msg.complete = ca8210_spi_transfer_complete; + cas_ctl->msg.context = cas_ctl; + + spi_message_add_tail( + &cas_ctl->transfer, + &cas_ctl->msg + ); + + status = spi_async(spi, &cas_ctl->msg); + if (status < 0) { + dev_crit( + &spi->dev, + "status %d from spi_sync in write\n", + status + ); + } + + return status; +} + +/** + * ca8210_spi_exchange() - Exchange API/SAP commands with the radio + * @buf: Octet array of command being sent downstream + * @len: length of buf + * @response: buffer for storing synchronous response + * @device_ref: spi_device pointer for ca8210 + * + * Effectively calls ca8210_spi_transfer to write buf[] to the spi, then for + * synchronous commands waits for the corresponding response to be read from + * the spi before returning. The response is written to the response parameter. + * + * Return: 0 or linux error code + */ +static int ca8210_spi_exchange( + const u8 *buf, + size_t len, + u8 *response, + void *device_ref +) +{ + int status = 0; + struct spi_device *spi = device_ref; + struct ca8210_priv *pr |