summaryrefslogtreecommitdiffstats
path: root/drivers/net/ieee802154
diff options
context:
space:
mode:
authorHarry Morris <harrymorris12@gmail.com>2017-03-28 13:08:58 +0100
committerMarcel Holtmann <marcel@holtmann.org>2017-04-12 22:02:38 +0200
commitded845a781a578dfb0b5b2c138e5a067aa3b1242 (patch)
treec58265a5f3b7a5a7cc08cf5f387c220e31bf912f /drivers/net/ieee802154
parent212d71833315c65644efc46223db61dee7b3c68e (diff)
ieee802154: Add CA8210 IEEE 802.15.4 device driver
Add driver source and config for softMAC implementation of Cascoda's CA8210 IEEE 802.15.4 transceiver device. The driver mimics a common PHY-only implementation despite the CA8210 being a hardMAC device which exposes a SAP interface to the fully integrated MAC. The chip is a modem-only device with an integrated processor which runs the 802.15.4 MAC. The chip communicates via full-duplex SPI with additional pins for NIRQ and NRESET. The chip can also output its 16MHz clock to a GPIO with a configurable divider. The driver can be configured to implement a debugfs node that provides access to the SAP-based API to drive mechanisms not currently supported by the standard kernel interface. Signed-off-by: Harry Morris <h.morris@cascoda.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/net/ieee802154')
-rw-r--r--drivers/net/ieee802154/Kconfig22
-rw-r--r--drivers/net/ieee802154/Makefile1
-rw-r--r--drivers/net/ieee802154/ca8210.c3240
3 files changed, 3263 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..53fa87bfede0
--- /dev/null
+++ b/drivers/net/ieee802154/ca8210.c
@@ -0,0 +1,3240 @@
+/*
+ * 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);
+ 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(struct work_priv_container),
+ GFP_KERNEL
+ );
+ 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(struct cas_control),
+ GFP_ATOMIC
+ );
+ 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 *priv = spi->dev.driver_data;
+ long wait_remaining;
+
+ if ((buf[0] & SPI_SYN) && response) { /* if sync wait for conf