From 098317b74e8a6401228e931da702d4f2f8cfee0d Mon Sep 17 00:00:00 2001 From: Xiangfu Date: Fri, 8 Jun 2012 10:49:34 +0800 Subject: [PATCH] xburst: add ben-wpan support --- .../patches-3.3/800-WPAN-new-files.patch | 9480 +++++++++++++++++ .../xburst/patches-3.3/800-WPAN-patches.patch | 473 + 2 files changed, 9953 insertions(+) create mode 100644 target/linux/xburst/patches-3.3/800-WPAN-new-files.patch create mode 100644 target/linux/xburst/patches-3.3/800-WPAN-patches.patch diff --git a/target/linux/xburst/patches-3.3/800-WPAN-new-files.patch b/target/linux/xburst/patches-3.3/800-WPAN-new-files.patch new file mode 100644 index 000000000..5972519b8 --- /dev/null +++ b/target/linux/xburst/patches-3.3/800-WPAN-new-files.patch @@ -0,0 +1,9480 @@ +From 6d184ce0928a5a30e88e3e4b7be4200aa103abc3 Mon Sep 17 00:00:00 2001 +From: Xiangfu +Date: Tue, 5 Jun 2012 11:32:52 +0800 +Subject: [PATCH 2/3] 001 + +--- + drivers/ieee802154/adf7242.c | 1034 +++++++++++++++++++++++++++++++++++++++ + drivers/ieee802154/at86rf230.c | 872 +++++++++++++++++++++++++++++++++ + drivers/ieee802154/at86rf230.h | 211 ++++++++ + drivers/ieee802154/cc2420.c | 859 ++++++++++++++++++++++++++++++++ + drivers/ieee802154/fakelb.c | 311 ++++++++++++ + drivers/ieee802154/serial.c | 1047 ++++++++++++++++++++++++++++++++++++++++ + drivers/ieee802154/spi_atben.c | 421 ++++++++++++++++ + drivers/ieee802154/spi_atusb.c | 750 ++++++++++++++++++++++++++++ + include/linux/if_ieee802154.h | 6 + + include/linux/spi/at86rf230.h | 34 ++ + include/net/mac802154.h | 156 ++++++ + net/mac802154/Kconfig | 24 + + net/mac802154/Makefile | 6 + + net/mac802154/beacon.c | 285 +++++++++++ + net/mac802154/beacon_hash.c | 106 ++++ + net/mac802154/beacon_hash.h | 41 ++ + net/mac802154/mac802154.h | 126 +++++ + net/mac802154/mac_cmd.c | 365 ++++++++++++++ + net/mac802154/main.c | 283 +++++++++++ + net/mac802154/mib.c | 249 ++++++++++ + net/mac802154/mib.h | 35 ++ + net/mac802154/monitor.c | 117 +++++ + net/mac802154/rx.c | 117 +++++ + net/mac802154/scan.c | 203 ++++++++ + net/mac802154/smac.c | 128 +++++ + net/mac802154/tx.c | 106 ++++ + net/mac802154/wpan.c | 631 ++++++++++++++++++++++++ + net/zigbee/Kconfig | 7 + + net/zigbee/Makefile | 5 + + net/zigbee/af_zigbee.c | 285 +++++++++++ + net/zigbee/dgram.c | 401 +++++++++++++++ + 31 files changed, 9221 insertions(+), 0 deletions(-) + create mode 100644 drivers/ieee802154/adf7242.c + create mode 100644 drivers/ieee802154/at86rf230.c + create mode 100644 drivers/ieee802154/at86rf230.h + create mode 100644 drivers/ieee802154/cc2420.c + create mode 100644 drivers/ieee802154/fakelb.c + create mode 100644 drivers/ieee802154/serial.c + create mode 100644 drivers/ieee802154/spi_atben.c + create mode 100644 drivers/ieee802154/spi_atusb.c + create mode 100644 include/linux/if_ieee802154.h + create mode 100644 include/linux/spi/at86rf230.h + create mode 100644 include/net/mac802154.h + create mode 100644 net/mac802154/Kconfig + create mode 100644 net/mac802154/Makefile + create mode 100644 net/mac802154/beacon.c + create mode 100644 net/mac802154/beacon_hash.c + create mode 100644 net/mac802154/beacon_hash.h + create mode 100644 net/mac802154/mac802154.h + create mode 100644 net/mac802154/mac_cmd.c + create mode 100644 net/mac802154/main.c + create mode 100644 net/mac802154/mib.c + create mode 100644 net/mac802154/mib.h + create mode 100644 net/mac802154/monitor.c + create mode 100644 net/mac802154/rx.c + create mode 100644 net/mac802154/scan.c + create mode 100644 net/mac802154/smac.c + create mode 100644 net/mac802154/tx.c + create mode 100644 net/mac802154/wpan.c + create mode 100644 net/zigbee/Kconfig + create mode 100644 net/zigbee/Makefile + create mode 100644 net/zigbee/af_zigbee.c + create mode 100644 net/zigbee/dgram.c + +diff --git a/drivers/ieee802154/adf7242.c b/drivers/ieee802154/adf7242.c +new file mode 100644 +index 0000000..b578a55 +--- /dev/null ++++ b/drivers/ieee802154/adf7242.c +@@ -0,0 +1,1034 @@ ++/* ++ * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver ++ * ++ * Copyright 2009-2010 Analog Devices Inc. ++ * ++ * Licensed under the GPL-2 or later. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * DEBUG LEVEL ++ * 0 OFF ++ * 1 INFO ++ * 2 INFO + TRACE ++ */ ++ ++#define ADF_DEBUG 0 ++#define DBG(n, args...) do { if (ADF_DEBUG >= (n)) pr_debug(args); } while (0) ++ ++#define FIRMWARE "adf7242_firmware.bin" ++#define MAX_POLL_LOOPS 10 ++ ++/* All Registers */ ++ ++#define REG_EXT_CTRL 0x100 /* RW External LNA/PA and internal PA control configuration bits */ ++#define REG_TX_FSK_TEST 0x101 /* RW TX FSK test mode configuration */ ++#define REG_CCA1 0x105 /* RW RSSI threshold for CCA */ ++#define REG_CCA2 0x106 /* RW CCA mode configuration */ ++#define REG_BUFFERCFG 0x107 /* RW RX_BUFFER overwrite control */ ++#define REG_PKT_CFG 0x108 /* RW FCS evaluation configuration */ ++#define REG_DELAYCFG0 0x109 /* RW RC_RX command to SFD or sync word search delay */ ++#define REG_DELAYCFG1 0x10A /* RW RC_TX command to TX state */ ++#define REG_DELAYCFG2 0x10B /* RW Mac delay extention */ ++#define REG_SYNC_WORD0 0x10C /* RW sync word bits [7:0] of [23:0] */ ++#define REG_SYNC_WORD1 0x10D /* RW sync word bits [15:8] of [23:0] */ ++#define REG_SYNC_WORD2 0x10E /* RW sync word bits [23:16] of [23:0] */ ++#define REG_SYNC_CONFIG 0x10F /* RW sync word configuration */ ++#define REG_RC_CFG 0x13E /* RW RX / TX packet configuration */ ++#define REG_RC_VAR44 0x13F /* RW RESERVED */ ++#define REG_CH_FREQ0 0x300 /* RW Channel Frequency Settings - Low Byte */ ++#define REG_CH_FREQ1 0x301 /* RW Channel Frequency Settings - Middle Byte */ ++#define REG_CH_FREQ2 0x302 /* RW Channel Frequency Settings - 2 MSBs */ ++#define REG_TX_FD 0x304 /* RW TX Frequency Deviation Register */ ++#define REG_DM_CFG0 0x305 /* RW RX Discriminator BW Register */ ++#define REG_TX_M 0x306 /* RW TX Mode Register */ ++#define REG_RX_M 0x307 /* RW RX Mode Register */ ++#define REG_RRB 0x30C /* R RSSI Readback Register */ ++#define REG_LRB 0x30D /* R Link Quality Readback Register */ ++#define REG_DR0 0x30E /* RW bits [15:8] of [15:0] for data rate setting */ ++#define REG_DR1 0x30F /* RW bits [7:0] of [15:0] for data rate setting */ ++#define REG_PRAMPG 0x313 /* RW RESERVED */ ++#define REG_TXPB 0x314 /* RW TX Packet Storage Base Address */ ++#define REG_RXPB 0x315 /* RW RX Packet Storage Base Address */ ++#define REG_TMR_CFG0 0x316 /* RW Wake up Timer Configuration Register - High Byte */ ++#define REG_TMR_CFG1 0x317 /* RW Wake up Timer Configuration Register - Low Byte */ ++#define REG_TMR_RLD0 0x318 /* RW Wake up Timer Value Register - High Byte */ ++#define REG_TMR_RLD1 0x319 /* RW Wake up Timer Value Register - Low Byte */ ++#define REG_TMR_CTRL 0x31A /* RW Wake up Timer Timeout flag */ ++#define REG_PD_AUX 0x31E /* RW Battmon enable */ ++#define REG_GP_CFG 0x32C /* RW GPIO Configuration */ ++#define REG_GP_OUT 0x32D /* RW GPIO Configuration */ ++#define REG_GP_IN 0x32E /* R GPIO Configuration */ ++#define REG_SYNT 0x335 /* RW bandwidth calibration timers */ ++#define REG_CAL_CFG 0x33D /* RW Calibration Settings */ ++#define REG_SYNT_CAL 0x371 /* RW Oscillator and Doubler Configuration */ ++#define REG_IIRF_CFG 0x389 /* RW BB Filter Decimation Rate */ ++#define REG_CDR_CFG 0x38A /* RW CDR kVCO */ ++#define REG_DM_CFG1 0x38B /* RW Postdemodulator Filter */ ++#define REG_AGCSTAT 0x38E /* R RXBB Ref Osc Calibration Engine Readback */ ++#define REG_RXCAL0 0x395 /* RW RX BB filter tuning, LSB */ ++#define REG_RXCAL1 0x396 /* RW RX BB filter tuning, MSB */ ++#define REG_RXFE_CFG 0x39B /* RW RXBB Ref Osc & RXFE Calibration */ ++#define REG_PA_RR 0x3A7 /* RW Set PA ramp rate */ ++#define REG_PA_CFG 0x3A8 /* RW PA enable */ ++#define REG_EXTPA_CFG 0x3A9 /* RW External PA BIAS DAC */ ++#define REG_EXTPA_MSC 0x3AA /* RW PA Bias Mode */ ++#define REG_ADC_RBK 0x3AE /* R Readback temp */ ++#define REG_AGC_CFG1 0x3B2 /* RW GC Parameters */ ++#define REG_AGC_MAX 0x3B4 /* RW Slew rate */ ++#define REG_AGC_CFG2 0x3B6 /* RW RSSI Parameters */ ++#define REG_AGC_CFG3 0x3B7 /* RW RSSI Parameters */ ++#define REG_AGC_CFG4 0x3B8 /* RW RSSI Parameters */ ++#define REG_AGC_CFG5 0x3B9 /* RW RSSI & NDEC Parameters */ ++#define REG_AGC_CFG6 0x3BA /* RW NDEC Parameters */ ++#define REG_OCL_CFG1 0x3C4 /* RW OCL System Parameters */ ++#define REG_IRQ1_EN0 0x3C7 /* RW Interrupt Mask set bits [7:0] of [15:0] for IRQ1 */ ++#define REG_IRQ1_EN1 0x3C8 /* RW Interrupt Mask set bits [15:8] of [15:0] for IRQ1 */ ++#define REG_IRQ2_EN0 0x3C9 /* RW Interrupt Mask set bits [7:0] of [15:0] for IRQ2 */ ++#define REG_IRQ2_EN1 0x3CA /* RW Interrupt Mask set bits [15:8] of [15:0] for IRQ2 */ ++#define REG_IRQ1_SRC0 0x3CB /* RW Interrupt Source bits [7:0] of [15:0] for IRQ */ ++#define REG_IRQ1_SRC1 0x3CC /* RW Interrupt Source bits [15:8] of [15:0] for IRQ */ ++#define REG_OCL_BW0 0x3D2 /* RW OCL System Parameters */ ++#define REG_OCL_BW1 0x3D3 /* RW OCL System Parameters */ ++#define REG_OCL_BW2 0x3D4 /* RW OCL System Parameters */ ++#define REG_OCL_BW3 0x3D5 /* RW OCL System Parameters */ ++#define REG_OCL_BW4 0x3D6 /* RW OCL System Parameters */ ++#define REG_OCL_BWS 0x3D7 /* RW OCL System Parameters */ ++#define REG_OCL_CFG13 0x3E0 /* RW OCL System Parameters */ ++#define REG_GP_DRV 0x3E3 /* RW I/O pads Configuration and bg trim */ ++#define REG_BM_CFG 0x3E6 /* RW Battery Monitor Threshold Voltage setting */ ++#define REG_SFD_15_4 0x3F4 /* RW Option to set non standard SFD */ ++#define REG_AFC_CFG 0x3F7 /* RW AFC mode and polarity */ ++#define REG_AFC_KI_KP 0x3F8 /* RW AFC ki and kp */ ++#define REG_AFC_RANGE 0x3F9 /* RW AFC range */ ++#define REG_AFC_READ 0x3FA /* RW Readback frequency error */ ++ ++#define REG_PAN_ID0 0x112 ++#define REG_PAN_ID1 0x113 ++#define REG_SHORT_ADDR_0 0x114 ++#define REG_SHORT_ADDR_1 0x115 ++#define REG_IEEE_ADDR_0 0x116 ++#define REG_IEEE_ADDR_1 0x117 ++#define REG_IEEE_ADDR_2 0x118 ++#define REG_IEEE_ADDR_3 0x119 ++#define REG_IEEE_ADDR_4 0x11A ++#define REG_IEEE_ADDR_5 0x11B ++#define REG_IEEE_ADDR_6 0x11C ++#define REG_IEEE_ADDR_7 0x11D ++#define REG_FFILT_CFG 0x11E ++#define REG_AUTO_CFG 0x11F ++#define REG_AUTO_TX1 0x120 ++#define REG_AUTO_TX2 0x121 ++#define REG_AUTO_STATUS 0x122 ++ ++/* REG_FFILT_CFG */ ++#define ACCEPT_BEACON_FRAMES (1 << 0) ++#define ACCEPT_DATA_FRAMES (1 << 1) ++#define ACCEPT_ACK_FRAMES (1 << 2) ++#define ACCEPT_MACCMD_FRAMES (1 << 3) ++#define ACCEPT_RESERVED_FRAMES (1 << 4) ++#define ACCEPT_ALL_ADDRESS (1 << 5) ++ ++/* REG_AUTO_CFG */ ++#define AUTO_ACK_FRAMEPEND (1 << 0) ++#define IS_PANCOORD (1 << 1) ++#define RX_AUTO_ACK_EN (1 << 3) ++#define CSMA_CA_RX_TURNAROUND (1 << 4) ++ ++/* REG_AUTO_TX1 */ ++#define MAX_FRAME_RETRIES(x) ((x) & 0xF) ++#define MAX_CCA_RETRIES(x) (((x) & 0x7) << 4) ++ ++/* REG_AUTO_TX2 */ ++#define CSMA_MAX_BE(x) ((x) & 0xF) ++#define CSMA_MIN_BE(x) (((x) & 0xF) << 4) ++ ++#define CMD_SPI_NOP 0xFF /* No operation. Use for dummy writes */ ++#define CMD_SPI_PKT_WR 0x10 /* Write telegram to the Packet RAM starting from the TX packet base address pointer tx_packet_base */ ++#define CMD_SPI_PKT_RD 0x30 /* Read telegram from the Packet RAM starting from RX packet base address pointer rxpb.rx_packet_base */ ++#define CMD_SPI_MEM_WR(x) (0x18 + (x >> 8)) /* Write data to MCR or Packet RAM sequentially */ ++#define CMD_SPI_MEM_RD(x) (0x38 + (x >> 8)) /* Read data from MCR or Packet RAM sequentially */ ++#define CMD_SPI_MEMR_WR(x) (0x08 + (x >> 8)) /* Write data to MCR or Packet RAM as random block */ ++#define CMD_SPI_MEMR_RD(x) (0x28 + (x >> 8)) /* Read data from MCR or Packet RAM as random block */ ++#define CMD_SPI_PRAM_WR 0x1E /* Write data sequentially to current PRAM page selected */ ++#define CMD_RC_SLEEP 0xB1 /* Invoke transition of radio controller into SLEEP state */ ++#define CMD_RC_IDLE 0xB2 /* Invoke transition of radio controller into IDLE state */ ++#define CMD_RC_PHY_RDY 0xB3 /* Invoke transition of radio controller into PHY_RDY state */ ++#define CMD_RC_RX 0xB4 /* Invoke transition of radio controller into RX state */ ++#define CMD_RC_TX 0xB5 /* Invoke transition of radio controller into TX state */ ++#define CMD_RC_MEAS 0xB6 /* Invoke transition of radio controller into MEAS state */ ++#define CMD_RC_CCA 0xB7 /* Invoke Clear channel assessment */ ++#define CMD_RC_CSMACA 0xC1 /* initiates CSMA-CA channel access sequence and frame transmission */ ++ ++/* STATUS */ ++ ++#define STAT_SPI_READY (1 << 7) ++#define STAT_IRQ_STATUS (1 << 6) ++#define STAT_RC_READY (1 << 5) ++#define STAT_CCA_RESULT (1 << 4) ++#define RC_STATUS_IDLE 1 ++#define RC_STATUS_MEAS 2 ++#define RC_STATUS_PHY_RDY 3 ++#define RC_STATUS_RX 4 ++#define RC_STATUS_TX 5 ++#define RC_STATUS_MASK 0xF ++ ++/* AUTO_STATUS */ ++ ++#define SUCCESS 0 ++#define SUCCESS_DATPEND 1 ++#define FAILURE_CSMACA 2 ++#define FAILURE_NOACK 3 ++#define AUTO_STATUS_MASK 0x3 ++ ++#define PRAM_PAGESIZE 256 ++ ++/* IRQ1 */ ++ ++#define IRQ_CCA_COMPLETE (1 << 0) ++#define IRQ_SFD_RX (1 << 1) ++#define IRQ_SFD_TX (1 << 2) ++#define IRQ_RX_PKT_RCVD (1 << 3) ++#define IRQ_TX_PKT_SENT (1 << 4) ++#define IRQ_FRAME_VALID (1 << 5) ++#define IRQ_ADDRESS_VALID (1 << 6) ++#define IRQ_CSMA_CA (1 << 7) ++ ++#define AUTO_TX_TURNAROUND (1 << 3) ++#define ADDON_EN (1 << 4) ++ ++struct adf7242_local { ++ struct spi_device *spi; ++ struct adf7242_platform_data *pdata; ++ struct work_struct irqwork; ++ struct completion tx_complete; ++ struct ieee802154_dev *dev; ++ struct mutex bmux; ++ spinlock_t lock; ++ unsigned irq_disabled:1; /* P: lock */ ++ unsigned is_tx:1; /* P: lock */ ++ unsigned mode; ++ unsigned tx_irq; ++ int tx_stat; ++ u8 buf[3]; ++}; ++ ++static int adf7242_status(struct adf7242_local *lp, u8 *stat) ++{ ++ int status; ++ struct spi_message msg; ++ u8 buf_tx[1], buf_rx[1]; ++ ++ struct spi_transfer xfer = { ++ .len = 1, ++ .tx_buf = buf_tx, ++ .rx_buf = buf_rx, ++ }; ++ ++ buf_tx[0] = CMD_SPI_NOP; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ mutex_lock(&lp->bmux); ++ status = spi_sync(lp->spi, &msg); ++ mutex_unlock(&lp->bmux); ++ ++ *stat = buf_rx[0]; ++ ++ return status; ++} ++ ++static int adf7242_wait_ready(struct adf7242_local *lp) ++{ ++ u8 stat; ++ int cnt = 0; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ do { ++ adf7242_status(lp, &stat); ++ cnt++; ++ } while (!(stat & STAT_RC_READY) && (cnt < MAX_POLL_LOOPS)); ++ ++ DBG(2, "%s :Exit loops=%d\n", __func__, cnt); ++ ++ return 0; ++} ++ ++static int adf7242_wait_status(struct adf7242_local *lp, int status) ++{ ++ u8 stat; ++ int cnt = 0; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ do { ++ adf7242_status(lp, &stat); ++ stat &= RC_STATUS_MASK; ++ cnt++; ++ } while ((stat != status) && (cnt < MAX_POLL_LOOPS)); ++ ++ DBG(2, "%s :Exit loops=%d\n", __func__, cnt); ++ ++ return 0; ++} ++ ++static int adf7242_write_fbuf(struct adf7242_local *lp, u8 *data, u8 len) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = buf, ++ ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = len, ++ .tx_buf = data, ++ }; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ adf7242_wait_ready(lp); ++ ++ buf[0] = CMD_SPI_PKT_WR; ++ buf[1] = len + 2; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ mutex_lock(&lp->bmux); ++ status = spi_sync(lp->spi, &msg); ++ mutex_unlock(&lp->bmux); ++ ++ DBG(2, "%s :Exit\n", __func__); ++ return status; ++} ++ ++static int adf7242_read_fbuf(struct adf7242_local *lp, ++ u8 *data, u8 *len, u8 *lqi) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 3, ++ .tx_buf = buf, ++ .rx_buf = buf, ++ ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = *len, ++ .rx_buf = data, ++ }; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ adf7242_wait_ready(lp); ++ ++ mutex_lock(&lp->bmux); ++ buf[0] = CMD_SPI_PKT_RD; ++ buf[1] = CMD_SPI_NOP; ++ buf[2] = 0; /* PHR */ ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ ++ if (!status) { ++ *lqi = data[buf[2] - 1]; ++ *len = buf[2]; /* PHR */ ++ } ++ ++ mutex_unlock(&lp->bmux); ++ DBG(2, "%s :Exit\n", __func__); ++ return status; ++} ++ ++static int adf7242_read_reg(struct adf7242_local *lp, ++ u16 addr, u8 *data) ++{ ++ int status; ++ struct spi_message msg; ++ u8 buf_tx[4], buf_rx[4]; ++ ++ struct spi_transfer xfer = { ++ .len = 4, ++ .tx_buf = buf_tx, ++ .rx_buf = buf_rx, ++ }; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ adf7242_wait_ready(lp); ++ ++ mutex_lock(&lp->bmux); ++ buf_tx[0] = CMD_SPI_MEM_RD(addr); ++ buf_tx[1] = addr; ++ buf_tx[2] = CMD_SPI_NOP; ++ buf_tx[3] = CMD_SPI_NOP; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ if (msg.status) ++ status = msg.status; ++ ++ if (!status) ++ *data = buf_rx[3]; ++ ++ mutex_unlock(&lp->bmux); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return status; ++} ++ ++static int adf7242_write_reg(struct adf7242_local *lp, ++ u16 addr, u8 data) ++{ ++ int status; ++ struct spi_message msg; ++ u8 buf_tx[4]; ++ ++ struct spi_transfer xfer = { ++ .len = 3, ++ .tx_buf = buf_tx, ++ }; ++ DBG(2, "%s :Enter\n", __func__); ++ adf7242_wait_ready(lp); ++ ++ buf_tx[0] = CMD_SPI_MEM_WR(addr); ++ buf_tx[1] = addr; ++ buf_tx[2] = data; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ mutex_lock(&lp->bmux); ++ status = spi_sync(lp->spi, &msg); ++ mutex_unlock(&lp->bmux); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return status; ++} ++ ++static int adf7242_cmd(struct adf7242_local *lp, u8 cmd) ++{ ++ int status; ++ struct spi_message msg; ++ u8 buf_tx[1]; ++ ++ struct spi_transfer xfer = { ++ .len = 1, ++ .tx_buf = buf_tx, ++ }; ++ ++ DBG(2, "%s :Enter CMD=0x%X\n", __func__, cmd); ++ adf7242_wait_ready(lp); ++ ++ buf_tx[0] = cmd; ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ mutex_lock(&lp->bmux); ++ status = spi_sync(lp->spi, &msg); ++ mutex_unlock(&lp->bmux); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return status; ++} ++ ++static int adf7242_upload_firmware(struct adf7242_local *lp, ++ u8 *data, u16 len) ++{ ++ int status, i, page = 0; ++ struct spi_message msg; ++ struct spi_transfer xfer_buf = {}; ++ u8 buf[2]; ++ ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = buf, ++ }; ++ ++ buf[0] = CMD_SPI_PRAM_WR; ++ buf[1] = 0; ++ ++ for (i = len; i >= 0 ; i -= PRAM_PAGESIZE) { ++ adf7242_write_reg(lp, REG_PRAMPG, page); ++ ++ xfer_buf.len = i >= PRAM_PAGESIZE ? PRAM_PAGESIZE : i, ++ xfer_buf.tx_buf = &data[page * PRAM_PAGESIZE], ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ mutex_lock(&lp->bmux); ++ status = spi_sync(lp->spi, &msg); ++ mutex_unlock(&lp->bmux); ++ page++; ++ } ++ ++ return status; ++} ++ ++static int adf7242_ed(struct ieee802154_dev *dev, u8 *level) ++{ ++ struct adf7242_local *lp = dev->priv; ++ int ret; ++ ++ DBG(2, "%s :Enter\n", __func__); ++#if 0 ++ adf7242_cmd(lp, CMD_RC_PHY_RDY); ++ adf7242_cmd(lp, CMD_RC_CCA); ++ adf7242_wait_status(lp, RC_STATUS_PHY_RDY); ++#else ++ udelay(128); ++#endif ++ ret = adf7242_read_reg(lp, REG_RRB, level); ++ adf7242_cmd(lp, CMD_RC_RX); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return ret; ++} ++ ++static int adf7242_start(struct ieee802154_dev *dev) ++{ ++ struct adf7242_local *lp = dev->priv; ++ int ret; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ret = adf7242_cmd(lp, CMD_RC_RX); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return ret; ++} ++ ++static void adf7242_stop(struct ieee802154_dev *dev) ++{ ++ struct adf7242_local *lp = dev->priv; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ adf7242_cmd(lp, CMD_RC_PHY_RDY); ++ DBG(2, "%s :Exit\n", __func__); ++} ++ ++static int adf7242_channel(struct ieee802154_dev *dev, int page, int channel) ++{ ++ struct adf7242_local *lp = dev->priv; ++ unsigned long freq; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ DBG(1, "%s :Channel=%d\n", __func__, channel); ++ ++ might_sleep(); ++ ++ BUG_ON(page != 0); ++ BUG_ON(channel < 11); ++ BUG_ON(channel > 26); ++ ++ freq = (2405 + 5 * (channel - 11)) * 100; ++ ++ adf7242_cmd(lp, CMD_RC_PHY_RDY); ++ ++ adf7242_write_reg(lp, REG_CH_FREQ0, freq); ++ adf7242_write_reg(lp, REG_CH_FREQ1, freq >> 8); ++ adf7242_write_reg(lp, REG_CH_FREQ2, freq >> 16); ++ ++ adf7242_cmd(lp, CMD_RC_RX); ++ ++ dev->phy->current_channel = channel; ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return 0; ++} ++ ++static int adf7242_set_hw_addr_filt(struct ieee802154_dev *dev, ++ struct ieee802154_hw_addr_filt *filt, ++ unsigned long changed) ++{ ++ struct adf7242_local *lp = dev->priv; ++ u8 reg; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ DBG(1, "%s :Changed=0x%lX\n", __func__, changed); ++ ++ might_sleep(); ++ ++ if (changed & IEEE802515_IEEEADDR_CHANGED) { ++ adf7242_write_reg(lp, REG_IEEE_ADDR_0, filt->ieee_addr[7]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_1, filt->ieee_addr[6]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_2, filt->ieee_addr[5]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_3, filt->ieee_addr[4]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_4, filt->ieee_addr[3]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_5, filt->ieee_addr[2]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_6, filt->ieee_addr[1]); ++ adf7242_write_reg(lp, REG_IEEE_ADDR_7, filt->ieee_addr[0]); ++ } ++ ++ if (changed & IEEE802515_SADDR_CHANGED) { ++ adf7242_write_reg(lp, REG_SHORT_ADDR_0, filt->short_addr); ++ adf7242_write_reg(lp, REG_SHORT_ADDR_1, filt->short_addr >> 8); ++ } ++ ++ if (changed & IEEE802515_PANID_CHANGED) { ++ adf7242_write_reg(lp, REG_PAN_ID0, filt->pan_id); ++ adf7242_write_reg(lp, REG_PAN_ID1, filt->pan_id >> 8); ++ } ++ ++ if (changed & IEEE802515_PANC_CHANGED) { ++ adf7242_read_reg(lp, REG_AUTO_CFG, ®); ++ if (filt->pan_coord) ++ reg |= IS_PANCOORD; ++ else ++ reg &= ~IS_PANCOORD; ++ adf7242_write_reg(lp, REG_AUTO_CFG, reg); ++ } ++ ++ DBG(2, "%s :Exit\n", __func__); ++ return 0; ++} ++ ++static int adf7242_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ++{ ++ struct adf7242_local *lp = dev->priv; ++ int ret; ++ unsigned long flags; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ BUG_ON(lp->is_tx); ++ lp->is_tx = 1; ++ spin_unlock_irqrestore(&lp->lock, flags); ++ ++ ret = adf7242_write_fbuf(lp, skb->data, skb->len); ++ if (ret) ++ goto err_rx; ++ ++ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA) { ++ ret = adf7242_cmd(lp, CMD_RC_PHY_RDY); ++ ret |= adf7242_cmd(lp, CMD_RC_CSMACA); ++ } else { ++ ret = adf7242_cmd(lp, CMD_RC_TX); ++ } ++ ++ if (ret) ++ goto err_rx; ++ ++ ret = wait_for_completion_interruptible(&lp->tx_complete); ++ ++ if (ret < 0) ++ goto err_rx; ++ ++ DBG(2, "%s :Exit\n", __func__); ++ return ret; ++ ++err_rx: ++ spin_lock_irqsave(&lp->lock, flags); ++ lp->is_tx = 0; ++ spin_unlock_irqrestore(&lp->lock, flags); ++ return ret; ++} ++ ++static int adf7242_rx(struct adf7242_local *lp) ++{ ++ u8 len = 128; ++ u8 lqi = 0; ++ int ret; ++ struct sk_buff *skb; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ skb = alloc_skb(len, GFP_KERNEL); ++ if (!skb) ++ return -ENOMEM; ++ ++ ret = adf7242_read_fbuf(lp, skb_put(skb, len), &len, &lqi); ++ ++ adf7242_cmd(lp, CMD_RC_RX); ++ ++ skb_trim(skb, len - 2); /* We do not put RSSI/LQI or CRC into the frame */ ++ ++ if (len < 2) { ++ kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ ieee802154_rx_irqsafe(lp->dev, skb, lqi); ++ ++ DBG(1, "%s: %d %d %x\n", __func__, ret, len, lqi); ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return 0; ++} ++ ++static struct ieee802154_ops adf7242_ops = { ++ .owner = THIS_MODULE, ++ .xmit = adf7242_xmit, ++ .ed = adf7242_ed, ++ .set_channel = adf7242_channel, ++ .set_hw_addr_filt = adf7242_set_hw_addr_filt, ++ .start = adf7242_start, ++ .stop = adf7242_stop, ++}; ++ ++static void adf7242_irqwork(struct work_struct *work) ++{ ++ struct adf7242_local *lp = ++ container_of(work, struct adf7242_local, irqwork); ++ u8 irq1, auto_stat = 0, stat = 0; ++ int ret; ++ unsigned long flags; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ ret = adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1); ++ ++ DBG(1, "%s IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", __func__, irq1, ++ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "", ++ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "", ++ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "", ++ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "", ++ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "", ++ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "", ++ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "", ++ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : ""); ++ ++ adf7242_status(lp, &stat); ++ ++ DBG(1, "%s STATUS = %X:\n%s\n%s%s%s%s%s\n", __func__, stat, ++ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY", ++ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "", ++ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "", ++ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "", ++ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "", ++ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : ""); ++ ++ adf7242_write_reg(lp, REG_IRQ1_SRC1, irq1); ++ ++ if (irq1 & IRQ_RX_PKT_RCVD) { ++ ++ /* Wait until ACK is processed */ ++ if ((lp->mode & ADF_IEEE802154_HW_AACK) && ++ ((stat & RC_STATUS_MASK) != RC_STATUS_PHY_RDY)) ++ adf7242_wait_status(lp, RC_STATUS_PHY_RDY); ++ ++ adf7242_rx(lp); ++ } ++ ++ if (irq1 & lp->tx_irq) { ++ ++ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA) { ++ adf7242_read_reg(lp, REG_AUTO_STATUS, &auto_stat); ++ auto_stat &= AUTO_STATUS_MASK; ++ ++ DBG(1, "%s AUTO_STATUS = %X:\n%s%s%s%s\n", ++ __func__, auto_stat, ++ auto_stat == SUCCESS ? "SUCCESS" : "", ++ auto_stat == SUCCESS_DATPEND ? "SUCCESS_DATPEND" : "", ++ auto_stat == FAILURE_CSMACA ? "FAILURE_CSMACA" : "", ++ auto_stat == FAILURE_NOACK ? "FAILURE_NOACK" : ""); ++ ++ /* save CSMA-CA completion status */ ++ lp->tx_stat = auto_stat; ++ } ++ spin_lock_irqsave(&lp->lock, flags); ++ if (lp->is_tx) { ++ lp->is_tx = 0; ++ complete(&lp->tx_complete); ++ } ++ spin_unlock_irqrestore(&lp->lock, flags); ++ ++ /* in case we just received a frame we are already in PHY_RX */ ++ ++ if (!(irq1 & IRQ_RX_PKT_RCVD)) ++ adf7242_cmd(lp, CMD_RC_RX); ++ } ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ if (lp->irq_disabled) { ++ lp->irq_disabled = 0; ++ enable_irq(lp->spi->irq); ++ } ++ spin_unlock_irqrestore(&lp->lock, flags); ++ ++ DBG(2, "%s :Exit\n", __func__); ++} ++ ++static irqreturn_t adf7242_isr(int irq, void *data) ++{ ++ struct adf7242_local *lp = data; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ spin_lock(&lp->lock); ++ if (!lp->irq_disabled) { ++ disable_irq_nosync(irq); ++ lp->irq_disabled = 1; ++ } ++ spin_unlock(&lp->lock); ++ ++ schedule_work(&lp->irqwork); ++ ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int adf7242_hw_init(struct adf7242_local *lp) ++{ ++ int ret; ++ const struct firmware *fw; ++ ++ DBG(2, "%s :Enter\n", __func__); ++ ++ adf7242_cmd(lp, CMD_RC_IDLE); ++ ++ if (lp->mode) { ++ /* get ADF7242 addon firmware ++ * build this driver as module ++ * and place under /lib/firmware/adf7242_firmware.bin ++ */ ++ ret = request_firmware(&fw, FIRMWARE, &lp->spi->dev); ++ if (ret) { ++ dev_err(&lp->spi->dev, ++ "request_firmware() failed with %i\n", ret); ++ return ret; ++ } ++ ++ adf7242_upload_firmware(lp, (u8 *) fw->data, fw->size); ++ release_firmware(fw); ++ ++ adf7242_write_reg(lp, REG_FFILT_CFG, ++ ACCEPT_BEACON_FRAMES | ++ ACCEPT_DATA_FRAMES | ++ ACCEPT_ACK_FRAMES | ++ ACCEPT_MACCMD_FRAMES | ++ (lp->mode & ADF_IEEE802154_PROMISCUOUS_MODE ? ++ ACCEPT_ALL_ADDRESS : 0) | ++ ACCEPT_RESERVED_FRAMES); ++ ++ adf7242_write_reg(lp, REG_AUTO_TX1, ++ MAX_FRAME_RETRIES(lp->pdata->max_frame_retries) | ++ MAX_CCA_RETRIES(lp->pdata->max_cca_retries)); ++ ++ adf7242_write_reg(lp, REG_AUTO_TX2, ++ CSMA_MAX_BE(lp->pdata->max_csma_be) | ++ CSMA_MIN_BE(lp->pdata->min_csma_be)); ++ ++ adf7242_write_reg(lp, REG_AUTO_CFG, ++ (lp->mode & ADF_IEEE802154_HW_AACK ? ++ RX_AUTO_ACK_EN : 0)); ++ } ++ ++ adf7242_write_reg(lp, REG_PKT_CFG, lp->mode ? ADDON_EN : 0); ++ ++ adf7242_write_reg(lp, REG_EXTPA_MSC, 0xF1); ++ adf7242_write_reg(lp, REG_RXFE_CFG, 0x1D); ++ adf7242_write_reg(lp, REG_IRQ1_EN0, 0); ++ ++ adf7242_write_reg(lp, REG_IRQ1_EN1, IRQ_RX_PKT_RCVD | lp->tx_irq); ++ ++ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); ++ adf7242_write_reg(lp, REG_IRQ1_SRC0, 0xFF); ++ ++ adf7242_cmd(lp, CMD_RC_PHY_RDY); ++ ++ DBG(2, "%s :Exit\n", __func__); ++ ++ return 0; ++} ++ ++static int adf7242_suspend(struct spi_device *spi, pm_message_t message) ++{ ++ return 0; ++} ++ ++static int adf7242_resume(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++static ssize_t adf7242_show(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct adf7242_local *lp = dev_get_drvdata(dev); ++ u8 stat; ++ ++ adf7242_status(lp, &stat); ++ ++ return sprintf(buf, "STATUS = %X:\n%s\n%s%s%s%s%s\n", stat, ++ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY", ++ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "", ++ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "", ++ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "", ++ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "", ++ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : ""); ++ ++} ++static DEVICE_ATTR(status, 0664, adf7242_show, NULL); ++ ++static struct attribute *adf7242_attributes[] = { ++ &dev_attr_status.attr, ++ NULL ++}; ++ ++static const struct attribute_group adf7242_attr_group = { ++ .attrs = adf7242_attributes, ++}; ++ ++static int __devinit adf7242_probe(struct spi_device *spi) ++{ ++ struct adf7242_platform_data *pdata = spi->dev.platform_data; ++ struct ieee802154_dev *dev; ++ struct adf7242_local *lp; ++ int ret; ++ ++ if (!spi->irq) { ++ dev_err(&spi->dev, "no IRQ specified\n"); ++ return -EINVAL; ++ } ++ ++ if (!pdata) { ++ dev_err(&spi->dev, "no platform data?\n"); ++ return -ENODEV; ++ } ++ ++ dev = ieee802154_alloc_device(sizeof(*lp), &adf7242_ops); ++ if (!dev) ++ return -ENOMEM; ++ ++ lp = dev->priv; ++ lp->dev = dev; ++ lp->spi = spi; ++ lp->pdata = pdata; ++ lp->mode = pdata->mode; ++ ++ dev->priv = lp; ++ dev->parent = &spi->dev; ++ dev->extra_tx_headroom = 0; ++ /* We do support only 2.4 Ghz */ ++ ++ dev->phy->channels_supported[0] = 0x7FFF800; ++ ++ if (!lp->mode) { ++ adf7242_ops.set_hw_addr_filt = NULL; ++ lp->tx_irq = IRQ_TX_PKT_SENT; ++ } else { ++ if ((lp->mode & ADF_IEEE802154_PROMISCUOUS_MODE) && ++ (lp->mode & ADF_IEEE802154_HW_AACK)) ++ lp->mode &= ~ADF_IEEE802154_HW_AACK; ++ ++ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA) ++ lp->tx_irq = IRQ_CSMA_CA; ++ else ++ lp->tx_irq = IRQ_TX_PKT_SENT; ++ } ++ ++ dev->flags = IEEE802154_HW_OMIT_CKSUM | ++ (lp->mode & ADF_IEEE802154_HW_AACK ? ++ IEEE802154_HW_AACK : 0); ++ ++ mutex_init(&lp->bmux); ++ INIT_WORK(&lp->irqwork, adf7242_irqwork); ++ spin_lock_init(&lp->lock); ++ init_completion(&lp->tx_complete); ++ ++ spi_set_drvdata(spi, lp); ++ ++ ret = adf7242_hw_init(lp); ++ if (ret) ++ goto err_hw_init; ++ ++ ret = request_irq(spi->irq, adf7242_isr, IRQF_TRIGGER_HIGH, ++ dev_name(&spi->dev), lp); ++ if (ret) ++ goto err_hw_init; ++ ++ ret = ieee802154_register_device(lp->dev); ++ if (ret) ++ goto err_irq; ++ ++ dev_set_drvdata(&spi->dev, lp); ++ ++ ret = sysfs_create_group(&spi->dev.kobj, &adf7242_attr_group); ++ if (ret) ++ goto out; ++ ++ dev_info(&spi->dev, "mac802154 IRQ-%d registered\n", spi->irq); ++ ++ return ret; ++ ++out: ++ ieee802154_unregister_device(lp->dev); ++err_irq: ++ free_irq(spi->irq, lp); ++ flush_work(&lp->irqwork); ++err_hw_init: ++ spi_set_drvdata(spi, NULL); ++ mutex_destroy(&lp->bmux); ++ ieee802154_free_device(lp->dev); ++ return ret; ++} ++ ++static int __devexit adf7242_remove(struct spi_device *spi) ++{ ++ struct adf7242_local *lp = spi_get_drvdata(spi); ++ ++ ieee802154_unregister_device(lp->dev); ++ free_irq(spi->irq, lp); ++ flush_work(&lp->irqwork); ++ spi_set_drvdata(spi, NULL); ++ mutex_destroy(&lp->bmux); ++ ieee802154_free_device(lp->dev); ++ ++ return 0; ++} ++ ++static struct spi_driver adf7242_driver = { ++ .driver = { ++ .name = "adf7242", ++ .owner = THIS_MODULE, ++ }, ++ .probe = adf7242_probe, ++ .remove = __devexit_p(adf7242_remove), ++ .suspend = adf7242_suspend, ++ .resume = adf7242_resume, ++}; ++ ++static int __init adf7242_init(void) ++{ ++ return spi_register_driver(&adf7242_driver); ++} ++module_init(adf7242_init); ++ ++static void __exit adf7242_exit(void) ++{ ++ spi_unregister_driver(&adf7242_driver); ++} ++module_exit(adf7242_exit); ++ ++MODULE_AUTHOR("Michael Hennerich "); ++MODULE_DESCRIPTION("ADF7242 Transceiver Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/ieee802154/at86rf230.c b/drivers/ieee802154/at86rf230.c +new file mode 100644 +index 0000000..8da82cf +--- /dev/null ++++ b/drivers/ieee802154/at86rf230.c +@@ -0,0 +1,872 @@ ++/* ++ * AT86RF230/RF231 driver ++ * ++ * Copyright (C) 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_OF ++#include ++#include ++#endif ++ ++#include ++#include ++ ++#include "at86rf230.h" ++ ++ ++struct at86rf230_local { ++ struct spi_device *spi; ++ int rstn, slp_tr, dig2; ++ void (*reset)(void *reset_data); ++ void *reset_data; ++ ++ u8 part; ++ u8 vers; ++ ++ u8 buf[2]; ++ struct mutex bmux; ++ ++ struct work_struct irqwork; ++ struct completion tx_complete; ++ ++ struct ieee802154_dev *dev; ++ ++ volatile unsigned is_tx:1; ++}; ++ ++ ++static int ++__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 2, ++ .tx_buf = buf, ++ }; ++ ++ buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE; ++ buf[1] = data; ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ ++ return status; ++} ++ ++static int ++__at86rf230_read_subreg(struct at86rf230_local *lp, ++ u8 addr, u8 mask, int shift, u8 *data) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 2, ++ .tx_buf = buf, ++ .rx_buf = buf, ++ }; ++ ++ buf[0] = (addr & CMD_REG_MASK) | CMD_REG; ++ buf[1] = 0xff; ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ ++ if (status == 0) ++ *data = buf[1]; ++ ++ return status; ++} ++ ++static int ++at86rf230_read_subreg(struct at86rf230_local *lp, ++ u8 addr, u8 mask, int shift, u8 *data) ++{ ++ int status; ++ ++ mutex_lock(&lp->bmux); ++ status = __at86rf230_read_subreg(lp, addr, mask, shift, data); ++ mutex_unlock(&lp->bmux); ++ ++ return status; ++} ++ ++static int ++at86rf230_write_subreg(struct at86rf230_local *lp, ++ u8 addr, u8 mask, int shift, u8 data) ++{ ++ int status; ++ u8 val; ++ ++ mutex_lock(&lp->bmux); ++ status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val); ++ if (status) ++ goto out; ++ ++ val &= ~mask; ++ val |= (data << shift) & mask; ++ ++ status = __at86rf230_write(lp, addr, val); ++out: ++ mutex_unlock(&lp->bmux); ++ ++ return status; ++} ++ ++static int ++at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = buf, ++ ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = len, ++ .tx_buf = data, ++ }; ++ ++ mutex_lock(&lp->bmux); ++ buf[0] = CMD_WRITE | CMD_FB; ++ buf[1] = len + 2; /* 2 bytes for CRC that isn't written */ ++ ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ ++ mutex_unlock(&lp->bmux); ++ return status; ++} ++ ++static int ++at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi) ++{ ++ u8 *buf = lp->buf; ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = buf, ++ .rx_buf = buf, ++ }; ++ struct spi_transfer xfer_head1 = { ++ .len = 2, ++ .tx_buf = buf, ++ .rx_buf = buf, ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = 0, ++ .rx_buf = data, ++ }; ++ ++ mutex_lock(&lp->bmux); ++ ++ buf[0] = CMD_FB; ++ buf[1] = 0x00; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ ++ if (buf[1] & 0x80) { ++ dev_err(&lp->spi->dev, "invalid PHR 0x%02x\n", buf[1]); ++ status = -EIO; ++ goto fail; ++ } ++ if (buf[1] >= *len) { ++ dev_err(&lp->spi->dev, "PHR 0x%02x >= buffer %d bytes\n", ++ buf[1], *len); ++ status = -EMSGSIZE; ++ goto fail; ++ } ++ xfer_buf.len = *(buf + 1) + 1; ++ *len = buf[1]; ++ ++ buf[0] = CMD_FB; ++ buf[1] = 0x00; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head1, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); ++ ++ if (!status) { ++ if (lqi && *len > lp->buf[1]) ++ *lqi = data[lp->buf[1]]; ++ } ++ ++fail: ++ mutex_unlock(&lp->bmux); ++ ++ return status; ++} ++ ++static int ++at86rf230_ed(struct ieee802154_dev *dev, u8 *level) ++{ ++ pr_debug("%s\n", __func__); ++ might_sleep(); ++ BUG_ON(!level); ++ *level = 0xbe; ++ return 0; ++} ++ ++static int ++at86rf230_state(struct ieee802154_dev *dev, int state) ++{ ++ struct at86rf230_local *lp = dev->priv; ++ int rc; ++ u8 val; ++ u8 desired_status; ++ ++ pr_debug("%s %d\n", __func__/*, priv->cur_state*/, state); ++ might_sleep(); ++ ++ if (state == STATE_FORCE_TX_ON) ++ desired_status = STATE_TX_ON; ++ else if (state == STATE_FORCE_TRX_OFF) ++ desired_status = STATE_TRX_OFF; ++ else ++ desired_status = state; ++ ++ do { ++ rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); ++ if (rc) ++ goto err; ++ pr_debug("%s val1 = %x\n", __func__, val); ++ } while (val == STATE_TRANSITION_IN_PROGRESS); ++ ++ if (val == desired_status) ++ return 0; ++ ++ /* state is equal to phy states */ ++ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state); ++ if (rc) ++ goto err; ++ ++ do { ++ rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); ++ if (rc) ++ goto err; ++ pr_debug("%s val2 = %x\n", __func__, val); ++ } while (val == STATE_TRANSITION_IN_PROGRESS); ++ ++ ++ if (val == desired_status) ++ return 0; ++ if (state == STATE_RX_ON && val == STATE_BUSY_RX) ++ return 0; ++ ++ pr_err("%s unexpected state change: %d, asked for %d\n", __func__, ++ val, state); ++ return -EBUSY; ++ ++err: ++ pr_err("%s error: %d\n", __func__, rc); ++ return rc; ++} ++ ++static int ++at86rf230_start(struct ieee802154_dev *dev) ++{ ++ struct at86rf230_local *lp = dev->priv; ++ u8 rc; ++ ++ rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); ++ if (rc) ++ return rc; ++ return at86rf230_state(dev, STATE_RX_ON); ++} ++ ++static void ++at86rf230_stop(struct ieee802154_dev *dev) ++{ ++ at86rf230_state(dev, STATE_FORCE_TRX_OFF); ++} ++ ++static int ++at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) ++{ ++ struct at86rf230_local *lp = dev->priv; ++ int rc; ++ ++ pr_debug("%s %d\n", __func__, channel); ++ might_sleep(); ++ ++ BUG_ON(page != 0); ++ BUG_ON(channel < 11); ++ BUG_ON(channel > 26); ++ ++ rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel); ++ msleep(1); /* Wait for PLL */ ++ dev->phy->current_channel = channel; ++ ++ return 0; ++} ++ ++/* FIXME: ++ * This function currently is a mess. It uses flush_work to guard ++ * against concurrent irqwork, etc. One has to use mutexes intead. */ ++static int ++at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ++{ ++ struct at86rf230_local *lp = dev->priv; ++ int rc; ++ ++ pr_debug("%s\n", __func__); ++ ++ might_sleep(); ++ ++ BUG_ON(lp->is_tx); ++ INIT_COMPLETION(lp->tx_complete); ++ ++ rc = at86rf230_state(dev, STATE_FORCE_TX_ON); ++ if (rc) ++ goto err; ++ ++ synchronize_irq(lp->spi->irq); ++ flush_work(&lp->irqwork); ++ ++ lp->is_tx = 1; ++ ++ rc = at86rf230_write_fbuf(lp, skb->data, skb->len); ++ if (rc) ++ goto err_rx; ++ ++ if (gpio_is_valid(lp->slp_tr)) { ++ gpio_set_value(lp->slp_tr, 1); ++ udelay(80); /* > 62.5 */ ++ gpio_set_value(lp->slp_tr, 0); ++ } else { ++ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX); ++ if (rc) ++ goto err_rx; ++ } ++ ++ /* FIXME: the logic is really strange here. Datasheet doesn't ++ * provide us enough info about behaviour in such cases. ++ * Basically either we were interrupted here, or we have lost ++ * the interrupt. Most probably this should be changed to ++ * wait_for_completion_timeout() and handle it's results ++ */ ++ rc = wait_for_completion_interruptible(&lp->tx_complete); ++ if (rc < 0) ++ goto err_state; ++ ++ lp->is_tx = 0; ++ ++ rc = at86rf230_start(dev); ++ return rc; ++ ++err_state: ++ /* try to recover from possibly problematic state */ ++ at86rf230_state(dev, STATE_FORCE_TX_ON); ++ synchronize_irq(lp->spi->irq); ++ flush_work(&lp->irqwork); ++ lp->is_tx = 0; ++err_rx: ++ at86rf230_start(dev); ++err: ++ if (rc) ++ pr_err("%s error: %d\n", __func__, rc); ++ ++ return rc; ++} ++ ++static int at86rf230_rx(struct at86rf230_local *lp) ++{ ++ u8 len = 128, lqi = 0; ++ int rc, rc2; ++ struct sk_buff *skb; ++ ++ skb = alloc_skb(len, GFP_KERNEL); ++ if (!skb) ++ return -ENOMEM; ++ ++ /* FIXME: process return status */ ++ rc = at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 1); ++ rc2 = at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi); ++ rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); ++ rc = at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 0); ++ if (rc2 < 0) ++ goto err_fbuf; ++ ++ if (len < 2) ++ goto err; ++ ++ skb_trim(skb, len-2); /* We do not put CRC into the frame */ ++ ++ ++ ieee802154_rx_irqsafe(lp->dev, skb, lqi); ++ ++ dev_dbg(&lp->spi->dev, "READ_FBUF: %d %d %x\n", rc, len, lqi); ++ ++ return 0; ++err: ++ pr_debug("%s: received frame is too small\n", __func__); ++ ++err_fbuf: ++ kfree_skb(skb); ++ return -EINVAL; ++} ++ ++static struct ieee802154_ops at86rf230_ops = { ++ .owner = THIS_MODULE, ++ .xmit = at86rf230_xmit, ++ .ed = at86rf230_ed, ++ .set_channel = at86rf230_channel, ++ .start = at86rf230_start, ++ .stop = at86rf230_stop, ++}; ++ ++static void at86rf230_irqwork(struct work_struct *work) ++{ ++ struct at86rf230_local *lp = ++ container_of(work, struct at86rf230_local, irqwork); ++ u8 status = 0, val; ++ int rc; ++ ++ dev_dbg(&lp->spi->dev, "IRQ Worker\n"); ++ ++ do { ++ rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val); ++ status |= val; ++ dev_dbg(&lp->spi->dev, "IRQ Status: %02x\n", status); ++ ++ status &= ~IRQ_PLL_LOCK; /* ignore */ ++ status &= ~IRQ_RX_START; /* ignore */ ++ status &= ~IRQ_AMI; /* ignore */ ++ status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/ ++ ++ if (status & IRQ_TRX_END) { ++ status &= ~IRQ_TRX_END; ++ if (lp->is_tx) ++ complete(&lp->tx_complete); ++ else ++ at86rf230_rx(lp); ++ } ++ ++ } while (status != 0); ++ ++ enable_irq(lp->spi->irq); ++} ++ ++static irqreturn_t at86rf230_isr(int irq, void *data) ++{ ++ struct at86rf230_local *lp = data; ++ ++ dev_dbg(&lp->spi->dev, "IRQ!\n"); ++ ++ disable_irq_nosync(irq); ++ schedule_work(&lp->irqwork); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int at86rf230_hw_init(struct at86rf230_local *lp) ++{ ++ u8 status; ++ int rc; ++ ++ rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); ++ if (rc) ++ return rc; ++ ++ dev_info(&lp->spi->dev, "Status: %02x\n", status); ++ if (status == STATE_P_ON) { ++ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF); ++ if (rc) ++ return rc; ++ msleep(1); ++ rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); ++ if (rc) ++ return rc; ++ dev_info(&lp->spi->dev, "Status: %02x\n", status); ++ } ++ ++ rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, ++ /*IRQ_TRX_UR | IRQ_CCA_ED | IRQ_TRX_END | IRQ_PLL_UNL | IRQ_PLL_LOCK*/ 0xff); ++ if (rc) ++ return rc; ++ ++ /* CLKM changes are applied immediately */ ++ rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); ++ if (rc) ++ return rc; ++ ++ /* Turn CLKM Off */ ++ rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); ++ if (rc) ++ return rc; ++ ++ msleep(100); ++ ++ rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON); ++ if (rc) ++ return rc; ++ msleep(1); ++ ++ rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); ++ if (rc) ++ return rc; ++ dev_info(&lp->spi->dev, "Status: %02x\n", status); ++ ++ rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status); ++ if (rc) ++ return rc; ++ if (!status) { ++ dev_err(&lp->spi->dev, "DVDD error\n"); ++ return -EINVAL; ++ } ++ ++ rc = at86rf230_read_subreg(lp, SR_AVDD_OK, &status); ++ if (rc) ++ return rc; ++ if (!status) { ++ dev_err(&lp->spi->dev, "AVDD error\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int at86rf230_suspend(struct spi_device *spi, pm_message_t message) ++{ ++ return 0; ++} ++ ++static int at86rf230_resume(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static int at86rf230_fill_data(struct spi_device *spi) ++{ ++ struct device_node *np = spi->dev.of_node; ++ struct at86rf230_local *lp = spi_get_drvdata(spi); ++ struct at86rf230_platform_data *pdata = spi->dev.platform_data; ++ enum of_gpio_flags gpio_flags; ++ ++ if (pdata) { ++ lp->rstn = pdata->rstn; ++ lp->slp_tr = pdata->slp_tr; ++ lp->dig2 = pdata->dig2; ++ lp->reset = pdata->reset; ++ lp->reset_data = pdata->reset_data; ++ ++ return 0; ++ } ++ ++ if (!np) { ++ dev_err(&spi->dev, "no platform_data and no node data\n"); ++ return -EINVAL; ++ } ++ ++ lp->rstn = of_get_gpio_flags(np, 0, &gpio_flags); ++ if (!gpio_is_valid(lp->rstn)) { ++ dev_err(&spi->dev, "no RSTN GPIO!\n"); ++ return -EINVAL; ++ } ++ ++ lp->slp_tr = of_get_gpio_flags(np, 1, &gpio_flags); ++ lp->dig2 = of_get_gpio_flags(np, 2, &gpio_flags); ++ ++ lp->reset = NULL; ++ ++ return 0; ++} ++#else ++static int at86rf230_fill_data(struct spi_device *spi) ++{ ++ struct at86rf230_local *lp = spi_get_drvdata(spi); ++ struct at86rf230_platform_data *pdata = spi->dev.platform_data; ++ ++ if (!pdata) { ++ dev_err(&spi->dev, "no platform_data\n"); ++ return -EINVAL; ++ } ++ ++ lp->rstn = pdata->rstn; ++ lp->slp_tr = pdata->slp_tr; ++ lp->dig2 = pdata->dig2; ++ lp->reset = pdata->reset; ++ lp->reset_data = pdata->reset_data; ++ ++ return 0; ++} ++#endif ++ ++static int __devinit at86rf230_probe(struct spi_device *spi) ++{ ++ struct ieee802154_dev *dev; ++ struct at86rf230_local *lp; ++ u8 man_id_0, man_id_1; ++ int rc; ++ const char *chip; ++ int supported = 0; ++ ++ if (spi->irq < 0) { ++ dev_err(&spi->dev, "no IRQ specified\n"); ++ return -EINVAL; ++ } ++ ++ dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); ++ if (!dev) ++ return -ENOMEM; ++ ++ lp = dev->priv; ++ lp->dev = dev; ++ ++ lp->spi = spi; ++ ++ dev->priv = lp; ++ dev->parent = &spi->dev; ++ dev->extra_tx_headroom = 0; ++ /* We do support only 2.4 Ghz */ ++ dev->phy->channels_supported[0] = 0x7FFF800; ++ dev->flags = IEEE802154_HW_OMIT_CKSUM; ++ ++ mutex_init(&lp->bmux); ++ INIT_WORK(&lp->irqwork, at86rf230_irqwork); ++ init_completion(&lp->tx_complete); ++ ++ spi_set_drvdata(spi, lp); ++ ++ rc = at86rf230_fill_data(spi); ++ if (rc) ++ goto err_fill; ++ ++ if (gpio_is_valid(lp->rstn)) { ++ rc = gpio_request(lp->rstn, "rstn"); ++ if (rc) ++ goto err_rstn; ++ } ++ ++ if (gpio_is_valid(lp->slp_tr)) { ++ rc = gpio_request(lp->slp_tr, "slp_tr"); ++ if (rc) ++ goto err_slp_tr; ++ } ++ ++ if (gpio_is_valid(lp->rstn)) { ++ rc = gpio_direction_output(lp->rstn, 1); ++ if (rc) ++ goto err_gpio_dir; ++ } ++ ++ if (gpio_is_valid(lp->slp_tr)) { ++ rc = gpio_direction_output(lp->slp_tr, 0); ++ if (rc) ++ goto err_gpio_dir; ++ } ++ ++ /* Reset */ ++ if (lp->reset) ++ lp->reset(lp->reset_data); ++ else { ++ msleep(1); ++ gpio_set_value(lp->rstn, 0); ++ msleep(1); ++ gpio_set_value(lp->rstn, 1); ++ msleep(1); ++ } ++ ++ rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0); ++ if (rc) ++ goto err_gpio_dir; ++ rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1); ++ if (rc) ++ goto err_gpio_dir; ++ ++ if (man_id_1 != 0x00 || man_id_0 != 0x1f) { ++ dev_err(&spi->dev, "Non-Atmel device found (MAN_ID:" ++ "%02x %02x)\n", man_id_1, man_id_0); ++ rc = -EINVAL; ++ goto err_gpio_dir; ++ } ++ ++ rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part); ++ if (rc) ++ goto err_gpio_dir; ++ ++ rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers); ++ if (rc) ++ goto err_gpio_dir; ++ ++ switch (lp->part) { ++ case 2: ++ chip = "at86rf230"; ++ /* supported = 1; FIXME: should be easy to support; */ ++ break; ++ case 3: ++ chip = "at86rf231"; ++ supported = 1; ++ break; ++ default: ++ chip = "UNKNOWN"; ++ break; ++ } ++ ++ dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers); ++ if (!supported) { ++ rc = -ENOTSUPP; ++ goto err_gpio_dir; ++ } ++ ++ rc = at86rf230_hw_init(lp); ++ if (rc) ++ goto err_gpio_dir; ++ ++ rc = request_irq(spi->irq, at86rf230_isr, IRQF_SHARED, ++ dev_name(&spi->dev), lp); ++ if (rc) ++ goto err_gpio_dir; ++ ++ dev_dbg(&spi->dev, "registered at86rf230\n"); ++ ++ rc = ieee802154_register_device(lp->dev); ++ if (rc) ++ goto err_irq; ++ ++ return rc; ++ ++err_irq: ++ disable_irq(spi->irq); ++ flush_work(&lp->irqwork); ++ free_irq(spi->irq, lp); ++err_gpio_dir: ++ if (gpio_is_valid(lp->slp_tr)) ++ gpio_free(lp->slp_tr); ++err_slp_tr: ++ if (gpio_is_valid(lp->rstn)) ++ gpio_free(lp->rstn); ++err_rstn: ++err_fill: ++ spi_set_drvdata(spi, NULL); ++ mutex_destroy(&lp->bmux); ++ ieee802154_free_device(lp->dev); ++ return rc; ++} ++ ++static int __devexit at86rf230_remove(struct spi_device *spi) ++{ ++ struct at86rf230_local *lp = spi_get_drvdata(spi); ++ ++ /* ++ * @@@ this looks wrong - what if a frame arrives before ++ * disable_irq ? -- wa ++ */ ++ ieee802154_unregister_device(lp->dev); ++ ++ disable_irq(spi->irq); ++ flush_work(&lp->irqwork); ++ free_irq(spi->irq, lp); ++ ++ if (gpio_is_valid(lp->slp_tr)) ++ gpio_free(lp->slp_tr); ++ if (gpio_is_valid(lp->rstn)) ++ gpio_free(lp->rstn); ++ ++ spi_set_drvdata(spi, NULL); ++ mutex_destroy(&lp->bmux); ++ ieee802154_free_device(lp->dev); ++ ++ dev_dbg(&spi->dev, "unregistered at86rf230\n"); ++ return 0; ++} ++ ++static struct spi_driver at86rf230_driver = { ++ .driver = { ++ .name = "at86rf230", ++ .owner = THIS_MODULE, ++ }, ++ .probe = at86rf230_probe, ++ .remove = __devexit_p(at86rf230_remove), ++ .suspend = at86rf230_suspend, ++ .resume = at86rf230_resume, ++}; ++ ++static int __init at86rf230_init(void) ++{ ++ return spi_register_driver(&at86rf230_driver); ++} ++module_init(at86rf230_init); ++ ++static void __exit at86rf230_exit(void) ++{ ++ spi_unregister_driver(&at86rf230_driver); ++} ++module_exit(at86rf230_exit); ++ ++MODULE_DESCRIPTION("AT86RF230 Transceiver Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/ieee802154/at86rf230.h b/drivers/ieee802154/at86rf230.h +new file mode 100644 +index 0000000..fcb0e11 +--- /dev/null ++++ b/drivers/ieee802154/at86rf230.h +@@ -0,0 +1,211 @@ ++/* ++ * AT86RF230/RF231 register and protocol definitions ++ * ++ * Copyright (C) 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#ifndef ATR86F230_H ++#define ATR86F230_H ++ ++#define RG_TRX_STATUS (0x01) ++#define SR_TRX_STATUS 0x01, 0x1f, 0 ++#define SR_RESERVED_01_3 0x01, 0x20, 5 ++#define SR_CCA_STATUS 0x01, 0x40, 6 ++#define SR_CCA_DONE 0x01, 0x80, 7 ++#define RG_TRX_STATE (0x02) ++#define SR_TRX_CMD 0x02, 0x1f, 0 ++#define SR_TRAC_STATUS 0x02, 0xe0, 5 ++#define RG_TRX_CTRL_0 (0x03) ++#define SR_CLKM_CTRL 0x03, 0x07, 0 ++#define SR_CLKM_SHA_SEL 0x03, 0x08, 3 ++#define SR_PAD_IO_CLKM 0x03, 0x30, 4 ++#define SR_PAD_IO 0x03, 0xc0, 6 ++#define RG_TRX_CTRL_1 (0x04) ++#define SR_IRQ_POLARITY 0x04, 0x01, 0 ++#define SR_IRQ_MASK_MODE 0x04, 0x02, 1 ++#define SR_SPI_CMD_MODE 0x04, 0x0c, 2 ++#define SR_RX_BL_CTRL 0x04, 0x10, 4 ++#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5 ++#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6 ++#define SR_PA_EXT_EN 0x04, 0x80, 7 ++#define RG_PHY_TX_PWR (0x05) ++#define SR_TX_PWR 0x05, 0x0f, 0 ++#define SR_PA_LT 0x05, 0x30, 4 ++#define SR_PA_BUF_LT 0x05, 0xc0, 6 ++#define RG_PHY_RSSI (0x06) ++#define SR_RSSI 0x06, 0x1f, 0 ++#define SR_RND_VALUE 0x06, 0x60, 5 ++#define SR_RX_CRC_VALID 0x06, 0x80, 7 ++#define RG_PHY_ED_LEVEL (0x07) ++#define SR_ED_LEVEL 0x07, 0xff, 0 ++#define RG_PHY_CC_CCA (0x08) ++#define SR_CHANNEL 0x08, 0x1f, 0 ++#define SR_CCA_MODE 0x08, 0x60, 5 ++#define SR_CCA_REQUEST 0x08, 0x80, 7 ++#define RG_CCA_THRES (0x09) ++#define SR_CCA_ED_THRES 0x09, 0x0f, 0 ++#define SR_RESERVED_09_1 0x09, 0xf0, 4 ++#define RG_RX_CTRL (0x0a) ++#define SR_PDT_THRES 0x0a, 0x0f, 0 ++#define SR_RESERVED_0a_1 0x0a, 0xf0, 4 ++#define RG_SFD_VALUE (0x0b) ++#define SR_SFD_VALUE 0x0b, 0xff, 0 ++#define RG_TRX_CTRL_2 (0x0c) ++#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0 ++#define SR_RESERVED_0c_2 0x0c, 0x7c, 2 ++#define SR_RX_SAFE_MODE 0x0c, 0x80, 7 ++#define RG_ANT_DIV (0x0d) ++#define SR_ANT_CTRL 0x0d, 0x03, 0 ++#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2 ++#define SR_ANT_DIV_EN 0x0d, 0x08, 3 ++#define SR_RESERVED_0d_2 0x0d, 0x70, 4 ++#define SR_ANT_SEL 0x0d, 0x80, 7 ++#define RG_IRQ_MASK (0x0e) ++#define SR_IRQ_MASK 0x0e, 0xff, 0 ++#define RG_IRQ_STATUS (0x0f) ++#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0 ++#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1 ++#define SR_IRQ_2_RX_START 0x0f, 0x04, 2 ++#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3 ++#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4 ++#define SR_IRQ_5_AMI 0x0f, 0x20, 5 ++#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6 ++#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7 ++#define RG_VREG_CTRL (0x10) ++#define SR_RESERVED_10_6 0x10, 0x03, 0 ++#define SR_DVDD_OK 0x10, 0x04, 2 ++#define SR_DVREG_EXT 0x10, 0x08, 3 ++#define SR_RESERVED_10_3 0x10, 0x30, 4 ++#define SR_AVDD_OK 0x10, 0x40, 6 ++#define SR_AVREG_EXT 0x10, 0x80, 7 ++#define RG_BATMON (0x11) ++#define SR_BATMON_VTH 0x11, 0x0f, 0 ++#define SR_BATMON_HR 0x11, 0x10, 4 ++#define SR_BATMON_OK 0x11, 0x20, 5 ++#define SR_RESERVED_11_1 0x11, 0xc0, 6 ++#define RG_XOSC_CTRL (0x12) ++#define SR_XTAL_TRIM 0x12, 0x0f, 0 ++#define SR_XTAL_MODE 0x12, 0xf0, 4 ++#define RG_RX_SYN (0x15) ++#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0 ++#define SR_RESERVED_15_2 0x15, 0x70, 4 ++#define SR_RX_PDT_DIS 0x15, 0x80, 7 ++#define RG_XAH_CTRL_1 (0x17) ++#define SR_RESERVED_17_8 0x17, 0x01, 0 ++#define SR_AACK_PROM_MODE 0x17, 0x02, 1 ++#define SR_AACK_ACK_TIME 0x17, 0x04, 2 ++#define SR_RESERVED_17_5 0x17, 0x08, 3 ++#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4 ++#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5 ++#define SR_RESERVED_17_2 0x17, 0x40, 6 ++#define SR_RESERVED_17_1 0x17, 0x80, 7 ++#define RG_FTN_CTRL (0x18) ++#define SR_RESERVED_18_2 0x18, 0x7f, 0 ++#define SR_FTN_START 0x18, 0x80, 7 ++#define RG_PLL_CF (0x1a) ++#define SR_RESERVED_1a_2 0x1a, 0x7f, 0 ++#define SR_PLL_CF_START 0x1a, 0x80, 7 ++#define RG_PLL_DCU (0x1b) ++#define SR_RESERVED_1b_3 0x1b, 0x3f, 0 ++#define SR_RESERVED_1b_2 0x1b, 0x40, 6 ++#define SR_PLL_DCU_START 0x1b, 0x80, 7 ++#define RG_PART_NUM (0x1c) ++#define SR_PART_NUM 0x1c, 0xff, 0 ++#define RG_VERSION_NUM (0x1d) ++#define SR_VERSION_NUM 0x1d, 0xff, 0 ++#define RG_MAN_ID_0 (0x1e) ++#define SR_MAN_ID_0 0x1e, 0xff, 0 ++#define RG_MAN_ID_1 (0x1f) ++#define SR_MAN_ID_1 0x1f, 0xff, 0 ++#define RG_SHORT_ADDR_0 (0x20) ++#define SR_SHORT_ADDR_0 0x20, 0xff, 0 ++#define RG_SHORT_ADDR_1 (0x21) ++#define SR_SHORT_ADDR_1 0x21, 0xff, 0 ++#define RG_PAN_ID_0 (0x22) ++#define SR_PAN_ID_0 0x22, 0xff, 0 ++#define RG_PAN_ID_1 (0x23) ++#define SR_PAN_ID_1 0x23, 0xff, 0 ++#define RG_IEEE_ADDR_0 (0x24) ++#define SR_IEEE_ADDR_0 0x24, 0xff, 0 ++#define RG_IEEE_ADDR_1 (0x25) ++#define SR_IEEE_ADDR_1 0x25, 0xff, 0 ++#define RG_IEEE_ADDR_2 (0x26) ++#define SR_IEEE_ADDR_2 0x26, 0xff, 0 ++#define RG_IEEE_ADDR_3 (0x27) ++#define SR_IEEE_ADDR_3 0x27, 0xff, 0 ++#define RG_IEEE_ADDR_4 (0x28) ++#define SR_IEEE_ADDR_4 0x28, 0xff, 0 ++#define RG_IEEE_ADDR_5 (0x29) ++#define SR_IEEE_ADDR_5 0x29, 0xff, 0 ++#define RG_IEEE_ADDR_6 (0x2a) ++#define SR_IEEE_ADDR_6 0x2a, 0xff, 0 ++#define RG_IEEE_ADDR_7 (0x2b) ++#define SR_IEEE_ADDR_7 0x2b, 0xff, 0 ++#define RG_XAH_CTRL_0 (0x2c) ++#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0 ++#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1 ++#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4 ++#define RG_CSMA_SEED_0 (0x2d) ++#define SR_CSMA_SEED_0 0x2d, 0xff, 0 ++#define RG_CSMA_SEED_1 (0x2e) ++#define SR_CSMA_SEED_1 0x2e, 0x07, 0 ++#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3 ++#define SR_AACK_DIS_ACK 0x2e, 0x10, 4 ++#define SR_AACK_SET_PD 0x2e, 0x20, 5 ++#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6 ++#define RG_CSMA_BE (0x2f) ++#define SR_MIN_BE 0x2f, 0x0f, 0 ++#define SR_MAX_BE 0x2f, 0xf0, 4 ++ ++#define CMD_REG 0x80 ++#define CMD_REG_MASK 0x3f ++#define CMD_WRITE 0x40 ++#define CMD_FB 0x20 ++ ++#define IRQ_BAT_LOW (1 << 7) ++#define IRQ_TRX_UR (1 << 6) ++#define IRQ_AMI (1 << 5) ++#define IRQ_CCA_ED (1 << 4) ++#define IRQ_TRX_END (1 << 3) ++#define IRQ_RX_START (1 << 2) ++#define IRQ_PLL_UNL (1 << 1) ++#define IRQ_PLL_LOCK (1 << 0) ++ ++#define STATE_P_ON 0x00 /* BUSY */ ++#define STATE_BUSY_RX 0x01 ++#define STATE_BUSY_TX 0x02 ++#define STATE_FORCE_TRX_OFF 0x03 ++#define STATE_FORCE_TX_ON 0x04 /* IDLE */ ++/* 0x05 */ /* INVALID_PARAMETER */ ++#define STATE_RX_ON 0x06 ++/* 0x07 */ /* SUCCESS */ ++#define STATE_TRX_OFF 0x08 ++#define STATE_TX_ON 0x09 ++/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */ ++#define STATE_SLEEP 0x0F ++#define STATE_BUSY_RX_AACK 0x11 ++#define STATE_BUSY_TX_ARET 0x12 ++#define STATE_BUSY_RX_AACK_ON 0x16 ++#define STATE_BUSY_TX_ARET_ON 0x19 ++#define STATE_RX_ON_NOCLK 0x1C ++#define STATE_RX_AACK_ON_NOCLK 0x1D ++#define STATE_BUSY_RX_AACK_NOCLK 0x1E ++#define STATE_TRANSITION_IN_PROGRESS 0x1F ++ ++#endif /* !ATR86F230_H */ +diff --git a/drivers/ieee802154/cc2420.c b/drivers/ieee802154/cc2420.c +new file mode 100644 +index 0000000..50761de +--- /dev/null ++++ b/drivers/ieee802154/cc2420.c +@@ -0,0 +1,859 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Author: Jonathan Cameron ++ * ++ * Modified 2010: xue liu ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CC2420_WRITEREG(x) (x) ++#define CC2420_READREG(x) (0x40 | x) ++#define CC2420_RAMADDR(x) ((x & 0x7F) | 0x80) ++#define CC2420_RAMBANK(x) ((x >> 1) & 0xc0) ++#define CC2420_WRITERAM(x) (x) ++#define CC2420_READRAM(x) (0x20 | x) ++ ++#define CC2420_FREQ_MASK 0x3FF ++#define CC2420_ADR_DECODE_MASK 0x0B00 ++#define CC2420_FIFOP_THR_MASK 0x003F ++#define CC2420_CRC_MASK 0x80 ++#define CC2420_RSSI_MASK 0x7F ++#define CC2420_FSMSTATE_MASK 0x2F ++ ++#define CC2420_MANFIDLOW 0x233D ++#define CC2420_MANFIDHIGH 0x3000 /* my chip appears to version 3 - broaden this with testing */ ++ ++#define RSSI_OFFSET 45 ++ ++#define STATE_PDOWN 0 ++#define STATE_IDLE 1 ++#define STATE_RX_CALIBRATE 2 ++#define STATE_RX_CALIBRATE2 40 ++ ++#define STATE_RX_SFD_SEARCH_MIN 3 ++#define STATE_RX_SFD_SEARCH_MAX 6 ++#define STATE_RX_FRAME 16 ++#define STATE_RX_FRAME2 40 ++#define STATE_RX_WAIT 14 ++#define STATE_RX_OVERFLOW 17 ++#define STATE_TX_ACK_CALIBRATE 48 ++#define STATE_TX_ACK_PREAMBLE_MIN 49 ++#define STATE_TX_ACK_PREAMBLE_MAX 51 ++#define STATE_TX_ACK_MIN 52 ++#define STATE_TX_ACK_MAX 54 ++#define STATE_TX_CALIBRATE 32 ++#define STATE_TX_PREAMBLE_MIN 34 ++#define STATE_TX_PREAMBLE_MAX 36 ++#define STATE_TX_FRAME_MIN 37 ++#define STATE_TX_FRAME_MAX 39 ++#define STATE_TX_UNDERFLOW 56 ++ ++struct cc2420_local { ++ struct cc2420_platform_data *pdata; ++ struct spi_device *spi; ++ struct ieee802154_dev *dev; ++ u8 *buf; ++ struct mutex bmux; ++ int fifop_irq; ++ int sfd_irq; ++ struct work_struct fifop_irqwork; ++ struct work_struct sfd_irqwork; ++ spinlock_t lock; ++ unsigned irq_disabled:1;/* P:lock */ ++ unsigned is_tx:1; /* P:lock */ ++ ++ struct completion tx_complete; ++}; ++static int cc2420_get_status(struct cc2420_local *lp, u8 *status) ++{ ++ int ret; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 1, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_WRITEREG(CC2420_SNOP); ++ dev_vdbg(&lp->spi->dev, "get status command buf[0] = %02x\n", lp->buf[0]); ++ ret = spi_sync(lp->spi, &msg); ++ if (!ret) ++ *status = lp->buf[0]; ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]); ++ mutex_unlock(&lp->bmux); ++ return ret; ++ ++} ++static int cc2420_cmd_strobe(struct cc2420_local *lp, ++ u8 addr) ++{ ++ int ret; ++ u8 status = 0xf; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 1, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_WRITEREG(addr); ++ dev_vdbg(&lp->spi->dev, "cmd strobe buf[0] = %02x\n", lp->buf[0]); ++ ret = spi_sync(lp->spi, &msg); ++ if (!ret) ++ status = lp->buf[0]; ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]); ++ ++ mutex_unlock(&lp->bmux); ++ ++ return ret; ++} ++ ++static int cc2420_read_16_bit_reg(struct cc2420_local *lp, ++ u8 addr, u16 *data) ++{ ++ int ret; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 3, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_READREG(addr); ++ dev_vdbg(&lp->spi->dev, "readreg addr buf[0] = %02x\n", lp->buf[0]); ++ ret = spi_sync(lp->spi, &msg); ++ dev_dbg(&lp->spi->dev, "status = %d\n", ret); ++ mutex_unlock(&lp->bmux); ++ dev_dbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]); ++ if (!ret) ++ *data = ((u16) (lp->buf[1]) << 8) | lp->buf[2]; ++ return ret; ++} ++ ++static int cc2420_write_16_bit_reg_partial(struct cc2420_local *lp, ++ u8 addr, u16 data, u16 mask) ++{ ++ int ret; ++ struct spi_message msg; ++ struct spi_transfer xfer = { ++ .len = 3, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ dev_dbg(&lp->spi->dev, "data = %x\n", data); ++ dev_dbg(&lp->spi->dev, "mask = %x\n", mask); ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer, &msg); ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_READREG(addr); ++ dev_vdbg(&lp->spi->dev, "read addr buf[0] = %02x\n", lp->buf[0]); ++ ret = spi_sync(lp->spi, &msg); ++ if (ret) ++ goto err_ret; ++ dev_dbg(&lp->spi->dev, "read buf[0] = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]); ++ ++ lp->buf[0] = CC2420_WRITEREG(addr); ++ ++ lp->buf[1] &= ~(mask >> 8); ++ lp->buf[2] &= ~(mask & 0xFF); ++ lp->buf[1] |= (mask >> 8) & (data >> 8); ++ lp->buf[2] |= (mask & 0xFF) & (data & 0xFF); ++ dev_vdbg(&lp->spi->dev, "writereg addr buf[0] = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]); ++ ret = spi_sync(lp->spi, &msg); ++ if (ret) ++ goto err_ret; ++ dev_dbg(&lp->spi->dev, "return status buf[0] = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]); ++ ++err_ret: ++ mutex_unlock(&lp->bmux); ++ return ret; ++} ++ ++static int cc2420_channel(struct ieee802154_dev *dev, int page, int channel) ++{ ++ struct cc2420_local *lp = dev->priv; ++ int ret; ++ ++ might_sleep(); ++ dev_dbg(&lp->spi->dev, "trying to set channel\n"); ++ ++ BUG_ON(page != 0); ++ BUG_ON(channel < CC2420_MIN_CHANNEL); ++ BUG_ON(channel > CC2420_MAX_CHANNEL); ++ ++ ret = cc2420_write_16_bit_reg_partial(lp, CC2420_FSCTRL, 357 + 5*(channel - 11), CC2420_FREQ_MASK); ++ ++ dev->phy->current_channel = channel; ++ return ret; ++} ++ ++static int cc2420_write_ram(struct cc2420_local *lp, u16 addr, u8 len, u8 *data) ++{ ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = len, ++ .tx_buf = data, ++ }; ++ ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_RAMADDR(addr); ++ lp->buf[1] = CC2420_WRITERAM(CC2420_RAMBANK(addr)); ++ dev_dbg(&lp->spi->dev, "write ram addr buf[0] = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "ram bank buf[1] = %02x\n", lp->buf[1]); ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_dbg(&lp->spi->dev, "spi status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_dbg(&lp->spi->dev, "cc2420 status = %02x\n", lp->buf[0]); ++ dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ ++ mutex_unlock(&lp->bmux); ++ return status; ++} ++ ++static int cc2420_write_txfifo(struct cc2420_local *lp, u8 *data, u8 len) ++{ ++ int status; ++ /* Length byte must include FCS even if calculated in hardware */ ++ int len_byte = len + 2; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 1, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ struct spi_transfer xfer_len = { ++ .len = 1, ++ .tx_buf = &len_byte, ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = len, ++ .tx_buf = data, ++ }; ++ ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_WRITEREG(CC2420_TXFIFO); ++ dev_vdbg(&lp->spi->dev, "TX_FIFO addr buf[0] = %02x\n", lp->buf[0]); ++ ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_len, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]); ++ ++ mutex_unlock(&lp->bmux); ++ return status; ++} ++ ++static int ++cc2420_read_rxfifo(struct cc2420_local *lp, u8 *data, u8 *len, u8 *lqi) ++{ ++ int status; ++ struct spi_message msg; ++ struct spi_transfer xfer_head = { ++ .len = 2, ++ .tx_buf = lp->buf, ++ .rx_buf = lp->buf, ++ }; ++ struct spi_transfer xfer_buf = { ++ .len = *len, ++ .rx_buf = data, ++ }; ++ ++ mutex_lock(&lp->bmux); ++ lp->buf[0] = CC2420_READREG(CC2420_RXFIFO); ++ lp->buf[1] = 0x00; ++ dev_vdbg(&lp->spi->dev, "read rxfifo buf[0] = %02x\n", lp->buf[0]); ++ dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]); ++ spi_message_init(&msg); ++ spi_message_add_tail(&xfer_head, &msg); ++ spi_message_add_tail(&xfer_buf, &msg); ++ ++ status = spi_sync(lp->spi, &msg); ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ if (msg.status) ++ status = msg.status; ++ dev_vdbg(&lp->spi->dev, "status = %d\n", status); ++ dev_vdbg(&lp->spi->dev, "return status buf[0] = %02x\n", lp->buf[0]); ++ dev_vdbg(&lp->spi->dev, "length buf[1] = %02x\n", lp->buf[1]); ++ ++ *lqi = data[lp->buf[1] - 1] & 0x7f; ++ *len = lp->buf[1]; /* it should be less than 130 */ ++ ++ mutex_unlock(&lp->bmux); ++ ++ return status; ++} ++ ++ ++static int cc2420_tx(struct ieee802154_dev *dev, struct sk_buff *skb) ++{ ++ struct cc2420_local *lp = dev->priv; ++ int rc; ++ unsigned long flags; ++ u8 status = 0; ++ ++ pr_debug("%s\n", __func__); ++ ++ might_sleep(); ++ ++ rc = cc2420_cmd_strobe(lp, CC2420_SFLUSHTX); ++ if (rc) ++ goto err_rx; ++ rc = cc2420_write_txfifo(lp, skb->data, skb->len); ++ if (rc) ++ goto err_rx; ++ ++ /* TODO: test CCA pin */ ++ ++ rc = cc2420_get_status(lp, &status); ++ if (rc) ++ goto err_rx; ++ ++ if (status & CC2420_STATUS_TX_UNDERFLOW) { ++ dev_err(&lp->spi->dev, "cc2420 tx underflow!\n"); ++ goto err_rx; ++ } ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ BUG_ON(lp->is_tx); ++ lp->is_tx = 1; ++ INIT_COMPLETION(lp->tx_complete); ++ spin_unlock_irqrestore(&lp->lock, flags); ++ ++ rc = cc2420_cmd_strobe(lp, CC2420_STXONCCA); ++ if (rc) ++ goto err; ++ ++ rc = wait_for_completion_interruptible(&lp->tx_complete); ++ if (rc < 0) ++ goto err; ++ ++ cc2420_cmd_strobe(lp, CC2420_SFLUSHTX); ++ cc2420_cmd_strobe(lp, CC2420_SRXON); ++ ++ return rc; ++ ++err: ++ spin_lock_irqsave(&lp->lock, flags); ++ lp->is_tx = 0; ++ spin_unlock_irqrestore(&lp->lock, flags); ++err_rx: ++ cc2420_cmd_strobe(lp, CC2420_SFLUSHTX); ++ cc2420_cmd_strobe(lp, CC2420_SRXON); ++ return rc; ++} ++ ++static int cc2420_rx(struct cc2420_local *lp) ++{ ++ u8 len = 128; ++ u8 lqi = 0; /* link quality */ ++ int rc; ++ struct sk_buff *skb; ++ ++ skb = alloc_skb(len, GFP_KERNEL); ++ if (!skb) ++ return -ENOMEM; ++ ++ rc = cc2420_read_rxfifo(lp, skb_put(skb, len), &len, &lqi); ++ if (len < 2) { ++ kfree_skb(skb); ++ return -EINVAL; ++ } ++ ++ /* Clip last two bytes. When using hardware FCS they get replaced with ++ * correlation value, FCS flag and RSSI value */ ++ skb_trim(skb, len-2); ++ ++ ieee802154_rx_irqsafe(lp->dev, skb, lqi); ++ ++ dev_dbg(&lp->spi->dev, "RXFIFO: %d %d %x\n", rc, len, lqi); ++ ++ return 0; ++} ++ ++static int ++cc2420_set_hw_addr_filt(struct ieee802154_dev *dev, ++ struct ieee802154_hw_addr_filt *filt, ++ unsigned long changed) ++{ ++ struct cc2420_local *lp = dev->priv; ++ u16 reg; ++ ++ might_sleep(); ++ ++ if (changed & IEEE802515_IEEEADDR_CHANGED) ++ cc2420_write_ram(lp, CC2420_RAM_IEEEADR, ++ IEEE802154_ALEN, ++ filt->ieee_addr); ++ ++ if (changed & IEEE802515_SADDR_CHANGED) { ++ u8 short_addr[2]; ++ short_addr[0] = filt->short_addr & 0xff;/* LSB */ ++ short_addr[1] = filt->short_addr >> 8; /* MSB */ ++ cc2420_write_ram(lp, CC2420_RAM_SHORTADR, ++ sizeof(short_addr), ++ short_addr); ++ } ++ ++ if (changed & IEEE802515_PANID_CHANGED) { ++ u8 panid[2]; ++ panid[0] = filt->pan_id & 0xff; /* LSB */ ++ panid[1] = filt->pan_id >> 8; /* MSB */ ++ cc2420_write_ram(lp, CC2420_RAM_PANID, ++ sizeof(panid), ++ panid); ++ } ++ ++ if (changed & IEEE802515_PANC_CHANGED) { ++ cc2420_read_16_bit_reg(lp, CC2420_MDMCTRL0, ®); ++ if (filt->pan_coord) ++ reg |= 1 << CC2420_MDMCTRL0_PANCRD; ++ else ++ reg &= ~(1 << CC2420_MDMCTRL0_PANCRD); ++ cc2420_write_16_bit_reg_partial(lp, CC2420_MDMCTRL0, ++ reg, 1 << CC2420_MDMCTRL0_PANCRD); ++ } ++ ++ return 0; ++} ++ ++static int cc2420_ed(struct ieee802154_dev *dev, u8 *level) ++{ ++ struct cc2420_local *lp = dev->priv; ++ u16 rssi; ++ int ret; ++ dev_dbg(&lp->spi->dev, "ed called\n"); ++ ++ ret = cc2420_read_16_bit_reg(lp, CC2420_RSSI, &rssi); ++ if (ret) ++ return ret; ++ ++ /* P = RSSI_VAL + RSSI_OFFSET[dBm] */ ++ *level = (rssi & CC2420_RSSI_MASK) + RSSI_OFFSET; ++ return ret; ++} ++ ++static int cc2420_start(struct ieee802154_dev *dev) ++{ ++ return cc2420_cmd_strobe(dev->priv, CC2420_SRXON); ++} ++ ++static void cc2420_stop(struct ieee802154_dev *dev) ++{ ++ cc2420_cmd_strobe(dev->priv, CC2420_SRFOFF); ++} ++ ++static struct ieee802154_ops cc2420_ops = { ++ .owner = THIS_MODULE, ++ .xmit = cc2420_tx, ++ .ed = cc2420_ed, ++ .start = cc2420_start, ++ .stop = cc2420_stop, ++ .set_channel = cc2420_channel, ++ .set_hw_addr_filt = cc2420_set_hw_addr_filt, ++}; ++ ++static int cc2420_register(struct cc2420_local *lp) ++{ ++ int ret = -ENOMEM; ++ lp->dev = ieee802154_alloc_device(sizeof(*lp), &cc2420_ops); ++ if (!lp->dev) ++ goto err_ret; ++ ++ lp->dev->priv = lp; ++ lp->dev->parent = &lp->spi->dev; ++ //look this up. ++ lp->dev->extra_tx_headroom = 0; ++ //and this ++ //lp->dev->channel_mask = 0x7ff; ++ //and more. ++ ++ /* We do support only 2.4 Ghz */ ++ lp->dev->phy->channels_supported[0] = 0x7FFF800; ++ lp->dev->flags = IEEE802154_HW_OMIT_CKSUM; ++ ++ dev_dbg(&lp->spi->dev, "registered cc2420\n"); ++ ret = ieee802154_register_device(lp->dev); ++ if (ret) ++ goto err_free_device; ++ ++ return 0; ++err_free_device: ++ ieee802154_free_device(lp->dev); ++err_ret: ++ return ret; ++} ++ ++static void cc2420_unregister(struct cc2420_local *lp) ++{ ++ ieee802154_unregister_device(lp->dev); ++ //check this is needed ++ ieee802154_free_device(lp->dev); ++} ++ ++static irqreturn_t cc2420_isr(int irq, void *data) ++{ ++ struct cc2420_local *lp = data; ++ ++ spin_lock(&lp->lock); ++ if (!lp->irq_disabled) { ++ disable_irq_nosync(irq); ++ lp->irq_disabled = 1; ++ } ++ spin_unlock(&lp->lock); ++ ++ if (irq == lp->sfd_irq) ++ schedule_work(&lp->sfd_irqwork); ++ ++ if (irq == lp->fifop_irq) ++ schedule_work(&lp->fifop_irqwork); ++ ++ return IRQ_HANDLED; ++} ++ ++static void cc2420_fifop_irqwork(struct work_struct *work) ++{ ++ struct cc2420_local *lp ++ = container_of(work, struct cc2420_local, fifop_irqwork); ++ unsigned long flags; ++ ++ dev_dbg(&lp->spi->dev, "fifop interrupt received\n"); ++ ++ if (gpio_get_value(lp->pdata->fifo)) ++ cc2420_rx(lp); ++ else ++ dev_err(&lp->spi->dev, "rxfifo overflow\n"); ++ ++ cc2420_cmd_strobe(lp, CC2420_SFLUSHRX); ++ cc2420_cmd_strobe(lp, CC2420_SFLUSHRX); ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ if (lp->irq_disabled) { ++ lp->irq_disabled = 0; ++ enable_irq(lp->fifop_irq); ++ } ++ spin_unlock_irqrestore(&lp->lock, flags); ++} ++ ++static void cc2420_sfd_irqwork(struct work_struct *work) ++{ ++ struct cc2420_local *lp ++ = container_of(work, struct cc2420_local, sfd_irqwork); ++ unsigned long flags; ++ ++ dev_dbg(&lp->spi->dev, "sfd interrupt received\n"); ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ if (lp->is_tx) { ++ lp->is_tx = 0; ++ spin_unlock_irqrestore(&lp->lock, flags); ++ complete(&lp->tx_complete); ++ } else { ++ spin_unlock_irqrestore(&lp->lock, flags); ++ } ++ ++ spin_lock_irqsave(&lp->lock, flags); ++ if (lp->irq_disabled) { ++ lp->irq_disabled = 0; ++ enable_irq(lp->sfd_irq); ++ } ++ spin_unlock_irqrestore(&lp->lock, flags); ++} ++ ++static int cc2420_hw_init(struct cc2420_local *lp) ++{ ++ int ret; ++ u16 state; ++ u8 status = 0xff; ++ int timeout = 500; /* 500us delay */ ++ ret = cc2420_read_16_bit_reg(lp, CC2420_FSMSTATE, &state); ++ if (ret) ++ goto error_ret; ++ /* reset has occured prior to this, so there should be no other option */ ++ if (state != STATE_PDOWN) { ++ ret = -EINVAL; ++ goto error_ret; ++ } ++ ret = cc2420_cmd_strobe(lp, CC2420_SXOSCON); ++ if (ret) ++ goto error_ret; ++ ++ do { ++ ret = cc2420_get_status(lp, &status); ++ if (ret) ++ goto error_ret; ++ if (timeout-- <= 0) { ++ dev_err(&lp->spi->dev, "oscillator start failed!\n"); ++ return ret; ++ } ++ udelay(1); ++ } while (!(status & CC2420_STATUS_XOSC16M_STABLE)); ++ ++ dev_info(&lp->spi->dev, "oscillator succesfully brought up\n"); ++ ++ return 0; ++error_ret: ++ return ret; ++} ++ ++static int __devinit cc2420_probe(struct spi_device *spi) ++{ ++ int ret; ++ u16 manidl, manidh; ++ struct cc2420_local *lp = kzalloc(sizeof *lp, GFP_KERNEL); ++ if (!lp) { ++ ret = -ENOMEM; ++ goto error_ret; ++ } ++ ++ lp->pdata = spi->dev.platform_data; ++ if (!lp->pdata) { ++ dev_err(&spi->dev, "no platform data\n"); ++ ret = -EINVAL; ++ goto err_free_local; ++ } ++ spi_set_drvdata(spi, lp); ++ mutex_init(&lp->bmux); ++ INIT_WORK(&lp->fifop_irqwork, cc2420_fifop_irqwork); ++ INIT_WORK(&lp->sfd_irqwork, cc2420_sfd_irqwork); ++ spin_lock_init(&lp->lock); ++ init_completion(&lp->tx_complete); ++ ++ lp->spi = spi; ++ lp->buf = kmalloc(3*sizeof *lp->buf, GFP_KERNEL); ++ if (!lp->buf) { ++ ret = -ENOMEM; ++ goto err_free_local; ++ } ++ ++ /* Request all the gpio's */ ++ ret = gpio_request(lp->pdata->fifo, "fifo"); ++ if (ret) ++ goto err_free_buf; ++ ret = gpio_request(lp->pdata->cca, "cca"); ++ if (ret) ++ goto err_free_gpio_fifo; ++#if 0 ++ /* This is causing problems as fifop is gpio 0 ? */ ++ ret = gpio_request(lp->pdata->fifop, "fifop"); ++ if (ret) ++ goto err_free_gpio_cca; ++#endif ++ ret = gpio_request(lp->pdata->sfd, "sfd"); ++ if (ret) ++ goto err_free_gpio_fifop; ++ ret = gpio_request(lp->pdata->reset, "reset"); ++ if (ret) ++ goto err_free_gpio_sfd; ++ ret = gpio_request(lp->pdata->vreg, "vreg"); ++ if (ret) ++ goto err_free_gpio_reset; ++ /* Configure the gpios appropriately */ ++ ++ /* Enable the voltage regulator */ ++ ret = gpio_direction_output(lp->pdata->vreg, 1); ++ if (ret) ++ goto err_free_gpio_reset; ++ udelay(600); /* Time for regulator to power up */ ++ /* Toggle the reset */ ++ ret = gpio_direction_output(lp->pdata->reset, 0); ++ if (ret) ++ goto err_disable_vreg; ++ udelay(10); /* no idea how long this should be? */ ++ ret = gpio_direction_output(lp->pdata->reset, 1); ++ if (ret) ++ goto err_disable_vreg; ++ udelay(10); ++ ++ ret = gpio_direction_input(lp->pdata->cca); ++ if (ret) ++ goto err_disable_vreg; ++ ret = gpio_direction_input(lp->pdata->fifo); ++ if (ret) ++ goto err_disable_vreg; ++ ret = gpio_direction_input(lp->pdata->fifop); ++ if (ret) ++ goto err_disable_vreg; ++ ret = gpio_direction_input(lp->pdata->sfd); ++ if (ret) ++ goto err_disable_vreg; ++ ++ ++ /* Check this is actually a cc2420 */ ++ ret = cc2420_read_16_bit_reg(lp, CC2420_MANFIDL, &manidl); ++ if (ret) ++ goto err_free_gpio_vreg; ++ ret = cc2420_read_16_bit_reg(lp, CC2420_MANFIDH, &manidh); ++ if (ret) ++ goto err_free_gpio_vreg; ++ if (manidh != CC2420_MANFIDHIGH || manidl != CC2420_MANFIDLOW) { ++ dev_err(&spi->dev, "Incorrect manufacturer id %x%x\n", manidh, manidl); ++ ret = -ENODEV; ++ goto err_free_gpio_vreg; ++ } ++ /* TODO: make it more readable */ ++ dev_info(&lp->spi->dev, "Found Chipcon CC2420\n"); ++ dev_info(&lp->spi->dev, "Manufacturer ID:%x Version:%x Partnum:%x\n", ++ manidl & 0x0FFF, manidh >> 12, manidl >> 12); ++ ++ ret = cc2420_hw_init(lp); ++ if (ret) ++ goto err_disable_vreg; ++ ++ lp->fifop_irq = gpio_to_irq(lp->pdata->fifop); ++ lp->sfd_irq = gpio_to_irq(lp->pdata->sfd); ++ ++ ret = request_irq(lp->fifop_irq, ++ cc2420_isr, ++ IRQF_TRIGGER_RISING | IRQF_SHARED, ++ dev_name(&spi->dev), ++ lp); ++ if (ret) { ++ dev_err(&spi->dev, "could not get fifop irq?\n"); ++ goto err_free_fifop_irq; ++ } ++ ++ ret = request_irq(lp->sfd_irq, ++ cc2420_isr, ++ IRQF_TRIGGER_FALLING, ++ dev_name(&spi->dev), ++ lp); ++ if (ret) { ++ dev_err(&spi->dev, "could not get sfd irq?\n"); ++ goto err_free_sfd_irq; ++ } ++ ++ dev_info(&lp->spi->dev, "Set fifo threshold to 127\n"); ++ cc2420_write_16_bit_reg_partial(lp, CC2420_IOCFG0, 127, CC2420_FIFOP_THR_MASK); ++ ret = cc2420_register(lp); ++ if (ret) ++ goto err_free_sfd_irq; ++ ++ return 0; ++err_free_sfd_irq: ++ free_irq(lp->sfd_irq, lp); ++err_free_fifop_irq: ++ free_irq(lp->fifop_irq, lp); ++err_disable_vreg: ++ gpio_set_value(lp->pdata->vreg, 0); ++err_free_gpio_vreg: ++ gpio_free(lp->pdata->vreg); ++err_free_gpio_reset: ++ gpio_free(lp->pdata->reset); ++err_free_gpio_sfd: ++ gpio_free(lp->pdata->sfd); ++err_free_gpio_fifop: ++ gpio_free(lp->pdata->fifop); ++//err_free_gpio_cca: ++// gpio_free(lp->pdata->cca); ++err_free_gpio_fifo: ++ gpio_free(lp->pdata->fifo); ++err_free_buf: ++ kfree(lp->buf); ++err_free_local: ++ kfree(lp); ++error_ret: ++ return ret; ++} ++ ++static int __devexit cc2420_remove(struct spi_device *spi) ++{ ++ struct cc2420_local *lp = spi_get_drvdata(spi); ++ ++ cc2420_unregister(lp); ++ free_irq(lp->fifop_irq, lp); ++ free_irq(lp->sfd_irq, lp); ++ flush_work(&lp->fifop_irqwork); ++ flush_work(&lp->sfd_irqwork); ++ gpio_free(lp->pdata->vreg); ++ gpio_free(lp->pdata->reset); ++ gpio_free(lp->pdata->sfd); ++ gpio_free(lp->pdata->fifop); ++ gpio_free(lp->pdata->cca); ++ gpio_free(lp->pdata->fifo); ++ kfree(lp->buf); ++ kfree(lp); ++ ++ return 0; ++} ++ ++static struct spi_driver cc2420_driver = { ++ .driver = { ++ .name = "cc2420", ++ .owner = THIS_MODULE, ++ }, ++ .probe = cc2420_probe, ++ .remove = __devexit_p(cc2420_remove), ++}; ++ ++static int __init cc2420_init(void) ++{ ++ return spi_register_driver(&cc2420_driver); ++} ++module_init(cc2420_init); ++ ++static void __exit cc2420_exit(void) ++{ ++ spi_unregister_driver(&cc2420_driver); ++} ++module_exit(cc2420_exit); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/ieee802154/fakelb.c b/drivers/ieee802154/fakelb.c +new file mode 100644 +index 0000000..ae6e53f +--- /dev/null ++++ b/drivers/ieee802154/fakelb.c +@@ -0,0 +1,311 @@ ++/* ++ * Loopback IEEE 802.15.4 interface ++ * ++ * Copyright 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct fake_dev_priv { ++ struct ieee802154_dev *dev; ++ ++ struct list_head list; ++ struct fake_priv *fake; ++ ++ unsigned int working:1; ++}; ++ ++struct fake_priv { ++ struct list_head list; ++ rwlock_t lock; ++}; ++ ++static int ++hw_ed(struct ieee802154_dev *dev, u8 *level) ++{ ++ pr_debug("%s\n", __func__); ++ might_sleep(); ++ BUG_ON(!level); ++ *level = 0xbe; ++ return 0; ++} ++ ++static int ++hw_channel(struct ieee802154_dev *dev, int page, int channel) ++{ ++ pr_debug("%s %d\n", __func__, channel); ++ might_sleep(); ++ dev->phy->current_page = page; ++ dev->phy->current_channel = channel; ++ return 0; ++} ++ ++static void ++hw_deliver(struct fake_dev_priv *priv, struct sk_buff *skb) ++{ ++ struct sk_buff *newskb; ++ ++ if (!priv->working) ++ return; ++ ++ newskb = pskb_copy(skb, GFP_ATOMIC); ++ ++ ieee802154_rx_irqsafe(priv->dev, newskb, 0xcc); ++} ++ ++static int ++hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ++{ ++ struct fake_dev_priv *priv = dev->priv; ++ struct fake_priv *fake = priv->fake; ++ ++ might_sleep(); ++ ++ read_lock_bh(&fake->lock); ++ if (priv->list.next == priv->list.prev) { ++ /* we are the only one device */ ++ hw_deliver(priv, skb); ++ } else { ++ struct fake_dev_priv *dp; ++ list_for_each_entry(dp, &priv->fake->list, list) ++ if (dp != priv && ++ dp->dev->phy->current_channel == ++ priv->dev->phy->current_channel) ++ hw_deliver(dp, skb); ++ } ++ read_unlock_bh(&fake->lock); ++ ++ return 0; ++} ++ ++static int ++hw_start(struct ieee802154_dev *dev) { ++ struct fake_dev_priv *priv = dev->priv; ++ ++ if (priv->working) ++ return -EBUSY; ++ ++ priv->working = 1; ++ ++ return 0; ++} ++ ++static void ++hw_stop(struct ieee802154_dev *dev) { ++ struct fake_dev_priv *priv = dev->priv; ++ ++ priv->working = 0; ++} ++ ++static struct ieee802154_ops fake_ops = { ++ .owner = THIS_MODULE, ++ .xmit = hw_xmit, ++ .ed = hw_ed, ++ .set_channel = hw_channel, ++ .start = hw_start, ++ .stop = hw_stop, ++}; ++ ++static int ieee802154fake_add_priv(struct device *dev, struct fake_priv *fake) ++{ ++ struct fake_dev_priv *priv; ++ int err = -ENOMEM; ++ struct ieee802154_dev *ieee; ++ ++ ieee = ieee802154_alloc_device(sizeof(*priv), &fake_ops); ++ if (!dev) ++ goto err_alloc_dev; ++ ++ priv = ieee->priv; ++ priv->dev = ieee; ++ ++ /* 868 MHz BPSK 802.15.4-2003 */ ++ ieee->phy->channels_supported[0] |= 1; ++ /* 915 MHz BPSK 802.15.4-2003 */ ++ ieee->phy->channels_supported[0] |= 0x7fe; ++ /* 2.4 GHz O-QPSK 802.15.4-2003 */ ++ ieee->phy->channels_supported[0] |= 0x7FFF800; ++ /* 868 MHz ASK 802.15.4-2006 */ ++ ieee->phy->channels_supported[1] |= 1; ++ /* 915 MHz ASK 802.15.4-2006 */ ++ ieee->phy->channels_supported[1] |= 0x7fe; ++ /* 868 MHz O-QPSK 802.15.4-2006 */ ++ ieee->phy->channels_supported[2] |= 1; ++ /* 915 MHz O-QPSK 802.15.4-2006 */ ++ ieee->phy->channels_supported[2] |= 0x7fe; ++ /* 2.4 GHz CSS 802.15.4a-2007 */ ++ ieee->phy->channels_supported[3] |= 0x3fff; ++ /* UWB Sub-gigahertz 802.15.4a-2007 */ ++ ieee->phy->channels_supported[4] |= 1; ++ /* UWB Low band 802.15.4a-2007 */ ++ ieee->phy->channels_supported[4] |= 0x1e; ++ /* UWB High band 802.15.4a-2007 */ ++ ieee->phy->channels_supported[4] |= 0xffe0; ++ /* 750 MHz O-QPSK 802.15.4c-2009 */ ++ ieee->phy->channels_supported[5] |= 0xf; ++ /* 750 MHz MPSK 802.15.4c-2009 */ ++ ieee->phy->channels_supported[5] |= 0xf0; ++ /* 950 MHz BPSK 802.15.4d-2009 */ ++ ieee->phy->channels_supported[6] |= 0x3ff; ++ /* 950 MHz GFSK 802.15.4d-2009 */ ++ ieee->phy->channels_supported[6] |= 0x3ffc00; ++ ++ ++ INIT_LIST_HEAD(&priv->list); ++ priv->fake = fake; ++ ++ ieee->parent = dev; ++ ++ err = ieee802154_register_device(ieee); ++ if (err) ++ goto err_reg; ++ ++ write_lock_bh(&fake->lock); ++ list_add_tail(&priv->list, &fake->list); ++ write_unlock_bh(&fake->lock); ++ ++ return 0; ++ ++err_reg: ++ ieee802154_free_device(priv->dev); ++err_alloc_dev: ++ return err; ++} ++ ++static void ieee802154fake_del_priv(struct fake_dev_priv *priv) ++{ ++ write_lock_bh(&priv->fake->lock); ++ list_del(&priv->list); ++ write_unlock_bh(&priv->fake->lock); ++ ++ ieee802154_unregister_device(priv->dev); ++ ieee802154_free_device(priv->dev); ++} ++ ++static ssize_t ++adddev_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t n) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct fake_priv *priv = platform_get_drvdata(pdev); ++ int err; ++ ++ err = ieee802154fake_add_priv(dev, priv); ++ if (err) ++ return err; ++ return n; ++} ++ ++static DEVICE_ATTR(adddev, 0200, NULL, adddev_store); ++ ++static struct attribute *fake_attrs[] = { ++ &dev_attr_adddev.attr, ++ NULL, ++}; ++ ++static struct attribute_group fake_group = { ++ .name = NULL /* fake */, ++ .attrs = fake_attrs, ++}; ++ ++ ++static int __devinit ieee802154fake_probe(struct platform_device *pdev) ++{ ++ struct fake_priv *priv; ++ struct fake_dev_priv *dp; ++ ++ int err = -ENOMEM; ++ priv = kzalloc(sizeof(struct fake_priv), GFP_KERNEL); ++ if (!priv) ++ goto err_alloc; ++ ++ INIT_LIST_HEAD(&priv->list); ++ rwlock_init(&priv->lock); ++ ++ err = sysfs_create_group(&pdev->dev.kobj, &fake_group); ++ if (err) ++ goto err_grp; ++ ++ err = ieee802154fake_add_priv(&pdev->dev, priv); ++ if (err < 0) ++ goto err_slave; ++ ++ platform_set_drvdata(pdev, priv); ++ dev_info(&pdev->dev, "Added ieee802154 hardware\n"); ++ return 0; ++ ++err_slave: ++ list_for_each_entry(dp, &priv->list, list) ++ ieee802154fake_del_priv(dp); ++ sysfs_remove_group(&pdev->dev.kobj, &fake_group); ++err_grp: ++ kfree(priv); ++err_alloc: ++ return err; ++} ++ ++static int __devexit ieee802154fake_remove(struct platform_device *pdev) ++{ ++ struct fake_priv *priv = platform_get_drvdata(pdev); ++ struct fake_dev_priv *dp, *temp; ++ ++ list_for_each_entry_safe(dp, temp, &priv->list, list) ++ ieee802154fake_del_priv(dp); ++ sysfs_remove_group(&pdev->dev.kobj, &fake_group); ++ kfree(priv); ++ return 0; ++} ++ ++static struct platform_device *ieee802154fake_dev; ++ ++static struct platform_driver ieee802154fake_driver = { ++ .probe = ieee802154fake_probe, ++ .remove = __devexit_p(ieee802154fake_remove), ++ .driver = { ++ .name = "ieee802154fakelb", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static __init int fake_init(void) ++{ ++ ieee802154fake_dev = platform_device_register_simple( ++ "ieee802154fakelb", -1, NULL, 0); ++ return platform_driver_register(&ieee802154fake_driver); ++} ++ ++static __exit void fake_exit(void) ++{ ++ platform_driver_unregister(&ieee802154fake_driver); ++ platform_device_unregister(ieee802154fake_dev); ++} ++ ++module_init(fake_init); ++module_exit(fake_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dmitry Eremin-Solenikov, Sergey Lapin"); ++ ++ +diff --git a/drivers/ieee802154/serial.c b/drivers/ieee802154/serial.c +new file mode 100644 +index 0000000..6981d0e +--- /dev/null ++++ b/drivers/ieee802154/serial.c +@@ -0,0 +1,1047 @@ ++/* ++ * ZigBee TTY line discipline. ++ * ++ * Provides interface between ZigBee stack and IEEE 802.15.4 compatible ++ * firmware over serial line. Communication protocol is described below. ++ * ++ * Copyright (C) 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Maxim Gorbachyov ++ * Maxim Osipov ++ * Sergey Lapin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* NOTE: be sure to use here the same values as in the firmware */ ++#define START_BYTE1 'z' ++#define START_BYTE2 'b' ++#define MAX_DATA_SIZE 127 ++ ++#define IDLE_MODE 0x00 ++#define RX_MODE 0x02 ++#define TX_MODE 0x03 ++#define FORCE_TRX_OFF 0xF0 ++ ++#define STATUS_SUCCESS 0 ++#define STATUS_RX_ON 1 ++#define STATUS_TX_ON 2 ++#define STATUS_TRX_OFF 3 ++#define STATUS_IDLE 4 ++#define STATUS_BUSY 5 ++#define STATUS_BUSY_RX 6 ++#define STATUS_BUSY_TX 7 ++#define STATUS_ERR 8 ++ ++#define STATUS_WAIT ((u8) -1) /* waiting for the answer */ ++ ++/* We re-use PPP ioctl for our purposes */ ++#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */ ++ ++/* ++ * The following messages are used to control ZigBee firmware. ++ * All communication has request/response format, ++ * except of asynchronous incoming data stream (DATA_RECV_* messages). ++ */ ++enum { ++ NO_ID = 0, /* means no pending id */ ++ ++ /* Driver to Firmware */ ++ CMD_OPEN = 0x01, /* u8 id */ ++ CMD_CLOSE = 0x02, /* u8 id */ ++ CMD_SET_CHANNEL = 0x04, /* u8 id, u8 channel */ ++ CMD_ED = 0x05, /* u8 id */ ++ CMD_CCA = 0x06, /* u8 id */ ++ CMD_SET_STATE = 0x07, /* u8 id, u8 flag */ ++ DATA_XMIT_BLOCK = 0x09, /* u8 id, u8 len, u8 data[len] */ ++ RESP_RECV_BLOCK = 0x0b, /* u8 id, u8 status */ ++ CMD_ADDRESS = 0x0d, /* u8 id */ ++ ++ /* Firmware to Driver */ ++ RESP_OPEN = 0x81, /* u8 id, u8 status */ ++ RESP_CLOSE = 0x82, /* u8 id, u8 status */ ++ RESP_SET_CHANNEL = 0x84, /* u8 id, u8 status */ ++ RESP_ED = 0x85, /* u8 id, u8 status, u8 level */ ++ RESP_CCA = 0x86, /* u8 id, u8 status */ ++ RESP_SET_STATE = 0x87, /* u8 id, u8 status */ ++ RESP_XMIT_BLOCK = 0x89, /* u8 id, u8 status */ ++ DATA_RECV_BLOCK = 0x8b, /* u8 id, u8 lq, u8 len, u8 data[len] */ ++ RESP_ADDRESS = 0x8d, /* u8 id, u8 status, u8 u8 u8 u8 u8 u8 u8 u8 address */ ++}; ++ ++enum { ++ STATE_WAIT_START1, ++ STATE_WAIT_START2, ++ STATE_WAIT_COMMAND, ++ STATE_WAIT_PARAM1, ++ STATE_WAIT_PARAM2, ++ STATE_WAIT_DATA ++}; ++ ++struct zb_device { ++ /* Relative devices */ ++ struct tty_struct *tty; ++ struct ieee802154_dev *dev; ++ ++ /* locks the ldisc for the command */ ++ struct mutex mutex; ++ ++ /* command completition */ ++ wait_queue_head_t wq; ++ u8 status; ++ u8 ed; ++ ++ /* Internal state */ ++ struct completion open_done; ++ unsigned char opened; ++ u8 pending_id; ++ unsigned int pending_size; ++ u8 *pending_data; ++ /* FIXME: WE NEED LOCKING!!! */ ++ ++ /* Command (rx) processing */ ++ int state; ++ unsigned char id; ++ unsigned char param1; ++ unsigned char param2; ++ unsigned char index; ++ unsigned char data[MAX_DATA_SIZE]; ++}; ++ ++/***************************************************************************** ++ * ZigBee serial device protocol handling ++ *****************************************************************************/ ++static int _open_dev(struct zb_device *zbdev); ++ ++static int ++_send_pending_data(struct zb_device *zbdev) ++{ ++ struct tty_struct *tty; ++ ++ BUG_ON(!zbdev); ++ tty = zbdev->tty; ++ if (!tty) ++ return -ENODEV; ++ ++ zbdev->status = STATUS_WAIT; ++ ++ /* Debug info */ ++ printk(KERN_INFO "%s, %d bytes\n", __func__, ++ zbdev->pending_size); ++#ifdef DEBUG ++ print_hex_dump_bytes("send_pending_data ", DUMP_PREFIX_NONE, ++ zbdev->pending_data, zbdev->pending_size); ++#endif ++ ++ if (tty->driver->ops->write(tty, zbdev->pending_data, ++ zbdev->pending_size) != zbdev->pending_size) { ++ printk(KERN_ERR "%s: device write failed\n", __func__); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++send_cmd(struct zb_device *zbdev, u8 id) ++{ ++ u8 len = 0; ++ /* 4 because of 2 start bytes, id and optional extra */ ++ u8 buf[4]; ++ ++ /* Check arguments */ ++ BUG_ON(!zbdev); ++ ++ if (!zbdev->opened) { ++ if (!_open_dev(zbdev)) ++ return -EAGAIN; ++ } ++ ++ pr_debug("%s(): id = %u\n", __func__, id); ++ if (zbdev->pending_size) { ++ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n", ++ __func__, zbdev->pending_id); ++ BUG(); ++ } ++ ++ /* Prepare a message */ ++ buf[len++] = START_BYTE1; ++ buf[len++] = START_BYTE2; ++ buf[len++] = id; ++ ++ zbdev->pending_id = id; ++ zbdev->pending_size = len; ++ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL); ++ if (!zbdev->pending_data) { ++ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__); ++ zbdev->pending_id = 0; ++ zbdev->pending_size = 0; ++ return -ENOMEM; ++ } ++ memcpy(zbdev->pending_data, buf, len); ++ ++ return _send_pending_data(zbdev); ++} ++ ++static int ++send_cmd2(struct zb_device *zbdev, u8 id, u8 extra) ++{ ++ u8 len = 0; ++ /* 4 because of 2 start bytes, id and optional extra */ ++ u8 buf[4]; ++ ++ /* Check arguments */ ++ BUG_ON(!zbdev); ++ ++ if (!zbdev->opened) { ++ if (!_open_dev(zbdev)) ++ return -EAGAIN; ++ } ++ ++ pr_debug("%s(): id = %u\n", __func__, id); ++ if (zbdev->pending_size) { ++ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n", ++ __func__, zbdev->pending_id); ++ BUG(); ++ } ++ ++ /* Prepare a message */ ++ buf[len++] = START_BYTE1; ++ buf[len++] = START_BYTE2; ++ buf[len++] = id; ++ buf[len++] = extra; ++ ++ zbdev->pending_id = id; ++ zbdev->pending_size = len; ++ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL); ++ if (!zbdev->pending_data) { ++ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__); ++ zbdev->pending_id = 0; ++ zbdev->pending_size = 0; ++ return -ENOMEM; ++ } ++ memcpy(zbdev->pending_data, buf, len); ++ ++ return _send_pending_data(zbdev); ++} ++ ++static int ++send_block(struct zb_device *zbdev, u8 len, u8 *data) ++{ ++ u8 i = 0, buf[4]; /* 4 because of 2 start bytes, id and len */ ++ ++ /* Check arguments */ ++ BUG_ON(!zbdev); ++ ++ if (!zbdev->opened) { ++ if (!_open_dev(zbdev)) ++ return -EAGAIN; ++ } ++ ++ pr_debug("%s(): id = %u\n", __func__, DATA_XMIT_BLOCK); ++ if (zbdev->pending_size) { ++ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n", ++ __func__, zbdev->pending_id); ++ BUG(); ++ } ++ ++ /* Prepare a message */ ++ buf[i++] = START_BYTE1; ++ buf[i++] = START_BYTE2; ++ buf[i++] = DATA_XMIT_BLOCK; ++ buf[i++] = len; ++ ++ zbdev->pending_id = DATA_XMIT_BLOCK; ++ zbdev->pending_size = i + len; ++ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL); ++ if (!zbdev->pending_data) { ++ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__); ++ zbdev->pending_id = 0; ++ zbdev->pending_size = 0; ++ return -ENOMEM; ++ } ++ memcpy(zbdev->pending_data, buf, i); ++ memcpy(zbdev->pending_data + i, data, len); ++ ++ return _send_pending_data(zbdev); ++} ++ ++static void ++cleanup(struct zb_device *zbdev) ++{ ++ zbdev->state = STATE_WAIT_START1; ++ zbdev->id = 0; ++ zbdev->param1 = 0; ++ zbdev->param2 = 0; ++ zbdev->index = 0; ++} ++ ++static int ++is_command(unsigned char c) ++{ ++ switch (c) { ++ /* ids we can get here: */ ++ case RESP_OPEN: ++ case RESP_CLOSE: ++ case RESP_SET_CHANNEL: ++ case RESP_ED: ++ case RESP_CCA: ++ case RESP_SET_STATE: ++ case RESP_XMIT_BLOCK: ++ case DATA_RECV_BLOCK: ++ case RESP_ADDRESS: ++ return 1; ++ } ++ return 0; ++} ++ ++static int ++_match_pending_id(struct zb_device *zbdev) ++{ ++ return ((CMD_OPEN == zbdev->pending_id && ++ RESP_OPEN == zbdev->id) || ++ (CMD_CLOSE == zbdev->pending_id && ++ RESP_CLOSE == zbdev->id) || ++ (CMD_SET_CHANNEL == zbdev->pending_id && ++ RESP_SET_CHANNEL == zbdev->id) || ++ (CMD_ED == zbdev->pending_id && ++ RESP_ED == zbdev->id) || ++ (CMD_CCA == zbdev->pending_id && ++ RESP_CCA == zbdev->id) || ++ (CMD_SET_STATE == zbdev->pending_id && ++ RESP_SET_STATE == zbdev->id) || ++ (DATA_XMIT_BLOCK == zbdev->pending_id && ++ RESP_XMIT_BLOCK == zbdev->id) || ++ (DATA_RECV_BLOCK == zbdev->id) || ++ (CMD_ADDRESS == zbdev->pending_id && ++ RESP_ADDRESS == zbdev->id)); ++} ++ ++static void serial_net_rx(struct zb_device *zbdev) ++{ ++ /* zbdev->param1 is LQI ++ * zbdev->param2 is length of data ++ * zbdev->data is data itself ++ */ ++ struct sk_buff *skb; ++ skb = alloc_skb(zbdev->param2, GFP_ATOMIC); ++ skb_put(skb, zbdev->param2); ++ skb_copy_to_linear_data(skb, zbdev->data, zbdev->param2); ++ ieee802154_rx_irqsafe(zbdev->dev, skb, zbdev->param1); ++} ++ ++static void ++process_command(struct zb_device *zbdev) ++{ ++ /* Command processing */ ++ if (!_match_pending_id(zbdev)) ++ return; ++ ++ if (RESP_OPEN == zbdev->id && STATUS_SUCCESS == zbdev->param1) { ++ zbdev->opened = 1; ++ pr_debug("Opened device\n"); ++ complete(&zbdev->open_done); ++ /* Input is not processed during output, so ++ * using completion is not possible during output. ++ * so we need to handle open as any other command ++ * and hope for best ++ */ ++ return; ++ } ++ ++ if (!zbdev->opened) ++ return; ++ ++ zbdev->pending_id = 0; ++ kfree(zbdev->pending_data); ++ zbdev->pending_data = NULL; ++ zbdev->pending_size = 0; ++ if (zbdev->id != DATA_RECV_BLOCK) { ++ /* XXX: w/around for old FW, REMOVE */ ++ if (zbdev->param1 == STATUS_IDLE) ++ zbdev->status = STATUS_SUCCESS; ++ else ++ zbdev->status = zbdev->param1; ++ } ++ ++ switch (zbdev->id) { ++ case RESP_ED: ++ zbdev->ed = zbdev->param2; ++ break; ++ case DATA_RECV_BLOCK: ++ pr_debug("Received block, lqi %02x, len %02x\n", ++ zbdev->param1, zbdev->param2); ++ /* zbdev->param1 is LQ, zbdev->param2 is length */ ++ serial_net_rx(zbdev); ++ break; ++ case RESP_ADDRESS: ++ pr_debug("Received address, %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", ++ zbdev->data[0], zbdev->data[1], zbdev->data[2], zbdev->data[3], ++ zbdev->data[4], zbdev->data[5], zbdev->data[6], zbdev->data[7]); ++ break; ++ } ++ ++ wake_up(&zbdev->wq); ++} ++ ++static void ++process_char(struct zb_device *zbdev, unsigned char c) ++{ ++ /* Data processing */ ++ switch (zbdev->state) { ++ case STATE_WAIT_START1: ++ if (START_BYTE1 == c) ++ zbdev->state = STATE_WAIT_START2; ++ break; ++ ++ case STATE_WAIT_START2: ++ if (START_BYTE2 == c) ++ zbdev->state = STATE_WAIT_COMMAND; ++ else ++ cleanup(zbdev); ++ break; ++ ++ case STATE_WAIT_COMMAND: ++ if (is_command(c)) { ++ zbdev->id = c; ++ zbdev->state = STATE_WAIT_PARAM1; ++ } else { ++ cleanup(zbdev); ++ printk(KERN_ERR "%s, unexpected command id: %x\n", ++ __func__, c); ++ } ++ break; ++ ++ case STATE_WAIT_PARAM1: ++ zbdev->param1 = c; ++ if ((RESP_ED == zbdev->id) || (DATA_RECV_BLOCK == zbdev->id)) ++ zbdev->state = STATE_WAIT_PARAM2; ++ else if (RESP_ADDRESS == zbdev->id) { ++ zbdev->param2 = 8; ++ zbdev->state = STATE_WAIT_DATA; ++ } else { ++ process_command(zbdev); ++ cleanup(zbdev); ++ } ++ break; ++ ++ case STATE_WAIT_PARAM2: ++ zbdev->param2 = c; ++ if (RESP_ED == zbdev->id) { ++ process_command(zbdev); ++ cleanup(zbdev); ++ } else if (DATA_RECV_BLOCK == zbdev->id) ++ zbdev->state = STATE_WAIT_DATA; ++ else ++ cleanup(zbdev); ++ break; ++ ++ case STATE_WAIT_DATA: ++ if (zbdev->index < sizeof(zbdev->data)) { ++ zbdev->data[zbdev->index] = c; ++ zbdev->index++; ++ /* ++ * Pending data is received, ++ * param2 is length for DATA_RECV_BLOCK ++ */ ++ if (zbdev->index == zbdev->param2) { ++ process_command(zbdev); ++ cleanup(zbdev); ++ } ++ } else { ++ printk(KERN_ERR "%s(): data size is greater " ++ "than buffer available\n", __func__); ++ cleanup(zbdev); ++ } ++ break; ++ ++ default: ++ cleanup(zbdev); ++ } ++} ++ ++/***************************************************************************** ++ * Device operations for IEEE 802.15.4 PHY side interface ZigBee stack ++ *****************************************************************************/ ++ ++static int _open_dev(struct zb_device *zbdev) ++{ ++ int retries; ++ u8 len = 0; ++ /* 4 because of 2 start bytes, id and optional extra */ ++ u8 buf[4]; ++ ++ /* Check arguments */ ++ BUG_ON(!zbdev); ++ if (zbdev->opened) ++ return 1; ++ ++ pr_debug("%s()\n", __func__); ++ if (zbdev->pending_size) { ++ printk(KERN_ERR "%s(): cmd is already pending, id = %u\n", ++ __func__, zbdev->pending_id); ++ BUG(); ++ } ++ ++ /* Prepare a message */ ++ buf[len++] = START_BYTE1; ++ buf[len++] = START_BYTE2; ++ buf[len++] = CMD_OPEN; ++ ++ zbdev->pending_id = CMD_OPEN; ++ zbdev->pending_size = len; ++ zbdev->pending_data = kzalloc(zbdev->pending_size, GFP_KERNEL); ++ if (!zbdev->pending_data) { ++ printk(KERN_ERR "%s(): unable to allocate memory\n", __func__); ++ zbdev->pending_id = 0; ++ zbdev->pending_size = 0; ++ return -ENOMEM; ++ } ++ memcpy(zbdev->pending_data, buf, len); ++ ++ retries = 5; ++ while (!zbdev->opened && retries) { ++ if (_send_pending_data(zbdev) != 0) ++ return 0; ++ ++ /* 3 second before retransmission */ ++ wait_for_completion_interruptible_timeout( ++ &zbdev->open_done, msecs_to_jiffies(1000)); ++ --retries; ++ } ++ ++ zbdev->pending_id = 0; ++ kfree(zbdev->pending_data); ++ zbdev->pending_data = NULL; ++ zbdev->pending_size = 0; ++ ++ if (zbdev->opened) { ++ printk(KERN_INFO "Opened connection to device\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* Valid channels: 1-16 */ ++static int ++ieee802154_serial_set_channel(struct ieee802154_dev *dev, int page, int channel) ++{ ++ struct zb_device *zbdev; ++ int ret = 0; ++ ++ pr_debug("%s %d\n", __func__, channel); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return -EINVAL; ++ } ++ ++ BUG_ON(page != 0); ++ /* Our channels are actually from 11 to 26 ++ * We have IEEE802.15.4 channel no from 0 to 26. ++ * channels 0-10 are not valid for us */ ++ BUG_ON(channel < 11 || channel > 26); ++ /* ... but our crappy firmware numbers channels from 1 to 16 ++ * which is a mystery. We suould enforce that using PIB API ++ * but additional checking here won't kill, and gcc will ++ * optimize this stuff anyway. */ ++ BUG_ON((channel - 10) < 1 && (channel - 10) > 16); ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return -EINTR; ++ ret = send_cmd2(zbdev, CMD_SET_CHANNEL, channel - 10); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) ++ ret = -EBUSY; ++ } else ++ ret = -EINTR; ++ ++ if (!ret) ++ zbdev->dev->phy->current_channel = channel; ++out: ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++ return ret; ++} ++ ++static int ++ieee802154_serial_ed(struct ieee802154_dev *dev, u8 *level) ++{ ++ struct zb_device *zbdev; ++ int ret = 0; ++ ++ pr_debug("%s\n", __func__); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return -EINTR; ++ ++ ret = send_cmd(zbdev, CMD_ED); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ *level = zbdev->ed; ++ if (zbdev->status != STATUS_SUCCESS) ++ ret = -EBUSY; ++ } else ++ ret = -ETIMEDOUT; ++out: ++ ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++ return ret; ++} ++ ++static int ++ieee802154_serial_address(struct ieee802154_dev *dev, u8 addr[IEEE802154_ALEN]) ++{ ++ struct zb_device *zbdev; ++ int ret = 0; ++ ++ pr_debug("%s\n", __func__); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return -EINTR; ++ ++ ret = send_cmd(zbdev, CMD_ADDRESS); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ memcpy(addr, zbdev->data, sizeof addr); ++ if (zbdev->status != STATUS_SUCCESS) ++ ret = -EBUSY; ++ } else ++ ret = -ETIMEDOUT; ++out: ++ ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++ return ret; ++} ++ ++static int ++ieee802154_serial_start(struct ieee802154_dev *dev) ++{ ++ struct zb_device *zbdev; ++ int ret = 0; ++ ++ pr_debug("%s\n", __func__); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return -EINTR; ++ ++ ret = send_cmd2(zbdev, CMD_SET_STATE, RX_MODE); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) ++ ret = -EBUSY; ++ } else ++ ret = -ETIMEDOUT; ++out: ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++ return ret; ++} ++ ++static void ++ieee802154_serial_stop(struct ieee802154_dev *dev) ++{ ++ struct zb_device *zbdev; ++ pr_debug("%s\n", __func__); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return; ++ } ++ ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return; ++ ++ ++ if (send_cmd2(zbdev, CMD_SET_STATE, FORCE_TRX_OFF) != 0) ++ goto out; ++ ++ wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)); ++out: ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++} ++ ++static int ++ieee802154_serial_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) ++{ ++ struct zb_device *zbdev; ++ int ret; ++ ++ pr_debug("%s\n", __func__); ++ ++ zbdev = dev->priv; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s: wrong phy\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (mutex_lock_interruptible(&zbdev->mutex)) ++ return -EINTR; ++ ++ ret = send_cmd(zbdev, CMD_CCA); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } else { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ ret = send_cmd2(zbdev, CMD_SET_STATE, TX_MODE); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } else { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ ret = send_block(zbdev, skb->len, skb->data); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } else { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ ret = send_cmd2(zbdev, CMD_SET_STATE, RX_MODE); ++ if (ret) ++ goto out; ++ ++ if (wait_event_interruptible_timeout(zbdev->wq, ++ zbdev->status != STATUS_WAIT, ++ msecs_to_jiffies(1000)) > 0) { ++ if (zbdev->status != STATUS_SUCCESS) { ++ ret = -EBUSY; ++ goto out; ++ } ++ } else { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++out: ++ ++ mutex_unlock(&zbdev->mutex); ++ pr_debug("%s end\n", __func__); ++ return ret; ++} ++ ++/***************************************************************************** ++ * Line discipline interface for IEEE 802.15.4 serial device ++ *****************************************************************************/ ++ ++static struct ieee802154_ops serial_ops = { ++ .owner = THIS_MODULE, ++ .xmit = ieee802154_serial_xmit, ++ .ed = ieee802154_serial_ed, ++ .set_channel = ieee802154_serial_set_channel, ++ .start = ieee802154_serial_start, ++ .stop = ieee802154_serial_stop, ++ .ieee_addr = ieee802154_serial_address, ++}; ++ ++/* ++ * Called when a tty is put into ZB line discipline. Called in process context. ++ * Returns 0 on success. ++ */ ++static int ++ieee802154_tty_open(struct tty_struct *tty) ++{ ++ struct zb_device *zbdev = tty->disc_data; ++ struct ieee802154_dev *dev; ++ int err; ++ ++ pr_debug("Openning ldisc\n"); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (tty->disc_data) ++ return -EBUSY; ++ ++ dev = ieee802154_alloc_device(sizeof(*zbdev), &serial_ops); ++ if (!dev) ++ return -ENOMEM; ++ ++ zbdev = dev->priv; ++ zbdev->dev = dev; ++ ++ mutex_init(&zbdev->mutex); ++ init_completion(&zbdev->open_done); ++ init_waitqueue_head(&zbdev->wq); ++ ++ dev->extra_tx_headroom = 0; ++ /* only 2.4 GHz band */ ++ dev->phy->channels_supported[0] = 0x7fff800; ++ ++ dev->flags = IEEE802154_HW_OMIT_CKSUM; ++ ++ dev->parent = tty->dev; ++ ++ zbdev->tty = tty_kref_get(tty); ++ ++ cleanup(zbdev); ++ ++ tty->disc_data = zbdev; ++ tty->receive_room = MAX_DATA_SIZE; ++ ++ /* FIXME: why is this needed. Note don't use ldisc_ref here as the ++ open path is before the ldisc is referencable */ ++ ++ if (tty->ldisc->ops->flush_buffer) ++ tty->ldisc->ops->flush_buffer(tty); ++ tty_driver_flush_buffer(tty); ++ ++ err = ieee802154_register_device(dev); ++ if (err) { ++ printk(KERN_ERR "%s: device register failed\n", __func__); ++ goto out_free; ++ } ++ ++ return 0; ++ ++ ieee802154_unregister_device(zbdev->dev); ++ ++out_free: ++ tty->disc_data = NULL; ++ tty_kref_put(tty); ++ zbdev->tty = NULL; ++ ++ ieee802154_free_device(zbdev->dev); ++ ++ return err; ++} ++ ++/* ++ * Called when the tty is put into another line discipline or it hangs up. We ++ * have to wait for any cpu currently executing in any of the other zb_tty_* ++ * routines to finish before we can call zb_tty_close and free the ++ * zb_serial_dev struct. This routine must be called from process context, not ++ * interrupt or softirq context. ++ */ ++static void ++ieee802154_tty_close(struct tty_struct *tty) ++{ ++ struct zb_device *zbdev; ++ ++ zbdev = tty->disc_data; ++ if (NULL == zbdev) { ++ printk(KERN_WARNING "%s: match is not found\n", __func__); ++ return; ++ } ++ ++ tty->disc_data = NULL; ++ tty_kref_put(tty); ++ zbdev->tty = NULL; ++ ++ ieee802154_unregister_device(zbdev->dev); ++ ++ tty_ldisc_flush(tty); ++ tty_driver_flush_buffer(tty); ++ ++ ieee802154_free_device(zbdev->dev); ++} ++ ++/* ++ * Called on tty hangup in process context. ++ */ ++static int ++ieee802154_tty_hangup(struct tty_struct *tty) ++{ ++ ieee802154_tty_close(tty); ++ return 0; ++} ++ ++/* ++ * Called in process context only. May be re-entered ++ * by multiple ioctl calling threads. ++ */ ++static int ++ieee802154_tty_ioctl(struct tty_struct *tty, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct zb_device *zbdev; ++ ++ pr_debug("cmd = 0x%x\n", cmd); ++ ++ zbdev = tty->disc_data; ++ if (NULL == zbdev) { ++ pr_debug("match is not found\n"); ++ return -EINVAL; ++ } ++ ++ switch (cmd) { ++ case TCFLSH: ++ return tty_perform_flush(tty, arg); ++ default: ++ /* Try the mode commands */ ++ return tty_mode_ioctl(tty, file, cmd, arg); ++ } ++} ++ ++ ++/* ++ * This can now be called from hard interrupt level as well ++ * as soft interrupt level or mainline. ++ */ ++static void ++ieee802154_tty_receive(struct tty_struct *tty, const unsigned char *buf, ++ char *cflags, int count) ++{ ++ struct zb_device *zbdev; ++ int i; ++ ++ /* Debug info */ ++ printk(KERN_INFO "%s, received %d bytes\n", __func__, ++ count); ++#ifdef DEBUG ++ print_hex_dump_bytes("ieee802154_tty_receive ", DUMP_PREFIX_NONE, ++ buf, count); ++#endif ++ ++ /* Actual processing */ ++ zbdev = tty->disc_data; ++ if (NULL == zbdev) { ++ printk(KERN_ERR "%s(): record for tty is not found\n", ++ __func__); ++ return; ++ } ++ for (i = 0; i < count; ++i) ++ process_char(zbdev, buf[i]); ++#if 0 ++ if (tty->driver->flush_chars) ++ tty->driver->flush_chars(tty); ++#endif ++ tty_unthrottle(tty); ++} ++ ++/* ++ * Line discipline device structure ++ */ ++static struct tty_ldisc_ops ieee802154_ldisc = { ++ .owner = THIS_MODULE, ++ .magic = TTY_LDISC_MAGIC, ++ .name = "ieee802154-ldisc", ++ .open = ieee802154_tty_open, ++ .close = ieee802154_tty_close, ++ .hangup = ieee802154_tty_hangup, ++ .receive_buf = ieee802154_tty_receive, ++ .ioctl = ieee802154_tty_ioctl, ++}; ++ ++/***************************************************************************** ++ * Module service routinues ++ *****************************************************************************/ ++ ++static int __init ieee802154_serial_init(void) ++{ ++ printk(KERN_INFO "Initializing ZigBee TTY interface\n"); ++ ++ if (tty_register_ldisc(N_IEEE802154, &ieee802154_ldisc) != 0) { ++ printk(KERN_ERR "%s: line discipline register failed\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void __exit ieee802154_serial_cleanup(void) ++{ ++ if (tty_unregister_ldisc(N_IEEE802154) != 0) ++ printk(KERN_CRIT ++ "failed to unregister ZigBee line discipline.\n"); ++} ++ ++module_init(ieee802154_serial_init); ++module_exit(ieee802154_serial_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_LDISC(N_IEEE802154); ++ +diff --git a/drivers/ieee802154/spi_atben.c b/drivers/ieee802154/spi_atben.c +new file mode 100644 +index 0000000..431bfe0 +--- /dev/null ++++ b/drivers/ieee802154/spi_atben.c +@@ -0,0 +1,421 @@ ++/* ++ * spi_atben.c - SPI host look-alike for ATBEN ++ * ++ * Written 2011 by Werner Almesberger ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "at86rf230.h" ++ ++ ++enum { ++ VDD_OFF = 1 << 2, /* VDD disable, PD02 */ ++ MOSI = 1 << 8, /* CMD, PD08 */ ++ SLP_TR = 1 << 9, /* CLK, PD09 */ ++ MISO = 1 << 10, /* DAT0, PD10 */ ++ SCLK = 1 << 11, /* DAT1, PD11 */ ++ IRQ = 1 << 12, /* DAT2, PD12 */ ++ nSEL = 1 << 13, /* DAT3/CD, PD13 */ ++}; ++ ++#define PDPIN (prv->regs) ++#define PDDATS (prv->regs+0x14) ++#define PDDATC (prv->regs+0x18) ++ ++ ++struct atben_prv { ++ struct device *dev; ++ void __iomem *regs; ++ struct resource *ioarea; ++ struct at86rf230_platform_data ++ platform_data; ++ /* copy platform_data so that we can adapt .reset_data */ ++}; ++ ++ ++/* ----- ATBEN reset ------------------------------------------------------- */ ++ ++ ++static void atben_reset(void *reset_data) ++{ ++ struct atben_prv *prv = reset_data; ++ const int charge = nSEL | MOSI | SLP_TR | SCLK; ++ const int discharge = charge | IRQ | MISO; ++ ++ dev_info(prv->dev, "atben_reset\n"); ++ jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 1 << 2, 1 << 2); ++ jz_gpio_port_direction_output(JZ_GPIO_PORTD(0), discharge); ++ jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, discharge); ++ msleep(100); /* let power drop */ ++ ++ /* ++ * Hack: PD12/DAT2/IRQ is an active-high interrupt input, which is ++ * indicated by setting its direction bit to 1. We thus must not ++ * configure it as an "input". ++ */ ++ jz_gpio_port_direction_input(JZ_GPIO_PORTD(0), MISO); ++ jz_gpio_port_set_value(JZ_GPIO_PORTD(0), charge, charge); ++ msleep(10); /* precharge caps */ ++ ++ jz_gpio_port_set_value(JZ_GPIO_PORTD(0), 0, VDD_OFF | SLP_TR | SCLK); ++ msleep(10); ++} ++ ++ ++/* ----- SPI transfers ----------------------------------------------------- */ ++ ++ ++static void rx_only(const struct atben_prv *prv, uint8_t *buf, int len) ++{ ++ uint8_t v; ++ ++ while (len--) { ++ writel(SCLK, PDDATS); ++ v = readl(PDPIN) & MISO ? 0x80 : 0; ++ writel(SCLK, PDDATC); ++ ++ #define DO_BIT(m) \ ++ writel(SCLK, PDDATS); \ ++ if (readl(PDPIN) & MISO) \ ++ v |= (m); \ ++ writel(SCLK, PDDATC) ++ ++ DO_BIT(0x40); ++ DO_BIT(0x20); ++ DO_BIT(0x10); ++ DO_BIT(0x08); ++ DO_BIT(0x04); ++ DO_BIT(0x02); ++ DO_BIT(0x01); ++ ++ #undef DO_BIT ++ ++ *buf++ = v; ++ } ++} ++ ++ ++static void tx_only(const struct atben_prv *prv, const uint8_t *buf, int len) ++{ ++ uint8_t tv; ++ ++ while (len--) { ++ tv = *buf++; ++ ++ if (tv & 0x80) { ++ writel(MOSI, PDDATS); ++ goto b6_1; ++ } else { ++ writel(MOSI, PDDATC); ++ goto b6_0; ++ } ++ ++ #define DO_BIT(m, this, next) \ ++ this##_1: \ ++ writel(SCLK, PDDATS); \ ++ if (tv & (m)) { \ ++ writel(SCLK, PDDATC); \ ++ goto next##_1; \ ++ } else { \ ++ writel(MOSI | SCLK, PDDATC); \ ++ goto next##_0; \ ++ } \ ++ this##_0: \ ++ writel(SCLK, PDDATS); \ ++ writel(SCLK, PDDATC); \ ++ if (tv & (m)) { \ ++ writel(MOSI, PDDATS); \ ++ goto next##_1; \ ++ } else { \ ++ goto next##_0; \ ++ } ++ ++ DO_BIT(0x40, b6, b5); ++ DO_BIT(0x20, b5, b4); ++ DO_BIT(0x10, b4, b3); ++ DO_BIT(0x08, b3, b2); ++ DO_BIT(0x04, b2, b1); ++ DO_BIT(0x02, b1, b0); ++ DO_BIT(0x01, b0, done); ++ ++ #undef DO_BIT ++ ++done_1: ++done_0: ++ writel(SCLK, PDDATS); ++ writel(SCLK, PDDATC); ++ writel(SCLK, PDDATC); /* delay to meet t5 timing */ ++ } ++} ++ ++ ++static void bidir(const struct atben_prv *prv, const uint8_t *tx, uint8_t *rx, ++ int len) ++{ ++ uint8_t mask, tv, rv; ++ ++ while (len--) { ++ tv = *tx++; ++ for (mask = 0x80; mask; mask >>= 1) { ++ if (tv & mask) ++ writel(MOSI, PDDATS); ++ else ++ writel(MOSI, PDDATC); ++ writel(SCLK, PDDATS); ++ if (readl(PDPIN) & MISO) ++ rv |= mask; ++ writel(SCLK, PDDATC); ++ } ++ *rx++ = rv; ++ } ++} ++ ++ ++static int atben_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct atben_prv *prv = spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ const uint8_t *tx; ++ uint8_t *rx; ++ ++ if (unlikely(list_empty(&msg->transfers))) { ++ dev_err(prv->dev, "transfer is empty\n"); ++ return -EINVAL; ++ } ++ ++ msg->actual_length = 0; ++ ++ writel(nSEL, PDDATC); ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ tx = xfer->tx_buf; ++ rx = xfer->rx_buf; ++ msg->actual_length += xfer->len; ++ ++ if (!tx) ++ rx_only(prv, rx, xfer->len); ++ else if (!rx) ++ tx_only(prv, tx, xfer->len); ++ else ++ bidir(prv, tx, rx, xfer->len); ++ } ++ writel(nSEL, PDDATS); ++ ++ msg->status = 0; ++ msg->complete(msg->context); ++ ++ return 0; ++} ++ ++static int atben_setup(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++ ++/* ----- SPI master creation/removal --------------------------------------- */ ++ ++ ++const static struct at86rf230_platform_data at86rf230_platform_data = { ++ .rstn = -1, ++ .slp_tr = JZ_GPIO_PORTD(9), ++ .dig2 = -1, ++ .reset = atben_reset, ++ /* set .reset_data later */ ++}; ++ ++static int __devinit atben_probe(struct platform_device *pdev) ++{ ++ struct spi_board_info board_info = { ++ .modalias = "at86rf230", ++ /* set .irq later */ ++ .chip_select = 0, ++ .bus_num = -1, ++ .max_speed_hz = 8 * 1000 * 1000, ++ }; ++ ++ struct spi_master *master; ++ struct atben_prv *prv; ++ struct resource *regs; ++ struct spi_device *spi; ++ int err = -ENXIO; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*prv)); ++ if (!master) ++ return -ENOMEM; ++ ++ prv = spi_master_get_devdata(master); ++ prv->dev = &pdev->dev; ++ platform_set_drvdata(pdev, spi_master_get(master)); ++ ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; ++ master->bus_num = pdev->id; ++ master->num_chipselect = 1; ++ master->setup = atben_setup; ++ master->transfer = atben_transfer; ++ ++ dev_dbg(prv->dev, "Setting up ATBEN SPI\n"); ++ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(prv->dev, "no IORESOURCE_MEM\n"); ++ err = -ENOENT; ++ goto out_master; ++ } ++ prv->ioarea = request_mem_region(regs->start, resource_size(regs), ++ pdev->name); ++ if (!prv->ioarea) { ++ dev_err(prv->dev, "can't request ioarea\n"); ++ goto out_master; ++ } ++ ++ prv->regs = ioremap(regs->start, resource_size(regs)); ++ if (!prv->regs) { ++ dev_err(prv->dev, "can't ioremap\n"); ++ goto out_ioarea; ++ } ++ ++ board_info.irq = platform_get_irq(pdev, 0); ++ if (board_info.irq < 0) { ++ dev_err(prv->dev, "can't get GPIO irq\n"); ++ err = -ENOENT; ++ goto out_regs; ++ } ++ ++ err = spi_register_master(master); ++ if (err) { ++ dev_err(prv->dev, "can't register master\n"); ++ goto out_regs; ++ } ++ ++ prv->platform_data = at86rf230_platform_data; ++ prv->platform_data.reset_data = prv; ++ board_info.platform_data = &prv->platform_data; ++ ++ spi = spi_new_device(master, &board_info); ++ if (!spi) { ++ dev_err(&pdev->dev, "can't create new device for %s\n", ++ board_info.modalias); ++ err = -ENXIO; ++ goto out_registered; ++ } ++ ++ dev_info(&spi->dev, "ATBEN ready for mischief (IRQ %d)\n", ++ board_info.irq); ++ ++ return 0; ++ ++out_registered: ++ spi_unregister_master(master); ++ ++out_regs: ++ iounmap(prv->regs); ++ ++out_ioarea: ++ release_resource(prv->ioarea); ++ kfree(prv->ioarea); ++ ++out_master: ++ platform_set_drvdata(pdev, NULL); ++ spi_master_put(master); ++ ++ return err; ++} ++ ++static int __devexit atben_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct atben_prv *prv = spi_master_get_devdata(master); ++ ++// restore GPIOs ++ ++ spi_unregister_master(master); ++ ++ iounmap(prv->regs); ++ ++ release_resource(prv->ioarea); ++ kfree(prv->ioarea); ++ ++ platform_set_drvdata(pdev, NULL); ++ spi_master_put(master); ++ ++ return 0; ++} ++ ++static struct platform_driver atben_driver = { ++ .driver = { ++ .name = "spi_atben", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __devexit_p(atben_remove), ++}; ++ ++static struct resource atben_resources[] = { ++ { ++ .start = JZ4740_GPIO_BASE_ADDR+0x300, ++ .end = JZ4740_GPIO_BASE_ADDR+0x3ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ /* set start and end later */ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device atben_device = { ++ .name = "spi_atben", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(atben_resources), ++ .resource = atben_resources, ++}; ++ ++/* ++ * Registering the platform device just to probe it immediately afterwards ++ * seems a little circuitous. Need to see if there's a better way. ++ * ++ * What we actually should do is this: ++ * - in module init, register the device ++ * - maybe probe as well, but keep the device also if the probe fails ++ * (due to a conflicting driver already occupying the 8:10 slot) ++ * - have a means for user space to kick off driver probing, e.g., when ++ * anything about the 8:10 slot changes ++ */ ++ ++static int __init atben_init(void) ++{ ++ int err; ++ ++ err = platform_device_register(&atben_device); ++ if (err) ++ return err; ++ ++ atben_resources[1].start = atben_resources[1].end = ++ gpio_to_irq(JZ_GPIO_PORTD(12)); ++ ++ return platform_driver_probe(&atben_driver, atben_probe); ++} ++ ++static void __exit atben_exit(void) ++{ ++ platform_driver_unregister(&atben_driver); ++ platform_device_unregister(&atben_device); ++} ++ ++module_init(atben_init); ++module_exit(atben_exit); ++ ++ ++MODULE_DESCRIPTION("ATBEN SPI Controller Driver"); ++MODULE_AUTHOR("Werner Almesberger "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/ieee802154/spi_atusb.c b/drivers/ieee802154/spi_atusb.c +new file mode 100644 +index 0000000..44a9d03 +--- /dev/null ++++ b/drivers/ieee802154/spi_atusb.c +@@ -0,0 +1,750 @@ ++/* ++ * spi_atusb - SPI host look-alike for ATUSB ++ * ++ * Copyright (c) 2011 Richard Sharpe ++ * Copyright (c) 2011 Stefan Schmidt ++ * Copyright (c) 2011 Werner Almesberger ++ * ++ * 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, version 2 ++ * ++ */ ++ ++/* ++ * - implement more robust interrupt synchronization ++ * - check URB killing in atusb_disconnect for races ++ * - switch from bulk to interrupt endpoint ++ * - implement buffer read without extra copy ++ * - harmonize indentation style ++ * - mv atusb.c ../ieee802.15.4/spi_atusb.c, or maybe atrf_atusb.c or such ++ * - check module load/unload ++ * - review dev_* severity levels ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "at86rf230.h" ++ ++ ++#define SYNC_TIMEOUT_MS 50 /* assume interrupt has been synced after ++ waiting this long */ ++ ++#define VENDOR_ID 0x20b7 ++#define PRODUCT_ID 0x1540 ++ ++/* The devices we work with */ ++static const struct usb_device_id atusb_device_table[] = { ++ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(usb, atusb_device_table); ++ ++#define ATUSB_BUILD_SIZE 256 ++struct atusb_local { ++ struct usb_device * udev; ++ /* The interface to the RF part info, if applicable */ ++ uint8_t ep0_atusb_major; ++ uint8_t ep0_atusb_minor; ++ uint8_t atusb_hw_type; ++ struct spi_master *master; ++ int slave_irq; ++ struct urb *irq_urb; ++ uint8_t irq_buf; /* receive irq serial here*/ ++ uint8_t irq_seen; /* last irq serial from bulk */ ++ uint8_t irq_sync; /* last irq serial from WRITE2_SYNC */ ++ struct tasklet_struct task; /* interrupt delivery tasklet */ ++ struct timer_list timer; /* delay, for interrupt synch */ ++ struct at86rf230_platform_data platform_data; ++ /* copy platform_data so that we can adapt .reset_data */ ++ struct spi_device *spi; ++// unsigned char buffer[3]; ++ unsigned char buffer[260]; /* XXL, just in case */ ++ struct spi_message *msg; ++}; ++ ++/* Commands to our device. Make sure this is synced with the firmware */ ++enum atspi_requests { ++ ATUSB_ID = 0x00, /* system status/control grp */ ++ ATUSB_BUILD, ++ ATUSB_RESET, ++ ATUSB_RF_RESET = 0x10, /* debug/test group */ ++ ATUSB_POLL_INT, ++ ATUSB_TEST, /* atusb-sil only */ ++ ATUSB_TIMER, ++ ATUSB_GPIO, ++ ATUSB_SLP_TR, ++ ATUSB_GPIO_CLEANUP, ++ ATUSB_REG_WRITE = 0x20, /* transceiver group */ ++ ATUSB_REG_READ, ++ ATUSB_BUF_WRITE, ++ ATUSB_BUF_READ, ++ ATUSB_SRAM_WRITE, ++ ATUSB_SRAM_READ, ++ ATUSB_SPI_WRITE = 0x30, /* SPI group */ ++ ATUSB_SPI_READ1, ++ ATUSB_SPI_READ2, ++ ATUSB_SPI_WRITE2_SYNC, ++}; ++ ++/* ++ * Direction bRequest wValue wIndex wLength ++ * ++ * ->host ATUSB_ID - - 3 ++ * ->host ATUSB_BUILD - - #bytes ++ * host-> ATUSB_RESET - - 0 ++ * ++ * host-> ATUSB_RF_RESET - - 0 ++ * ->host ATUSB_POLL_INT - - 1 ++ * host-> ATUSB_TEST - - 0 ++ * ->host ATUSB_TIMER - - #bytes (6) ++ * ->host ATUSB_GPIO dir+data mask+p# 3 ++ * host-> ATUSB_SLP_TR - - 0 ++ * host-> ATUSB_GPIO_CLEANUP - - 0 ++ * ++ * host-> ATUSB_REG_WRITE value addr 0 ++ * ->host ATUSB_REG_READ - addr 1 ++ * host-> ATUSB_BUF_WRITE - - #bytes ++ * ->host ATUSB_BUF_READ - - #bytes ++ * host-> ATUSB_SRAM_WRITE - addr #bytes ++ * ->host ATUSB_SRAM_READ - addr #bytes ++ * ++ * host-> ATUSB_SPI_WRITE byte0 byte1 #bytes ++ * ->host ATUSB_SPI_READ1 byte0 - #bytes ++ * ->host ATUSB_SPI_READ2 byte0 byte1 #bytes ++ * ->host ATUSB_SPI_WRITE2_SYNC byte0 byte1 0/1 ++ */ ++ ++#define ATUSB_FROM_DEV (USB_TYPE_VENDOR | USB_DIR_IN) ++#define ATUSB_TO_DEV (USB_TYPE_VENDOR | USB_DIR_OUT) ++ ++ ++/* ----- Control transfers ------------------------------------------------- */ ++ ++ ++static int atusb_async_errchk(struct urb *urb) ++{ ++ struct atusb_local *atusb = urb->context; ++ struct spi_message *msg = atusb->msg; ++ struct usb_device *dev = atusb->udev; ++ ++ if (!urb->status) { ++ dev_dbg(&dev->dev, "atusb_async_errchk OK len %d\n", ++ urb->actual_length); ++ return 0; ++ } ++ ++ if (urb->status != -ENOENT && urb->status != -ECONNRESET && ++ urb->status != -ESHUTDOWN) ++ dev_info(&dev->dev, "atusb_async_errchk FAIL error %d\n", ++ urb->status); ++ ++ msg->actual_length = 0; ++ ++ return urb->status; ++} ++ ++static void atusb_async_finish(struct urb *urb) ++{ ++ struct atusb_local *atusb = urb->context; ++ struct spi_message *msg = atusb->msg; ++ ++ msg->status = urb->status; ++ msg->complete(msg->context); ++ ++ kfree(urb->setup_packet); ++ usb_free_urb(urb); ++} ++ ++static void atusb_ctrl_cb(struct urb *urb) ++{ ++ atusb_async_errchk(urb); ++ atusb_async_finish(urb); ++} ++ ++static void atusb_timer(unsigned long data) ++{ ++ struct urb *urb = (void *) data; ++ ++ dev_warn(&urb->dev->dev, "atusb_timer\n"); ++ atusb_async_finish(urb); ++} ++ ++static void atusb_ctrl_cb_sync(struct urb *urb) ++{ ++ struct atusb_local *atusb = urb->context; ++ ++ /* @@@ needs locking/atomic */ ++ if (atusb_async_errchk(urb) || atusb->irq_sync == atusb->irq_seen) { ++ atusb_async_finish(urb); ++ return; ++ } ++ ++ BUG_ON(timer_pending(&atusb->timer)); ++ atusb->timer.expires = jiffies+msecs_to_jiffies(SYNC_TIMEOUT_MS); ++ atusb->timer.data = (unsigned long) urb; ++ add_timer(&atusb->timer); ++} ++ ++static void atusb_read_fb_cb(struct urb *urb) ++{ ++ struct atusb_local *atusb = urb->context; ++ struct spi_message *msg = atusb->msg; ++ const struct spi_transfer *xfer; ++ uint8_t *rx; ++ ++ if (!atusb_async_errchk(urb)) { ++ BUG_ON(!urb->actual_length); ++ ++ xfer = list_first_entry(&msg->transfers, struct spi_transfer, ++ transfer_list); ++ rx = xfer->rx_buf; ++ rx[1] = atusb->buffer[0]; ++ ++ xfer = list_entry(xfer->transfer_list.next, ++ struct spi_transfer, transfer_list); ++ memcpy(xfer->rx_buf, atusb->buffer+1, urb->actual_length-1); ++ } ++ ++ atusb_async_finish(urb); ++} ++ ++static int submit_control_msg(struct atusb_local *atusb, ++ __u8 request, __u8 requesttype, __u16 value, __u16 index, ++ void *data, __u16 size, usb_complete_t complete_fn, void *context) ++{ ++ struct usb_device *dev = atusb->udev; ++ struct usb_ctrlrequest *req; ++ struct urb *urb; ++ int retval = -ENOMEM; ++ ++ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); ++ if (!req) ++ return -ENOMEM; ++ ++ req->bRequest = request; ++ req->bRequestType = requesttype; ++ req->wValue = cpu_to_le16(value); ++ req->wIndex = cpu_to_le16(index); ++ req->wLength = cpu_to_le16(size); ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) ++ goto out_nourb; ++ ++ usb_fill_control_urb(urb, dev, ++ requesttype == ATUSB_FROM_DEV ? ++ usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0), ++ (unsigned char *) req, data, size, complete_fn, context); ++ ++ retval = usb_submit_urb(urb, GFP_KERNEL); ++ if (!retval) ++ return 0; ++ dev_warn(&dev->dev, "failed submitting read urb, error %d", ++ retval); ++ retval = retval == -ENOMEM ? retval : -EIO; ++ ++ usb_free_urb(urb); ++out_nourb: ++ kfree(req); ++ ++ return retval; ++} ++ ++ ++/* ----- SPI transfers ----------------------------------------------------- */ ++ ++ ++static int atusb_read1(struct atusb_local *atusb, ++ uint8_t tx, uint8_t *rx, int len) ++{ ++ dev_dbg(&atusb->udev->dev, "atusb_read1: tx = 0x%x\n", tx); ++ return submit_control_msg(atusb, ++ ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, ++ rx, 1, atusb_ctrl_cb, atusb); ++} ++ ++static int atusb_read_fb(struct atusb_local *atusb, ++ uint8_t tx, uint8_t *rx0, uint8_t *rx, int len) ++{ ++ dev_dbg(&atusb->udev->dev, "atusb_read_fb: tx = 0x%x\n", tx); ++ return submit_control_msg(atusb, ++ ATUSB_SPI_READ1, ATUSB_FROM_DEV, tx, 0, ++ atusb->buffer, len+1, atusb_read_fb_cb, atusb); ++} ++ ++static int atusb_write(struct atusb_local *atusb, ++ uint8_t tx0, uint8_t tx1, const uint8_t *tx, int len) ++{ ++ dev_dbg(&atusb->udev->dev, ++ "atusb_write: tx0 = 0x%x tx1 = 0x%x\n", tx0, tx1); ++ ++ /* ++ * The AT86RF230 driver sometimes requires a transceiver state ++ * transition to be an interrupt barrier. This is the case after ++ * writing FORCE_TX_ON to the TRX_CMD field in the TRX_STATE register. ++ * ++ * Since there is no other means of notification, we just decode the ++ * transfer and do a bit of pattern matching. ++ */ ++ if (tx0 == (CMD_REG | CMD_WRITE | RG_TRX_STATE) && ++ (tx1 & 0x1f) == STATE_FORCE_TX_ON) ++ return submit_control_msg(atusb, ++ ATUSB_SPI_WRITE2_SYNC, ATUSB_FROM_DEV, tx0, tx1, ++ &atusb->irq_sync, 1, atusb_ctrl_cb_sync, atusb); ++ else ++ return submit_control_msg(atusb, ++ ATUSB_SPI_WRITE, ATUSB_TO_DEV, tx0, tx1, ++ (uint8_t *) tx, len, atusb_ctrl_cb, atusb); ++} ++ ++static int atusb_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct atusb_local *atusb = spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ struct spi_transfer *x[2]; ++ int n; ++ const uint8_t *tx; ++ uint8_t *rx; ++ int len; ++ int retval = 0; ++ ++ if (unlikely(list_empty(&msg->transfers))) { ++ dev_err(&atusb->udev->dev, "transfer is empty\n"); ++ return -EINVAL; ++ } ++ ++ atusb->msg = msg; ++ ++ /* Classify the request */ ++ n = 0; ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ if (n == ARRAY_SIZE(x)) { ++ dev_err(&atusb->udev->dev, "too many transfers\n"); ++ return -EINVAL; ++ } ++ x[n] = xfer; ++ n++; ++ } ++ ++ tx = x[0]->tx_buf; ++ rx = x[0]->rx_buf; ++ len = x[0]->len; ++ ++ msg->actual_length = len; ++ ++ if (!tx || len != 2) ++ goto bad_req; ++ if (n == 1) { ++ if (rx) { ++ dev_dbg(&atusb->udev->dev, "read 1\n"); ++ retval = atusb_read1(atusb, tx[0], rx+1, len-1); ++ } else { ++ dev_dbg(&atusb->udev->dev, "write 2\n"); ++ /* ++ * Don't take our clock away !! ;-) ++ */ ++ if (tx[0] == (CMD_REG | CMD_WRITE | RG_TRX_CTRL_0)) { ++ msg->status = 0; ++ msg->complete(msg->context); ++ } else { ++ retval = atusb_write(atusb, ++ tx[0], tx[1], NULL, 0); ++ } ++ } ++ } else { ++ if (x[0]->rx_buf) { ++ if (x[1]->tx_buf || !x[1]->rx_buf) ++ goto bad_req; ++ dev_dbg(&atusb->udev->dev, "read 1+\n"); ++ retval = atusb_read_fb(atusb, tx[0], rx+1, ++ x[1]->rx_buf, x[1]->len); ++ } else { ++ if (!x[1]->tx_buf ||x[1]->rx_buf) ++ goto bad_req; ++ dev_dbg(&atusb->udev->dev, "write 2+n\n"); ++ retval = atusb_write(atusb, tx[0], tx[1], ++ x[1]->tx_buf, x[1]->len); ++ } ++ } ++ return retval; ++ ++bad_req: ++ dev_err(&atusb->udev->dev, "unrecognized request:\n"); ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) ++ dev_err(&atusb->udev->dev, "%stx %srx len %u\n", ++ xfer->tx_buf ? "" : "!", xfer->rx_buf ? " " : "!", ++ xfer->len); ++ return -EINVAL; ++} ++ ++static int atusb_setup(struct spi_device *spi) ++{ ++ return 0; ++} ++ ++ ++/* ----- Interrupt handling ------------------------------------------------ */ ++ ++ ++static void atusb_tasklet(unsigned long data) ++{ ++ struct atusb_local *atusb = (void *) data; ++ ++ generic_handle_irq(atusb->slave_irq); ++} ++ ++static void atusb_irq(struct urb *urb) ++{ ++ struct atusb_local *atusb = urb->context; ++ ++ dev_dbg(&urb->dev->dev, "atusb_irq (%d), seen %d sync %d\n", ++ urb->status, atusb->irq_buf, atusb->irq_sync); ++ if (!urb->status) { ++ atusb->irq_seen = atusb->irq_buf; ++ if (atusb->irq_sync == atusb->irq_seen && ++ try_to_del_timer_sync(&atusb->timer) == 1) ++ atusb_async_finish((struct urb *) atusb->timer.data); ++ } ++ usb_free_urb(urb); ++ atusb->irq_urb = NULL; ++ tasklet_schedule(&atusb->task); ++} ++ ++static int atusb_arm_interrupt(struct atusb_local *atusb) ++{ ++ struct usb_device *dev = atusb->udev; ++ struct urb *urb; ++ int retval = -ENOMEM; ++ ++ BUG_ON(atusb->irq_urb); ++ ++ dev_vdbg(&dev->dev, "atusb_arm_interrupt\n"); ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ dev_err(&dev->dev, ++ "atusb_arm_interrupt: usb_alloc_urb failed\n"); ++ return -ENOMEM; ++ } ++ ++ usb_fill_bulk_urb(urb, dev, usb_rcvbulkpipe(dev, 1), ++ &atusb->irq_buf, 1, atusb_irq, atusb); ++ atusb->irq_urb = urb; ++ retval = usb_submit_urb(urb, GFP_KERNEL); ++ if (!retval) ++ return 0; ++ ++ dev_err(&dev->dev, "failed submitting bulk urb, error %d\n", retval); ++ retval = retval == -ENOMEM ? retval : -EIO; ++ ++ usb_free_urb(urb); ++ ++ return retval; ++} ++ ++static void atusb_irq_mask(struct irq_data *data) ++{ ++ struct atusb_local *atusb = irq_data_get_irq_chip_data(data); ++ ++ dev_vdbg(&atusb->udev->dev, "atusb_irq_mask\n"); ++ tasklet_disable_nosync(&atusb->task); ++} ++ ++static void atusb_irq_unmask(struct irq_data *data) ++{ ++ struct atusb_local *atusb = irq_data_get_irq_chip_data(data); ++ ++ dev_vdbg(&atusb->udev->dev, "atusb_irq_unmask\n"); ++ tasklet_enable(&atusb->task); ++} ++ ++static void atusb_irq_ack(struct irq_data *data) ++{ ++ struct atusb_local *atusb = irq_data_get_irq_chip_data(data); ++ ++ dev_vdbg(&atusb->udev->dev, "atusb_irq_ack\n"); ++ atusb_arm_interrupt(atusb); ++} ++ ++static struct irq_chip atusb_irq_chip = { ++ .name = "atusb-slave", ++ .irq_mask = atusb_irq_mask, ++ .irq_unmask = atusb_irq_unmask, ++ .irq_ack = atusb_irq_ack, ++}; ++ ++ ++/* ----- Transceiver reset ------------------------------------------------- */ ++ ++ ++static void atusb_reset(void *reset_data) ++{ ++ int retval; ++ struct atusb_local *atusb = reset_data; ++ ++ retval = usb_control_msg(atusb->udev, ++ usb_rcvctrlpipe(atusb->udev, 0), ++ ATUSB_RF_RESET, ATUSB_TO_DEV, 0, 0, ++ NULL, 0, 1000); ++ if (retval < 0) { ++ dev_err(&atusb->udev->dev, ++ "%s: error doing reset retval = %d\n", ++ __func__, retval); ++ } ++} ++ ++ ++/* ----- Firmware version information -------------------------------------- */ ++ ++ ++static int atusb_get_and_show_revision(struct atusb_local *atusb) ++{ ++ struct usb_device *dev = atusb->udev; ++ int retval; ++ ++ /* Get a couple of the ATMega Firmware values */ ++ retval = usb_control_msg(dev, ++ usb_rcvctrlpipe(dev, 0), ++ ATUSB_ID, ATUSB_FROM_DEV, 0, 0, ++ atusb->buffer, 3, 1000); ++ if (retval < 0) { ++ dev_info(&dev->dev, ++ "failed submitting urb for ATUSB_ID, error %d\n", retval); ++ return retval == -ENOMEM ? retval : -EIO; ++ } ++ ++ atusb->ep0_atusb_major = atusb->buffer[0]; ++ atusb->ep0_atusb_minor = atusb->buffer[1]; ++ atusb->atusb_hw_type = atusb->buffer[2]; ++ dev_info(&dev->dev, ++ "Firmware: major: %u, minor: %u, hardware type: %u\n", ++ atusb->ep0_atusb_major, atusb->ep0_atusb_minor, ++ atusb->atusb_hw_type); ++ ++ return 0; ++} ++ ++static int atusb_get_and_show_build(struct atusb_local *atusb) ++{ ++ struct usb_device *dev = atusb->udev; ++ char build[ATUSB_BUILD_SIZE+1]; ++ int retval; ++ ++ retval = usb_control_msg(dev, ++ usb_rcvctrlpipe(atusb->udev, 0), ++ ATUSB_BUILD, ATUSB_FROM_DEV, 0, 0, ++ build, ATUSB_BUILD_SIZE, 1000); ++ if (retval < 0) { ++ dev_err(&dev->dev, ++ "failed submitting urb for ATUSB_BUILD, error %d\n", ++ retval); ++ return retval == -ENOMEM ? retval : -EIO; ++ } ++ ++ build[retval] = 0; ++ dev_info(&dev->dev, "Firmware: build %s\n", build); ++ ++ return 0; ++} ++ ++ ++/* ----- Setup ------------------------------------------------------------- */ ++ ++ ++struct at86rf230_platform_data at86rf230_platform_data = { ++ .rstn = -1, ++ .slp_tr = -1, ++ .dig2 = -1, ++ .reset = atusb_reset, ++ /* set .reset_data later */ ++}; ++ ++static int atusb_probe(struct usb_interface *interface, ++ const struct usb_device_id *id) ++{ ++ struct spi_board_info board_info = { ++ .modalias = "at86rf230", ++ /* set .irq later */ ++ .chip_select = 0, ++ .bus_num = -1, ++ .max_speed_hz = 8 * 1000 * 1000, ++ }; ++ ++ struct usb_device *udev = interface_to_usbdev(interface); ++ struct atusb_local *atusb = NULL; ++ struct spi_master *master; ++ int retval; ++ ++ /* ++ * Ignore all interfaces used for DFU, i.e., everything while in the ++ * boot loader, and interface #1 when in the application. ++ */ ++ if (interface->cur_altsetting->desc.bInterfaceClass != ++ USB_CLASS_VENDOR_SPEC) { ++ dev_dbg(&udev->dev, ++ "Ignoring interface with class 0x%02x\n", ++ interface->cur_altsetting->desc.bInterfaceClass); ++ return -ENODEV; ++ } ++ ++ master = spi_alloc_master(&udev->dev, sizeof(*atusb)); ++ if (!master) ++ return -ENOMEM; ++ ++ atusb = spi_master_get_devdata(master); ++ ++ atusb->udev = usb_get_dev(udev); ++ usb_set_intfdata(interface, atusb); ++ ++ atusb->master = spi_master_get(master); ++ ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; ++ master->bus_num = -1; ++ master->num_chipselect = 1; ++ master->setup = atusb_setup; ++ master->transfer = atusb_transfer; ++ ++ atusb->slave_irq = irq_alloc_desc(numa_node_id()); ++ if (atusb->slave_irq < 0) { ++ dev_err(&udev->dev, "can't allocate slave irq\n"); ++ retval = -ENXIO; ++ goto err_free; ++ } ++ ++ irq_set_chip_data(atusb->slave_irq, atusb); ++ irq_set_chip_and_handler(atusb->slave_irq, &atusb_irq_chip, ++ handle_level_irq); ++ ++ /* FIXME prepare USB IRQ */ ++ ++ retval = spi_register_master(master); ++ if (retval < 0) { ++ dev_err(&udev->dev, "can't register spi master\n"); ++ goto err_slave_irq; ++ } ++ ++ atusb->platform_data = at86rf230_platform_data; ++ atusb->platform_data.reset_data = atusb; ++ board_info.platform_data = &atusb->platform_data; ++ board_info.irq = atusb->slave_irq; ++ ++ init_timer(&atusb->timer); ++ atusb->timer.function = atusb_timer; ++ ++ tasklet_init(&atusb->task, atusb_tasklet, (unsigned long) atusb); ++ tasklet_disable(&atusb->task); ++ atusb_arm_interrupt(atusb); ++ ++ if (atusb_get_and_show_revision(atusb) < 0) ++ goto err_master; ++ if (atusb_get_and_show_build(atusb) < 0) ++ goto err_master; ++ ++ atusb->spi = spi_new_device(master, &board_info); ++ if (!atusb->spi) { ++ dev_err(&udev->dev, "can't create new device for %s\n", ++ board_info.modalias); ++ goto err_master; ++ } ++ ++ dev_info(&atusb->spi->dev, ++ "ATUSB ready for mischief (IRQ %d)\n", board_info.irq); ++ ++ return 0; ++ ++err_master: ++ /* ++ * If we come here from a partially successful driver initialization, ++ * we don't really know how much it has done. In particular, it may ++ * have triggered an interrupt and thus removed the interrupt URB and ++ * maybe scheduled the tasklet. ++ */ ++ tasklet_disable(&atusb->task); ++ if (atusb->irq_urb) ++ usb_kill_urb(atusb->irq_urb); ++ spi_master_put(atusb->master); ++err_slave_irq: ++ irq_set_chained_handler(atusb->slave_irq, NULL); ++ irq_set_chip_data(atusb->slave_irq, NULL); ++ irq_free_desc(atusb->slave_irq); ++err_free: ++ return retval; ++} ++ ++static void atusb_disconnect(struct usb_interface *interface) ++{ ++ struct atusb_local *atusb = usb_get_intfdata(interface); ++ struct spi_master *master = atusb->master; ++ ++ tasklet_disable(&atusb->task); ++ /* @@@ this needs some extra protecion - wa */ ++ if (atusb->irq_urb) ++ usb_kill_urb(atusb->irq_urb); ++ ++ BUG_ON(timer_pending(&atusb->timer)); ++ ++ usb_set_intfdata(interface, NULL); ++ usb_put_dev(atusb->udev); ++ ++ spi_dev_put(atusb->spi); ++ ++ spi_unregister_master(master); ++ ++ irq_set_chained_handler(atusb->slave_irq, NULL); ++ irq_set_chip_data(atusb->slave_irq, NULL); ++ irq_free_desc(atusb->slave_irq); ++ ++ spi_master_put(master); ++} ++ ++void atusb_release(struct device *dev) ++{ ++ return; ++} ++ ++static struct usb_driver atusb_driver = { ++ .name = "atusb_ben-wpan", ++ .probe = atusb_probe, ++ .disconnect = atusb_disconnect, ++ .id_table = atusb_device_table, ++}; ++ ++static struct platform_device atusb_device = { ++ .name = "spi_atusb", ++ .id = -1, ++ .dev.release = atusb_release, ++}; ++ ++static int __init atusb_init(void) ++{ ++ int retval; ++ ++ retval = platform_device_register(&atusb_device); ++ if (retval) ++ return retval; ++ ++ return usb_register(&atusb_driver); ++} ++ ++static void __exit atusb_exit(void) ++{ ++ usb_deregister(&atusb_driver); ++ platform_device_unregister(&atusb_device); ++} ++ ++module_init (atusb_init); ++module_exit (atusb_exit); ++ ++MODULE_AUTHOR("Richard Sharpe "); ++MODULE_AUTHOR("Stefan Schmidt "); ++MODULE_AUTHOR("Werner Almesberger "); ++MODULE_DESCRIPTION("ATUSB ben-wpan Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/if_ieee802154.h b/include/linux/if_ieee802154.h +new file mode 100644 +index 0000000..cce32bb +--- /dev/null ++++ b/include/linux/if_ieee802154.h +@@ -0,0 +1,6 @@ ++#ifndef __LINUX_IF_IEEE802154_H ++#define __LINUX_IF_IEEE802154_H ++ ++#define IEEE802154_ALEN 8 /* size of 64-bit hardware address */ ++ ++#endif +diff --git a/include/linux/spi/at86rf230.h b/include/linux/spi/at86rf230.h +new file mode 100644 +index 0000000..dff0225 +--- /dev/null ++++ b/include/linux/spi/at86rf230.h +@@ -0,0 +1,34 @@ ++/* ++ * AT86RF230/RF231 driver ++ * ++ * Copyright (C) 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ */ ++#ifndef LINUX_SPI_AT86RF230_H ++#define LINUX_SPI_AT86RF230_H ++ ++struct at86rf230_platform_data { ++ int rstn; ++ int slp_tr; ++ int dig2; ++ void (*reset)(void *reset_data); ++ void *reset_data; ++}; ++ ++#endif ++ +diff --git a/include/net/mac802154.h b/include/net/mac802154.h +new file mode 100644 +index 0000000..df46f6a +--- /dev/null ++++ b/include/net/mac802154.h +@@ -0,0 +1,156 @@ ++/* ++ * IEEE802.15.4-2003 specification ++ * ++ * Copyright (C) 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ */ ++#ifndef NET_MAC802154_H ++#define NET_MAC802154_H ++ ++#include ++ ++/** ++ * enum ieee802154_hw_addr_filt_flags - hardware flags ++ * ++ * These flags are used to indicate changed address settings from ++ * the stack to the hardware. ++ * ++ * @IEEE802515_SADDR_CHANGED: ++ * Indicates that the Short Address changed ++ * ++ * @IEEE802515_IEEEADDR_CHANGED: ++ * Indicates that the IEEE Address changed ++ * ++ * @IEEE802515_PANID_CHANGED: ++ * Indicates that the PAN ID changed ++ * ++ * @IEEE802515_PANC_CHANGED: ++ * Indicates that PAN Coordinator status changed ++ */ ++enum ieee802154_hw_addr_filt_flags { ++ IEEE802515_SADDR_CHANGED = 1 << 0, ++ IEEE802515_IEEEADDR_CHANGED = 1 << 1, ++ IEEE802515_PANID_CHANGED = 1 << 2, ++ IEEE802515_PANC_CHANGED = 1 << 3, ++}; ++ ++struct ieee802154_hw_addr_filt { ++ u16 pan_id; ++ u16 short_addr; ++ u8 ieee_addr[IEEE802154_ALEN]; ++ u8 pan_coord; ++}; ++ ++struct ieee802154_dev { ++ /* filled by the driver */ ++ int extra_tx_headroom; /* headroom to reserve for tx skb */ ++ u32 flags; /* Flags for device to set */ ++ struct device *parent; ++ ++ /* filled by mac802154 core */ ++ struct ieee802154_hw_addr_filt hw_filt; ++ void *priv; /* driver-specific data */ ++ struct wpan_phy *phy; ++}; ++ ++/* Checksum is in hardware and is omitted from packet */ ++/** ++ * enum ieee802154_hw_flags - hardware flags ++ * ++ * These flags are used to indicate hardware capabilities to ++ * the stack. Generally, flags here should have their meaning ++ * done in a way that the simplest hardware doesn't need setting ++ * any particular flags. There are some exceptions to this rule, ++ * however, so you are advised to review these flags carefully. ++ * ++ * @IEEE802154_HW_OMIT_CKSUM: ++ * Indicates that receiver omits FCS and transmitter will add ++ * FCS on it's own. ++ * ++ * @IEEE802154_HW_AACK: ++ * Indicates that receiver will autorespond with ACK frames. ++ */ ++enum ieee802154_hw_flags { ++ IEEE802154_HW_OMIT_CKSUM = 1 << 0, ++ IEEE802154_HW_AACK = 1 << 1, ++}; ++ ++struct sk_buff; ++ ++/** ++ * struct ieee802154_ops - callbacks from mac802154 to the driver ++ * ++ * This structure contains various callbacks that the driver may ++ * handle or, in some cases, must handle, for example to transmit ++ * a frame. ++ * ++ * @start: Handler that 802.15.4 module calls for device initialisation. ++ * This function is called before the first interface is attached. ++ * ++ * @stop: Handler that 802.15.4 module calls for device cleanup ++ * This function is called after the last interface is removed. ++ * ++ * @xmit: Handler that 802.15.4 module calls for each transmitted frame. ++ * skb cntains the buffer starting from the IEEE 802.15.4 header. ++ * The low-level driver should send the frame based on available ++ * configuration. ++ * This function should return zero or negative errno. ++ * Called with pib_lock held. ++ * ++ * @ed: Handler that 802.15.4 module calls for Energy Detection. ++ * This function should place the value for detected energy ++ * (usually device-dependant) in the level pointer and return ++ * either zero or negative errno. ++ * Called with pib_lock held. ++ * ++ * @set_channel: Set radio for listening on specific channel. ++ * Set the device for listening on specified channel. ++ * Returns either zero, or negative errno. ++ * Called with pib_lock held. ++ * ++ * @set_hw_addr_filt: Set radio for listening on specific address. ++ * Set the device for listening on specified address. ++ * Returns either zero, or negative errno. ++ */ ++struct ieee802154_ops { ++ struct module *owner; ++ int (*start)(struct ieee802154_dev *dev); ++ void (*stop)(struct ieee802154_dev *dev); ++ int (*xmit)(struct ieee802154_dev *dev, ++ struct sk_buff *skb); ++ int (*ed)(struct ieee802154_dev *dev, u8 *level); ++ int (*set_channel)(struct ieee802154_dev *dev, ++ int page, ++ int channel); ++ int (*set_hw_addr_filt)(struct ieee802154_dev *dev, ++ struct ieee802154_hw_addr_filt *filt, ++ unsigned long changed); ++ int (*ieee_addr)(struct ieee802154_dev *dev, ++ u8 addr[IEEE802154_ALEN]); ++}; ++ ++struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size, ++ struct ieee802154_ops *ops); ++int ieee802154_register_device(struct ieee802154_dev *dev); ++void ieee802154_unregister_device(struct ieee802154_dev *dev); ++void ieee802154_free_device(struct ieee802154_dev *dev); ++ ++void ieee802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi); ++void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, ++ u8 lqi); ++#endif ++ +diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig +new file mode 100644 +index 0000000..32e63bc +--- /dev/null ++++ b/net/mac802154/Kconfig +@@ -0,0 +1,24 @@ ++config MAC802154 ++ tristate "Generic IEEE 802.15.4 Soft Networking Stack (mac802154)" ++ depends on IEEE802154 && EXPERIMENTAL ++ select CRC_CCITT ++ ---help--- ++ This option enables the hardware independent IEEE 802.15.4 ++ networking stack for SoftMAC devices (the ones implementing ++ only PHY level of IEEE 802.15.4 standard). ++ ++ Note: this implementation is neither certified, nor feature ++ complete! We do not guarantee that it is compatible w/ other ++ implementations, etc. ++ ++ If you plan to use HardMAC IEEE 802.15.4 devices, you can ++ say N here. Alternatievly you can say M to compile it as ++ module. ++ ++config MAC802154_DEBUG ++ bool "IEEE 802.15.4 SoftMAC debugging messages" ++ depends on MAC802154 ++ default y ++ help ++ Say Y here to make the IEEE 802.15.4 SoftMAC generate extensive ++ debugging messages. +diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile +new file mode 100644 +index 0000000..d76fabb +--- /dev/null ++++ b/net/mac802154/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_MAC802154) += mac802154.o ++mac802154-objs := rx.o tx.o main.o monitor.o wpan.o mac_cmd.o scan.o mib.o \ ++ beacon.o beacon_hash.o smac.o ++ ++ccflags-$(CONFIG_MAC802154_DEBUG) += -DDEBUG ++ccflags-y += -Wall +diff --git a/net/mac802154/beacon.c b/net/mac802154/beacon.c +new file mode 100644 +index 0000000..fbf67e9 +--- /dev/null ++++ b/net/mac802154/beacon.c +@@ -0,0 +1,285 @@ ++/* ++ * MAC beacon interface ++ * ++ * Copyright 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mac802154.h" ++#include "beacon_hash.h" ++ ++/* Beacon frame format per specification is the followinf: ++ * Standard MAC frame header: ++ * FC (2) SEQ (1) ++ * Addressing (4-20) ++ * Beacon fields: ++ * (2) ++ * (?) ++ * (?) ++ * (?) ++ * FCS (2) ++ * ++ * Superframe specification: ++ * bit Value ++ * 15 Association permit ++ * 14 PAN coordinator ++ * 13 Reserved ++ * 12 Battery life extension ++ * 8-11 Final CAP slot ++ * 4-7 Superframe order ++ * 0-3 Beacon order ++ * ++ * GTS: ++ * (1) ++ * (0-1) ++ * (?) ++ * ++ * Pending address: ++ * (1) ++ * type != ARPHRD_IEEE802154); ++ ++ hlen = LL_RESERVED_SPACE(dev); ++ tlen = dev->needed_tailroom; ++ skb = alloc_skb(len + hlen + tlen, GFP_ATOMIC); ++ if (!skb) ++ return -ENOMEM; ++ ++ skb_reserve(skb, LL_RESERVED_SPACE(dev)); ++ ++ skb_reset_network_header(skb); ++ ++ mac_cb(skb)->flags = IEEE802154_FC_TYPE_BEACON; ++ mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_bsn(dev); ++ ++ addr.addr_type = IEEE802154_ADDR_NONE; ++ err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &addr, saddr, len); ++ if (err < 0) { ++ kfree_skb(skb); ++ return err; ++ } ++ skb_reset_mac_header(skb); ++ ++ /* Superframe */ ++ sf = IEEE802154_BEACON_SF_BO_BEACONLESS; ++ sf |= IEEE802154_BEACON_SF_SO_INACTIVE; ++ if (flags & IEEE802154_BEACON_FLAG_PANCOORD) ++ sf |= IEEE802154_BEACON_SF_PANCOORD; ++ ++ if (flags & IEEE802154_BEACON_FLAG_CANASSOC) ++ sf |= IEEE802154_BEACON_SF_CANASSOC; ++ memcpy(skb_put(skb, sizeof(sf)), &sf, sizeof(sf)); ++ ++ /* TODO GTS */ ++ gts = 0; ++ ++ if (flags & IEEE802154_BEACON_FLAG_GTSPERMIT) ++ gts |= IEEE802154_BEACON_GTS_PERMIT; ++ memcpy(skb_put(skb, sizeof(gts)), >s, sizeof(gts)); ++ ++ /* FIXME pending address */ ++ addr16_cnt = 0; ++ addr64_cnt = 0; ++ ++ pa_spec = IEEE802154_BEACON_PA_LONG(addr64_cnt) | ++ IEEE802154_BEACON_PA_SHORT(addr16_cnt); ++ memcpy(skb_put(skb, sizeof(pa_spec)), &pa_spec, sizeof(pa_spec)); ++ ++ memcpy(skb_put(skb, len), buf, len); ++ ++ skb->dev = dev; ++ skb->protocol = htons(ETH_P_IEEE802154); ++ ++ return dev_queue_xmit(skb); ++} ++ ++/* at entry to this function we need skb->data to point to start ++ * of beacon field and MAC frame already parsed into MAC_CB */ ++ ++static int parse_beacon_frame(struct sk_buff *skb, u8 *buf, ++ int *flags, struct list_head *al) ++{ ++ int offt = 0; ++ u8 gts_spec; ++ u8 pa_spec; ++ struct mac802154_pandsc *pd; ++ u16 sf = skb->data[0] + (skb->data[1] << 8); ++ ++ pd = kzalloc(sizeof(struct mac802154_pandsc), GFP_KERNEL); ++ ++ /* Filling-up pre-parsed values */ ++ pd->lqi = mac_cb(skb)->lqi; ++ pd->sf = sf; ++ /* FIXME: make sure we do it right */ ++ memcpy(&pd->addr, &mac_cb(skb)->da, sizeof(struct ieee802154_addr)); ++ ++ /* Supplying our nitifiers with data */ ++ ieee802154_nl_beacon_indic(skb->dev, pd->addr.pan_id, ++ pd->addr.short_addr); ++ /* FIXME: We don't cache PAN descriptors yet */ ++ kfree(pd); ++ ++ offt += 2; ++ gts_spec = skb->data[offt++]; ++ /* FIXME !!! */ ++ if ((gts_spec & 7) != 0) { ++ pr_debug("We still don't parse GTS part properly"); ++ return -ENOTSUPP; ++ } ++ pa_spec = skb->data[offt++]; ++ /* FIXME !!! */ ++ if (pa_spec != 0) { ++ pr_debug("We still don't parse PA part properly"); ++ return -ENOTSUPP; ++ } ++ ++ *flags = 0; ++ ++ if (sf & IEEE802154_BEACON_SF_PANCOORD) ++ *flags |= IEEE802154_BEACON_FLAG_PANCOORD; ++ ++ if (sf & IEEE802154_BEACON_SF_CANASSOC) ++ *flags |= IEEE802154_BEACON_FLAG_CANASSOC; ++ BUG_ON(skb->len - offt < 0); ++ /* FIXME */ ++ if (buf && (skb->len - offt > 0)) ++ memcpy(buf, skb->data + offt, skb->len - offt); ++ return 0; ++} ++ ++int mac802154_process_beacon(struct net_device *dev, ++ struct sk_buff *skb) ++{ ++ int flags; ++ int ret; ++ ret = parse_beacon_frame(skb, NULL, &flags, NULL); ++ ++ /* Here we have cb->sa = coordinator address, and PAN address */ ++ ++ if (ret < 0) { ++ ret = NET_RX_DROP; ++ goto fail; ++ } ++ dev_dbg(&dev->dev, "got beacon from pan %04x\n", ++ mac_cb(skb)->sa.pan_id); ++ mac802154_beacon_hash_add(&mac_cb(skb)->sa); ++ mac802154_beacon_hash_dump(); ++ ret = NET_RX_SUCCESS; ++fail: ++ kfree_skb(skb); ++ return ret; ++} ++ +diff --git a/net/mac802154/beacon_hash.c b/net/mac802154/beacon_hash.c +new file mode 100644 +index 0000000..97fb987 +--- /dev/null ++++ b/net/mac802154/beacon_hash.c +@@ -0,0 +1,106 @@ ++/* ++ * MAC beacon hash storage ++ * ++ * Copyright 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "beacon_hash.h" ++ ++static struct hlist_head beacon_hash[IEEE802154_BEACON_HTABLE_SIZE]; ++static DEFINE_SPINLOCK(beacon_hash_lock); ++ ++static int beacon_hashfn(struct ieee802154_addr *coord_addr, u16 pan_addr) ++{ ++ return pan_addr % IEEE802154_BEACON_HTABLE_SIZE; ++} ++ ++static void __beacon_add_node(struct ieee802154_addr *coord_addr, u16 pan_addr) ++{ ++ struct beacon_node *node = ++ kzalloc(sizeof(struct beacon_node), GFP_KERNEL); ++ struct hlist_head *list = ++ &beacon_hash[beacon_hashfn(coord_addr, pan_addr)]; ++ memcpy(&node->coord_addr, coord_addr, sizeof(struct ieee802154_addr)); ++ node->pan_addr = pan_addr; ++ INIT_HLIST_NODE(&node->list); ++ hlist_add_head(&node->list, list); ++} ++ ++struct beacon_node *mac802154_beacon_find_pan( ++ struct ieee802154_addr *coord_addr, u16 pan_addr) ++{ ++ struct hlist_head *list; ++ struct hlist_node *tmp; ++ list = &beacon_hash[beacon_hashfn(coord_addr, pan_addr)]; ++ if (hlist_empty(list)) ++ return NULL; ++ hlist_for_each(tmp, list) { ++ struct beacon_node *entry = ++ hlist_entry(tmp, struct beacon_node, list); ++ if (entry->pan_addr == pan_addr) ++ return entry; ++ } ++ return NULL; ++} ++ ++void mac802154_beacon_hash_add(struct ieee802154_addr *coord_addr) ++{ ++ if (!mac802154_beacon_find_pan(coord_addr, coord_addr->pan_id)) { ++ spin_lock(&beacon_hash_lock); ++ __beacon_add_node(coord_addr, coord_addr->pan_id); ++ spin_unlock(&beacon_hash_lock); ++ } ++} ++ ++void mac802154_beacon_hash_del(struct ieee802154_addr *coord_addr) ++{ ++ struct beacon_node *entry = mac802154_beacon_find_pan(coord_addr, ++ coord_addr->pan_id); ++ if (!entry) ++ return; ++ spin_lock(&beacon_hash_lock); ++ hlist_del(&entry->list); ++ spin_unlock(&beacon_hash_lock); ++ kfree(entry); ++} ++ ++void mac802154_beacon_hash_dump(void) ++{ ++ int i; ++ struct hlist_node *tmp; ++ pr_debug("beacon hash dump begin\n"); ++ spin_lock(&beacon_hash_lock); ++ for (i = 0; i < IEEE802154_BEACON_HTABLE_SIZE; i++) { ++ struct beacon_node *entry; ++ hlist_for_each(tmp, &beacon_hash[i]) { ++ entry = hlist_entry(tmp, struct beacon_node, list); ++ pr_debug("PAN: %04x\n", entry->pan_addr); ++ } ++ } ++ spin_unlock(&beacon_hash_lock); ++ pr_debug("beacon hash dump end\n"); ++} ++ +diff --git a/net/mac802154/beacon_hash.h b/net/mac802154/beacon_hash.h +new file mode 100644 +index 0000000..a732aa5 +--- /dev/null ++++ b/net/mac802154/beacon_hash.h +@@ -0,0 +1,41 @@ ++/* ++ * MAC beacon hash storage ++ * ++ * Copyright 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#ifndef IEEE802154_BEACON_HASH_H ++#define IEEE802154_BEACON_HASH_H ++ ++#define IEEE802154_BEACON_HTABLE_SIZE 256 ++ ++struct beacon_node { ++ struct hlist_node list; ++ struct ieee802154_addr coord_addr; ++ u16 pan_addr; ++}; ++struct beacon_node *mac802154_beacon_find_pan( ++ struct ieee802154_addr *coord_addr, ++ u16 pan_addr); ++void mac802154_beacon_hash_add(struct ieee802154_addr *coord_addr); ++void mac802154_beacon_hash_del(struct ieee802154_addr *coord_addr); ++void mac802154_beacon_hash_dump(void); ++#endif ++ +diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h +new file mode 100644 +index 0000000..f35245d +--- /dev/null ++++ b/net/mac802154/mac802154.h +@@ -0,0 +1,126 @@ ++/* ++ * Copyright (C) 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Pavel Smolenskiy ++ * Maxim Gorbachyov ++ * Dmitry Eremin-Solenikov ++ */ ++#ifndef MAC802154_H ++#define MAC802154_H ++ ++#include ++ ++struct mac802154_priv { ++ struct ieee802154_dev hw; ++ struct ieee802154_ops *ops; ++ ++ struct wpan_phy *phy; ++ ++ int open_count; ++ /* As in mac80211 slaves list is modified: ++ * 1) under the RTNL ++ * 2) protected by slaves_mtx; ++ * 3) in an RCU manner ++ * ++ * So atomic readers can use any of this protection methods ++ */ ++ struct list_head slaves; ++ struct mutex slaves_mtx; ++ /* This one is used for scanning and other ++ * jobs not to be interfered with serial driver */ ++ struct workqueue_struct *dev_workqueue; ++ ++ /* ++ * These flags are also modified under slaves_mtx and RTNL, ++ * so you can read them using any of protection methods. ++ */ ++ /* SoftMAC device is registered and running. One can add subinterfaces. */ ++ unsigned running: 1; ++}; ++ ++#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) ++ ++struct mac802154_wpan_mib { ++ spinlock_t mib_lock; ++ ++ u16 pan_id; ++ u16 short_addr; ++ ++ u8 chan; ++ u8 page; ++ ++ /* MAC BSN field */ ++ u8 bsn; ++ /* MAC BSN field */ ++ u8 dsn; ++}; ++ ++struct mac802154_sub_if_data { ++ struct list_head list; /* the ieee802154_priv->slaves list */ ++ ++ struct mac802154_priv *hw; ++ struct net_device *dev; ++ ++ int type; ++ ++ spinlock_t mib_lock; ++ ++ u16 pan_id; ++ u16 short_addr; ++ ++ u8 chan; ++ u8 page; ++ ++ /* MAC BSN field */ ++ u8 bsn; ++ /* MAC DSN field */ ++ u8 dsn; ++}; ++ ++struct ieee802154_addr; ++ ++extern struct ieee802154_mlme_ops mac802154_mlme_wpan; ++extern struct simple_mlme_ops mac802154_mlme_simple; ++ ++int mac802154_mlme_scan_req(struct net_device *dev, ++ u8 type, u32 channels, u8 page, u8 duration); ++ ++int mac802154_process_cmd(struct net_device *dev, struct sk_buff *skb); ++int mac802154_process_beacon(struct net_device *dev, struct sk_buff *skb); ++int mac802154_send_beacon(struct net_device *dev, ++ struct ieee802154_addr *saddr, ++ u16 pan_id, const u8 *buf, int len, ++ int flags, struct list_head *al); ++int mac802154_send_beacon_req(struct net_device *dev); ++ ++struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev); ++ ++void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb); ++void mac802154_monitor_setup(struct net_device *dev); ++ ++void mac802154_smacs_rx(struct mac802154_priv *priv, struct sk_buff *skb); ++void mac802154_smac_setup(struct net_device *dev); ++ ++void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb); ++void mac802154_wpan_setup(struct net_device *dev); ++ ++int mac802154_slave_open(struct net_device *dev); ++int mac802154_slave_close(struct net_device *dev); ++ ++netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, ++ u8 page, u8 chan); ++#endif +diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c +new file mode 100644 +index 0000000..e92947a +--- /dev/null ++++ b/net/mac802154/mac_cmd.c +@@ -0,0 +1,365 @@ ++/* ++ * MAC commands interface ++ * ++ * Copyright 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mac802154.h" ++#include "mib.h" ++ ++static int mac802154_cmd_beacon_req(struct sk_buff *skb) ++{ ++ struct ieee802154_addr saddr; /* jeez */ ++ int flags = 0; ++ u16 shortaddr; ++ ++ if (skb->len != 1) ++ return -EINVAL; ++ ++ if (skb->pkt_type != PACKET_BROADCAST) ++ return 0; ++ ++ /* Checking if we're really PAN coordinator ++ * before sending beacons */ ++ if (!(skb->dev->priv_flags & IFF_IEEE802154_COORD)) ++ return 0; ++ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE || ++ mac_cb(skb)->da.addr_type != IEEE802154_ADDR_SHORT || ++ mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST || ++ mac_cb(skb)->da.short_addr != IEEE802154_ADDR_BROADCAST) ++ return -EINVAL; ++ ++ shortaddr = mac802154_dev_get_short_addr(skb->dev); ++ if (shortaddr != IEEE802154_ADDR_BROADCAST && ++ shortaddr != IEEE802154_ADDR_UNDEF) { ++ saddr.addr_type = IEEE802154_ADDR_SHORT; ++ saddr.short_addr = shortaddr; ++ } else { ++ saddr.addr_type = IEEE802154_ADDR_LONG; ++ memcpy(saddr.hwaddr, skb->dev->dev_addr, IEEE802154_ADDR_LEN); ++ } ++ saddr.pan_id = mac802154_dev_get_pan_id(skb->dev); ++ ++ ++ /* 7 bytes of MHR and 1 byte of command frame identifier ++ * We have no information in this command to proceed with. ++ * we need to submit beacon as answer to this. */ ++ ++ return mac802154_send_beacon(skb->dev, &saddr, ++ ieee802154_mlme_ops(skb->dev)->get_pan_id(skb->dev), ++ NULL, 0, flags, NULL); ++} ++ ++static int mac802154_cmd_assoc_req(struct sk_buff *skb) ++{ ++ u8 cap; ++ ++ if (skb->len != 2) ++ return -EINVAL; ++ ++ if (skb->pkt_type != PACKET_HOST) ++ return 0; ++ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_LONG || ++ mac_cb(skb)->sa.pan_id != IEEE802154_PANID_BROADCAST) ++ return -EINVAL; ++ ++ /* ++ * FIXME: check that we allow incoming ASSOC requests ++ * by consulting MIB ++ */ ++ ++ cap = skb->data[1]; ++ ++ return ieee802154_nl_assoc_indic(skb->dev, &mac_cb(skb)->sa, cap); ++} ++ ++static int mac802154_cmd_assoc_resp(struct sk_buff *skb) ++{ ++ u8 status; ++ u16 short_addr; ++ ++ if (skb->len != 4) ++ return -EINVAL; ++ ++ if (skb->pkt_type != PACKET_HOST) ++ return 0; ++ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_LONG || ++ mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_LONG || ++ !(mac_cb(skb)->flags & MAC_CB_FLAG_INTRAPAN)) ++ return -EINVAL; ++ ++ /* FIXME: check that we requested association ? */ ++ ++ status = skb->data[3]; ++ short_addr = skb->data[1] | (skb->data[2] << 8); ++ pr_info("Received ASSOC-RESP status %x, addr %hx\n", status, ++ short_addr); ++ if (status) { ++ mac802154_dev_set_short_addr(skb->dev, ++ IEEE802154_ADDR_BROADCAST); ++ mac802154_dev_set_pan_id(skb->dev, ++ IEEE802154_PANID_BROADCAST); ++ } else ++ mac802154_dev_set_short_addr(skb->dev, short_addr); ++ ++ return ieee802154_nl_assoc_confirm(skb->dev, short_addr, status); ++} ++ ++static int mac802154_cmd_disassoc_notify(struct sk_buff *skb) ++{ ++ u8 reason; ++ ++ if (skb->len != 2) ++ return -EINVAL; ++ ++ if (skb->pkt_type != PACKET_HOST) ++ return 0; ++ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_LONG || ++ (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_LONG && ++ mac_cb(skb)->da.addr_type != IEEE802154_ADDR_SHORT) || ++ mac_cb(skb)->sa.pan_id != mac_cb(skb)->da.pan_id) ++ return -EINVAL; ++ ++ reason = skb->data[1]; ++ ++ /* FIXME: checks if this was our coordinator and the disassoc us */ ++ /* FIXME: if we device, one should receive ->da and not ->sa */ ++ /* FIXME: the status should also help */ ++ ++ return ieee802154_nl_disassoc_indic(skb->dev, &mac_cb(skb)->sa, ++ reason); ++} ++ ++int mac802154_process_cmd(struct net_device *dev, struct sk_buff *skb) ++{ ++ u8 cmd; ++ ++ if (skb->len < 1) { ++ pr_warning("Uncomplete command frame!\n"); ++ goto drop; ++ } ++ ++ cmd = *(skb->data); ++ pr_debug("Command %02x on device %s\n", cmd, dev->name); ++ ++ switch (cmd) { ++ case IEEE802154_CMD_ASSOCIATION_REQ: ++ mac802154_cmd_assoc_req(skb); ++ break; ++ case IEEE802154_CMD_ASSOCIATION_RESP: ++ mac802154_cmd_assoc_resp(skb); ++ break; ++ case IEEE802154_CMD_DISASSOCIATION_NOTIFY: ++ mac802154_cmd_disassoc_notify(skb); ++ break; ++ case IEEE802154_CMD_BEACON_REQ: ++ mac802154_cmd_beacon_req(skb); ++ break; ++ default: ++ pr_debug("Frame type is not supported yet\n"); ++ goto drop; ++ } ++ ++ ++ kfree_skb(skb); ++ return NET_RX_SUCCESS; ++ ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++static int mac802154_send_cmd(struct net_device *dev, ++ struct ieee802154_addr *addr, struct ieee802154_addr *saddr, ++ const u8 *buf, int len) ++{ ++ struct sk_buff *skb; ++ int hlen, tlen; ++ int err; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ hlen = LL_RESERVED_SPACE(dev); ++ tlen = dev->needed_tailroom; ++ skb = alloc_skb(len + hlen + tlen, GFP_KERNEL); ++ if (!skb) ++ return -ENOMEM; ++ ++ skb_reserve(skb, LL_RESERVED_SPACE(dev)); ++ ++ skb_reset_network_header(skb); ++ ++ mac_cb(skb)->flags = IEEE802154_FC_TYPE_MAC_CMD | MAC_CB_FLAG_ACKREQ; ++ mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); ++ err = dev_hard_header(skb, dev, ETH_P_IEEE802154, addr, saddr, len); ++ if (err < 0) { ++ kfree_skb(skb); ++ return err; ++ } ++ ++ skb_reset_mac_header(skb); ++ memcpy(skb_put(skb, len), buf, len); ++ ++ skb->dev = dev; ++ skb->protocol = htons(ETH_P_IEEE802154); ++ ++ return dev_queue_xmit(skb); ++} ++ ++int mac802154_send_beacon_req(struct net_device *dev) ++{ ++ struct ieee802154_addr addr; ++ struct ieee802154_addr saddr; ++ u8 cmd = IEEE802154_CMD_BEACON_REQ; ++ addr.addr_type = IEEE802154_ADDR_SHORT; ++ addr.short_addr = IEEE802154_ADDR_BROADCAST; ++ addr.pan_id = IEEE802154_PANID_BROADCAST; ++ saddr.addr_type = IEEE802154_ADDR_NONE; ++ return mac802154_send_cmd(dev, &addr, &saddr, &cmd, 1); ++} ++ ++ ++static int mac802154_mlme_assoc_req(struct net_device *dev, ++ struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap) ++{ ++ struct ieee802154_addr saddr; ++ u8 buf[2]; ++ int pos = 0; ++ ++ saddr.addr_type = IEEE802154_ADDR_LONG; ++ saddr.pan_id = IEEE802154_PANID_BROADCAST; ++ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN); ++ ++ ++ /* FIXME: set PIB/MIB info */ ++ mac802154_dev_set_pan_id(dev, addr->pan_id); ++ mac802154_dev_set_page_channel(dev, page, channel); ++ mac802154_dev_set_ieee_addr(dev); ++ ++ buf[pos++] = IEEE802154_CMD_ASSOCIATION_REQ; ++ buf[pos++] = cap; ++ ++ return mac802154_send_cmd(dev, addr, &saddr, buf, pos); ++} ++ ++static int mac802154_mlme_assoc_resp(struct net_device *dev, ++ struct ieee802154_addr *addr, u16 short_addr, u8 status) ++{ ++ struct ieee802154_addr saddr; ++ u8 buf[4]; ++ int pos = 0; ++ ++ saddr.addr_type = IEEE802154_ADDR_LONG; ++ saddr.pan_id = addr->pan_id; ++ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN); ++ ++ buf[pos++] = IEEE802154_CMD_ASSOCIATION_RESP; ++ buf[pos++] = short_addr; ++ buf[pos++] = short_addr >> 8; ++ buf[pos++] = status; ++ ++ return mac802154_send_cmd(dev, addr, &saddr, buf, pos); ++} ++ ++static int mac802154_mlme_disassoc_req(struct net_device *dev, ++ struct ieee802154_addr *addr, u8 reason) ++{ ++ struct ieee802154_addr saddr; ++ u8 buf[2]; ++ int pos = 0; ++ int ret; ++ ++ saddr.addr_type = IEEE802154_ADDR_LONG; ++ saddr.pan_id = addr->pan_id; ++ memcpy(saddr.hwaddr, dev->dev_addr, IEEE802154_ADDR_LEN); ++ ++ buf[pos++] = IEEE802154_CMD_DISASSOCIATION_NOTIFY; ++ buf[pos++] = reason; ++ ++ ret = mac802154_send_cmd(dev, addr, &saddr, buf, pos); ++ ++ /* FIXME: this should be after the ack receved */ ++ mac802154_dev_set_pan_id(dev, 0xffff); ++ mac802154_dev_set_short_addr(dev, 0xffff); ++ ieee802154_nl_disassoc_confirm(dev, 0x00); ++ ++ return ret; ++} ++ ++static int mac802154_mlme_start_req(struct net_device *dev, ++ struct ieee802154_addr *addr, ++ u8 channel, u8 page, ++ u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx, ++ u8 coord_realign) ++{ ++ BUG_ON(addr->addr_type != IEEE802154_ADDR_SHORT); ++ ++ mac802154_dev_set_pan_id(dev, addr->pan_id); ++ mac802154_dev_set_short_addr(dev, addr->short_addr); ++ mac802154_dev_set_ieee_addr(dev); ++ mac802154_dev_set_page_channel(dev, page, channel); ++ ++ /* ++ * FIXME: add validation for unused parameters to be sane ++ * for SoftMAC ++ */ ++ ++ if (pan_coord) ++ dev->priv_flags |= IFF_IEEE802154_COORD; ++ else ++ dev->priv_flags &= ~IFF_IEEE802154_COORD; ++ ++ mac802154_dev_set_pan_coord(dev); ++ ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS); ++ ++ return 0; ++} ++ ++struct ieee802154_mlme_ops mac802154_mlme_wpan = { ++ .assoc_req = mac802154_mlme_assoc_req, ++ .assoc_resp = mac802154_mlme_assoc_resp, ++ .disassoc_req = mac802154_mlme_disassoc_req, ++ .start_req = mac802154_mlme_start_req, ++ .scan_req = mac802154_mlme_scan_req, ++ ++ .wpan_ops.get_phy = mac802154_get_phy, ++ ++ .get_pan_id = mac802154_dev_get_pan_id, ++ .get_short_addr = mac802154_dev_get_short_addr, ++ .get_dsn = mac802154_dev_get_dsn, ++ .get_bsn = mac802154_dev_get_bsn, ++}; ++ ++struct simple_mlme_ops mac802154_mlme_simple = { ++ .get_phy = mac802154_get_phy, ++}; +diff --git a/net/mac802154/main.c b/net/mac802154/main.c +new file mode 100644 +index 0000000..f2acbcb +--- /dev/null ++++ b/net/mac802154/main.c +@@ -0,0 +1,283 @@ ++/* ++ * Copyright (C) 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mac802154.h" ++#include "mib.h" ++ ++int mac802154_slave_open(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ int res = 0; ++ ++ if (priv->hw->open_count++ == 0) { ++ res = priv->hw->ops->start(&priv->hw->hw); ++ WARN_ON(res); ++ if (res) ++ goto err; ++ } ++ ++ if (priv->hw->ops->ieee_addr) { ++ res = priv->hw->ops->ieee_addr(&priv->hw->hw, dev->dev_addr); ++ WARN_ON(res); ++ if (res) ++ goto err; ++ mac802154_dev_set_ieee_addr(dev); ++ } ++ ++ netif_start_queue(dev); ++ return 0; ++err: ++ priv->hw->open_count--; ++ ++ return res; ++} ++ ++int mac802154_slave_close(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ ++ dev->priv_flags &= ~IFF_IEEE802154_COORD; ++ ++ netif_stop_queue(dev); ++ ++ if ((--priv->hw->open_count) == 0) ++ priv->hw->ops->stop(&priv->hw->hw); ++ ++ return 0; ++} ++ ++ ++static int mac802154_netdev_register(struct wpan_phy *phy, ++ struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ struct mac802154_priv *ipriv; ++ int err; ++ ++ ipriv = wpan_phy_priv(phy); ++ ++ priv = netdev_priv(dev); ++ priv->dev = dev; ++ priv->hw = ipriv; ++ ++ dev->needed_headroom = ipriv->hw.extra_tx_headroom; ++ ++ SET_NETDEV_DEV(dev, &ipriv->phy->dev); ++ ++ mutex_lock(&ipriv->slaves_mtx); ++ if (!ipriv->running) { ++ mutex_unlock(&ipriv->slaves_mtx); ++ return -ENODEV; ++ } ++ mutex_unlock(&ipriv->slaves_mtx); ++ ++ err = register_netdev(dev); ++ if (err < 0) ++ return err; ++ ++ rtnl_lock(); ++ mutex_lock(&ipriv->slaves_mtx); ++ list_add_tail_rcu(&priv->list, &ipriv->slaves); ++ mutex_unlock(&ipriv->slaves_mtx); ++ rtnl_unlock(); ++ ++ return 0; ++} ++ ++static void mac802154_del_iface(struct wpan_phy *phy, ++ struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *sdata; ++ ASSERT_RTNL(); ++ ++ sdata = netdev_priv(dev); ++ ++ BUG_ON(sdata->hw->phy != phy); ++ ++ mutex_lock(&sdata->hw->slaves_mtx); ++ list_del_rcu(&sdata->list); ++ mutex_unlock(&sdata->hw->slaves_mtx); ++ ++ synchronize_rcu(); ++ unregister_netdevice(sdata->dev); ++} ++ ++static struct net_device *mac802154_add_iface(struct wpan_phy *phy, ++ const char *name, int type) ++{ ++ struct net_device *dev; ++ int err = -ENOMEM; ++ ++ switch (type) { ++ case IEEE802154_DEV_WPAN: ++ dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), ++ name, mac802154_wpan_setup); ++ break; ++ case IEEE802154_DEV_MONITOR: ++ dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), ++ name, mac802154_monitor_setup); ++ break; ++ case IEEE802154_DEV_SMAC: ++ dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), ++ name, mac802154_smac_setup); ++ break; ++ default: ++ dev = NULL; ++ err = -EINVAL; ++ break; ++ } ++ if (!dev) ++ goto err; ++ ++ ++ err = mac802154_netdev_register(phy, dev); ++ ++ if (err) ++ goto err_free; ++ ++ dev_hold(dev); /* we return a device w/ incremented refcount */ ++ return dev; ++ ++err_free: ++ free_netdev(dev); ++err: ++ return ERR_PTR(err); ++} ++ ++ ++struct ieee802154_dev *ieee802154_alloc_device(size_t priv_size, ++ struct ieee802154_ops *ops) ++{ ++ struct wpan_phy *phy; ++ struct mac802154_priv *priv; ++ ++ phy = wpan_phy_alloc(ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_size); ++ if (!phy) { ++ printk(KERN_ERR ++ "Failure to initialize master IEEE802154 device\n"); ++ return NULL; ++ } ++ ++ priv = wpan_phy_priv(phy); ++ priv->hw.phy = priv->phy = phy; ++ ++ priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN); ++ ++ BUG_ON(!ops); ++ BUG_ON(!ops->xmit); ++ BUG_ON(!ops->ed); ++ BUG_ON(!ops->start); ++ BUG_ON(!ops->stop); ++ ++ priv->ops = ops; ++ ++ INIT_LIST_HEAD(&priv->slaves); ++ mutex_init(&priv->slaves_mtx); ++ ++ return &priv->hw; ++} ++EXPORT_SYMBOL(ieee802154_alloc_device); ++ ++void ieee802154_free_device(struct ieee802154_dev *hw) ++{ ++ struct mac802154_priv *priv = mac802154_to_priv(hw); ++ ++ BUG_ON(!list_empty(&priv->slaves)); ++ ++ wpan_phy_free(priv->phy); ++} ++EXPORT_SYMBOL(ieee802154_free_device); ++ ++int ieee802154_register_device(struct ieee802154_dev *dev) ++{ ++ struct mac802154_priv *priv = mac802154_to_priv(dev); ++ int rc; ++ ++ priv->dev_workqueue = ++ create_singlethread_workqueue(wpan_phy_name(priv->phy)); ++ if (!priv->dev_workqueue) { ++ rc = -ENOMEM; ++ goto out; ++ } ++ ++ wpan_phy_set_dev(priv->phy, priv->hw.parent); ++ ++ priv->phy->add_iface = mac802154_add_iface; ++ priv->phy->del_iface = mac802154_del_iface; ++ ++ rc = wpan_phy_register(priv->phy); ++ if (rc < 0) ++ goto out_wq; ++ ++ rtnl_lock(); ++ mutex_lock(&priv->slaves_mtx); ++ priv->running = 1; ++ mutex_unlock(&priv->slaves_mtx); ++ rtnl_unlock(); ++ ++ return 0; ++ ++out_wq: ++ destroy_workqueue(priv->dev_workqueue); ++out: ++ return rc; ++} ++EXPORT_SYMBOL(ieee802154_register_device); ++ ++void ieee802154_unregister_device(struct ieee802154_dev *dev) ++{ ++ struct mac802154_priv *priv = mac802154_to_priv(dev); ++ struct mac802154_sub_if_data *sdata, *next; ++ ++ ++ flush_workqueue(priv->dev_workqueue); ++ destroy_workqueue(priv->dev_workqueue); ++ ++ rtnl_lock(); ++ ++ mutex_lock(&priv->slaves_mtx); ++ priv->running = 0; ++ mutex_unlock(&priv->slaves_mtx); ++ ++ list_for_each_entry_safe(sdata, next, &priv->slaves, list) { ++ mutex_lock(&sdata->hw->slaves_mtx); ++ list_del(&sdata->list); ++ mutex_unlock(&sdata->hw->slaves_mtx); ++ ++ unregister_netdevice(sdata->dev); ++ } ++ ++ rtnl_unlock(); ++ ++ wpan_phy_unregister(priv->phy); ++} ++EXPORT_SYMBOL(ieee802154_unregister_device); ++ ++MODULE_DESCRIPTION("IEEE 802.15.4 implementation"); ++MODULE_LICENSE("GPL v2"); ++ +diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c +new file mode 100644 +index 0000000..23871df +--- /dev/null ++++ b/net/mac802154/mib.c +@@ -0,0 +1,249 @@ ++/* ++ * Copyright 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ * Sergey Lapin ++ * Maxim Gorbachyov ++ */ ++ ++#include ++ ++#include ++#include ++ ++#include "mac802154.h" ++#include "mib.h" ++ ++struct phy_chan_notify_work { ++ struct work_struct work; ++ struct net_device *dev; ++}; ++ ++struct hw_addr_filt_notify_work { ++ struct work_struct work; ++ struct net_device *dev; ++ unsigned long changed; ++}; ++ ++static void hw_addr_notify(struct work_struct *work) ++{ ++ struct hw_addr_filt_notify_work *nw = container_of(work, ++ struct hw_addr_filt_notify_work, work); ++ struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); ++ int res; ++ ++ res = hw->ops->set_hw_addr_filt(&hw->hw, ++ &hw->hw.hw_filt, nw->changed); ++ if (res) ++ pr_debug("%s: failed changed mask %lx\n", ++ __func__, nw->changed); ++ ++ kfree(nw); ++ ++ return; ++} ++ ++static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ struct hw_addr_filt_notify_work *work; ++ ++ work = kzalloc(sizeof(*work), GFP_ATOMIC); ++ if (!work) ++ return; ++ ++ INIT_WORK(&work->work, hw_addr_notify); ++ work->dev = dev; ++ work->changed = changed; ++ queue_work(priv->hw->dev_workqueue, &work->work); ++ ++ return; ++} ++ ++static void phy_chan_notify(struct work_struct *work) ++{ ++ struct phy_chan_notify_work *nw = container_of(work, ++ struct phy_chan_notify_work, work); ++ struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); ++ struct mac802154_sub_if_data *priv = netdev_priv(nw->dev); ++ int res; ++ ++ res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan); ++ if (res) ++ pr_debug("set_channel failed\n"); ++ ++ kfree(nw); ++ ++ return; ++} ++ ++u16 mac802154_dev_get_pan_id(const struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ u16 ret; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ ret = priv->pan_id; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ return ret; ++} ++ ++u16 mac802154_dev_get_short_addr(const struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ u16 ret; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ ret = priv->short_addr; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ return ret; ++} ++ ++void mac802154_dev_set_pan_id(struct net_device *dev, u16 val) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ priv->pan_id = val; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ if (priv->hw->ops->set_hw_addr_filt && ++ (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) { ++ priv->hw->hw.hw_filt.pan_id = priv->pan_id; ++ set_hw_addr_filt(dev, IEEE802515_PANID_CHANGED); ++ } ++} ++ ++void mac802154_dev_set_pan_coord(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ int pan_coord = !!(dev->priv_flags & IFF_IEEE802154_COORD); ++ ++ if (priv->hw->ops->set_hw_addr_filt && ++ (priv->hw->hw.hw_filt.pan_coord != pan_coord)) { ++ priv->hw->hw.hw_filt.pan_coord = pan_coord; ++ set_hw_addr_filt(dev, IEEE802515_PANC_CHANGED); ++ } ++} ++ ++void mac802154_dev_set_short_addr(struct net_device *dev, u16 val) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ priv->short_addr = val; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ if (priv->hw->ops->set_hw_addr_filt && ++ (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) { ++ priv->hw->hw.hw_filt.short_addr = priv->short_addr; ++ set_hw_addr_filt(dev, IEEE802515_SADDR_CHANGED); ++ } ++} ++ ++void mac802154_dev_set_ieee_addr(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ ++ if (priv->hw->ops->set_hw_addr_filt && ++ memcmp(priv->hw->hw.hw_filt.ieee_addr, ++ dev->dev_addr, IEEE802154_ALEN)) { ++ memcpy(priv->hw->hw.hw_filt.ieee_addr, ++ dev->dev_addr, IEEE802154_ALEN); ++ set_hw_addr_filt(dev, IEEE802515_IEEEADDR_CHANGED); ++ } ++} ++ ++void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ struct phy_chan_notify_work *work; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ priv->page = page; ++ priv->chan = chan; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ if (priv->hw->phy->current_channel != priv->chan || ++ priv->hw->phy->current_page != priv->page) { ++ work = kzalloc(sizeof(*work), GFP_ATOMIC); ++ if (!work) ++ return; ++ ++ INIT_WORK(&work->work, phy_chan_notify); ++ work->dev = dev; ++ queue_work(priv->hw->dev_workqueue, &work->work); ++ } ++} ++ ++u8 mac802154_dev_get_dsn(const struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ u16 ret; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ ret = priv->dsn++; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ return ret; ++} ++ ++u8 mac802154_dev_get_bsn(const struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ u16 ret; ++ ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ spin_lock_bh(&priv->mib_lock); ++ ret = priv->bsn++; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ return ret; ++} ++ ++struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ BUG_ON(dev->type != ARPHRD_IEEE802154); ++ ++ return priv->hw; ++} ++ ++struct wpan_phy *mac802154_get_phy(const struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ BUG_ON(dev->type != ARPHRD_IEEE802154 ++ && dev->type != ARPHRD_IEEE802154_MONITOR ++ && dev->type != ARPHRD_SMAC); ++ ++ return to_phy(get_device(&priv->hw->phy->dev)); ++} +diff --git a/net/mac802154/mib.h b/net/mac802154/mib.h +new file mode 100644 +index 0000000..228f6d0 +--- /dev/null ++++ b/net/mac802154/mib.h +@@ -0,0 +1,35 @@ ++/* ++ * Copyright 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#ifndef MIB802154_H ++#define MIB802154_H ++ ++/* FIXME: should be dropped in favour of generic MIB API */ ++u8 mac802154_dev_get_dsn(const struct net_device *dev); ++u8 mac802154_dev_get_bsn(const struct net_device *dev); ++u16 mac802154_dev_get_pan_id(const struct net_device *dev); ++u16 mac802154_dev_get_short_addr(const struct net_device *dev); ++void mac802154_dev_set_pan_id(struct net_device *dev, u16 val); ++void mac802154_dev_set_pan_coord(struct net_device *dev); ++void mac802154_dev_set_short_addr(struct net_device *dev, u16 val); ++void mac802154_dev_set_ieee_addr(struct net_device *dev); ++void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); ++struct wpan_phy *mac802154_get_phy(const struct net_device *dev); ++ ++ ++#endif +diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c +new file mode 100644 +index 0000000..24a4fbb +--- /dev/null ++++ b/net/mac802154/monitor.c +@@ -0,0 +1,117 @@ ++/* ++ * Copyright 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ * Sergey Lapin ++ * Maxim Gorbachyov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "mac802154.h" ++ ++static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ u8 chan, page; ++ ++ priv = netdev_priv(dev); ++ ++ /* FIXME: locking */ ++ chan = priv->hw->phy->current_channel; ++ page = priv->hw->phy->current_page; ++ ++ if (chan == (u8)-1) /* not init */ ++ return NETDEV_TX_OK; ++ ++ BUG_ON(page >= WPAN_NUM_PAGES); ++ BUG_ON(chan >= 27); ++ ++ skb->skb_iif = dev->ifindex; ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ return mac802154_tx(priv->hw, skb, page, chan); ++} ++ ++ ++void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb) ++{ ++ struct sk_buff *skb2; ++ struct mac802154_sub_if_data *sdata; ++ u16 crc = crc_ccitt(0, skb->data, skb->len); ++ u8 *data; ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(sdata, &priv->slaves, list) { ++ if (sdata->type != IEEE802154_DEV_MONITOR) ++ continue; ++ ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ skb2->dev = sdata->dev; ++ skb2->pkt_type = PACKET_HOST; ++ data = skb_put(skb2, 2); ++ data[0] = crc & 0xff; ++ data[1] = crc >> 8; ++ ++ if (in_interrupt()) ++ netif_rx(skb2); ++ else ++ netif_rx_ni(skb2); ++ } ++ rcu_read_unlock(); ++} ++ ++static const struct net_device_ops mac802154_monitor_ops = { ++ .ndo_open = mac802154_slave_open, ++ .ndo_stop = mac802154_slave_close, ++ .ndo_start_xmit = mac802154_monitor_xmit, ++}; ++ ++void mac802154_monitor_setup(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ ++ dev->addr_len = 0; ++ dev->features = NETIF_F_HW_CSUM; ++ dev->hard_header_len = 0; ++ dev->needed_tailroom = 2; /* FCS */ ++ dev->mtu = 127; ++ dev->tx_queue_len = 10; ++ dev->type = ARPHRD_IEEE802154_MONITOR; ++ dev->flags = IFF_NOARP | IFF_BROADCAST; ++ dev->watchdog_timeo = 0; ++ ++ dev->destructor = free_netdev; ++ dev->netdev_ops = &mac802154_monitor_ops; ++ dev->ml_priv = &mac802154_mlme_simple; ++ ++ priv = netdev_priv(dev); ++ priv->type = IEEE802154_DEV_MONITOR; ++ ++ priv->chan = -1; /* not initialized */ ++ priv->page = 0; /* for compat */ ++} ++ +diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c +new file mode 100644 +index 0000000..0e9d5d4 +--- /dev/null ++++ b/net/mac802154/rx.c +@@ -0,0 +1,117 @@ ++/* ++ * Copyright (C) 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Pavel Smolenskiy ++ * Maxim Gorbachyov ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mac802154.h" ++ ++static void mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb) ++{ ++ struct mac802154_priv *priv = mac802154_to_priv(hw); ++ ++ BUILD_BUG_ON(sizeof(struct ieee802154_mac_cb) > sizeof(skb->cb)); ++ pr_debug("%s()\n", __func__); ++ ++ if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { ++ u16 crc; ++ ++ if (skb->len < 2) { ++ pr_debug("%s(): Got invalid frame\n", __func__); ++ goto out; ++ } ++ crc = crc_ccitt(0, skb->data, skb->len); ++ if (crc) { ++ pr_debug("%s(): CRC mismatch\n", __func__); ++ goto out; ++ } ++ skb_trim(skb, skb->len - 2); /* CRC */ ++ } ++ ++ mac802154_monitors_rx(priv, skb); ++ mac802154_smacs_rx(priv, skb); ++ mac802154_wpans_rx(priv, skb); ++ ++out: ++ dev_kfree_skb(skb); ++ return; ++} ++ ++static void __mac802154_rx_prepare(struct ieee802154_dev *dev, ++ struct sk_buff *skb, u8 lqi) ++{ ++ BUG_ON(!skb); ++ ++ mac_cb(skb)->lqi = lqi; ++ ++ skb->protocol = htons(ETH_P_IEEE802154); ++ ++ skb_reset_mac_header(skb); ++} ++ ++void mac802154_rx(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) ++{ ++ __mac802154_rx_prepare(dev, skb, lqi); ++ ++ mac802154_subif_rx(dev, skb); ++} ++EXPORT_SYMBOL(mac802154_rx); ++ ++struct rx_work { ++ struct sk_buff *skb; ++ struct work_struct work; ++ struct ieee802154_dev *dev; ++}; ++ ++static void mac802154_rx_worker(struct work_struct *work) ++{ ++ struct rx_work *rw = container_of(work, struct rx_work, work); ++ struct sk_buff *skb = rw->skb; ++ ++ mac802154_subif_rx(rw->dev, skb); ++ kfree(rw); ++} ++ ++void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, ++ struct sk_buff *skb, u8 lqi) ++{ ++ struct mac802154_priv *priv = mac802154_to_priv(dev); ++ struct rx_work *work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC); ++ ++ if (!work) ++ return; ++ ++ __mac802154_rx_prepare(dev, skb, lqi); ++ ++ INIT_WORK(&work->work, mac802154_rx_worker); ++ work->skb = skb; ++ work->dev = dev; ++ ++ queue_work(priv->dev_workqueue, &work->work); ++} ++EXPORT_SYMBOL(ieee802154_rx_irqsafe); +diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c +new file mode 100644 +index 0000000..7c6f313 +--- /dev/null ++++ b/net/mac802154/scan.c +@@ -0,0 +1,203 @@ ++/* ++ * scan.c ++ * ++ * Description: MAC scan helper functions. ++ * ++ * Copyright (C) 2007, 2008 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Pavel Smolenskiy ++ * Maxim Gorbachyov ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "mac802154.h" ++ ++/* ++ * ED scan is periodic issuing of ed device function ++ * on evry permitted channel, so it is virtually PHY-only scan */ ++ ++struct scan_work { ++ struct work_struct work; ++ ++ int (*scan_ch)(struct scan_work *work, int channel, u8 duration); ++ struct net_device *dev; ++ ++ u8 edl[27]; ++ ++ u8 type; ++ u32 channels; ++ u8 page; ++ u8 duration; ++}; ++ ++static int scan_ed(struct scan_work *work, int channel, u8 duration) ++{ ++ int ret; ++ struct mac802154_priv *hw = mac802154_slave_get_priv(work->dev); ++ pr_debug("ed scan channel %d duration %d\n", channel, duration); ++ mutex_lock(&hw->phy->pib_lock); ++ ret = hw->ops->ed(&hw->hw, &work->edl[channel]); ++ mutex_unlock(&hw->phy->pib_lock); ++ pr_debug("ed scan channel %d value %d\n", channel, work->edl[channel]); ++ return ret; ++} ++ ++static int scan_passive(struct scan_work *work, int channel, u8 duration) ++{ ++ unsigned long j; ++ pr_debug("passive scan channel %d duration %d\n", channel, duration); ++ ++ /* Hope 2 msecs will be enough for scan */ ++ j = msecs_to_jiffies(2); ++ while (j > 0) ++ j = schedule_timeout(j); ++ ++ return 0; ++} ++ ++/* Active scan is periodic submission of beacon request ++ * and waiting for beacons which is useful for collecting LWPAN information */ ++static int scan_active(struct scan_work *work, int channel, u8 duration) ++{ ++ int ret; ++ pr_debug("active scan channel %d duration %d\n", channel, duration); ++ ret = mac802154_send_beacon_req(work->dev); ++ if (ret) ++ return ret; ++ return scan_passive(work, channel, duration); ++} ++ ++static int scan_orphan(struct scan_work *work, int channel, u8 duration) ++{ ++ pr_debug("orphan scan channel %d duration %d\n", channel, duration); ++ return 0; ++} ++ ++static void scanner(struct work_struct *work) ++{ ++ struct scan_work *sw = container_of(work, struct scan_work, work); ++ struct mac802154_priv *hw = mac802154_slave_get_priv(sw->dev); ++ struct mac802154_sub_if_data *priv = netdev_priv(sw->dev); ++ int i; ++ int ret; ++ ++ for (i = 0; i < 27; i++) { ++ if (!(sw->channels & (1 << i))) ++ continue; ++ ++ mutex_lock(&hw->phy->pib_lock); ++ ret = hw->ops->set_channel(&hw->hw, sw->page, i); ++ mutex_unlock(&hw->phy->pib_lock); ++ if (ret) ++ goto exit_error; ++ ++ priv->chan = i; ++ priv->page = sw->page; ++ ++ ret = sw->scan_ch(sw, i, sw->duration); ++ if (ret) ++ goto exit_error; ++ ++ sw->channels &= ~(1 << i); ++ } ++ ++ ieee802154_nl_scan_confirm(sw->dev, IEEE802154_SUCCESS, sw->type, ++ sw->channels, sw->page, sw->edl/*, NULL */); ++ ++ kfree(sw); ++ ++ return; ++ ++exit_error: ++ ieee802154_nl_scan_confirm(sw->dev, IEEE802154_INVALID_PARAMETER, ++ sw->type, sw->channels, sw->page, NULL/*, NULL */); ++ kfree(sw); ++ return; ++} ++ ++/* ++ * Alloc ed_detect list for ED scan. ++ * ++ * @param mac current mac pointer ++ * @param type type of the scan to be performed ++ * @param channels 32-bit mask of requested to scan channels ++ * @param duration scan duration, see ieee802.15.4-2003.pdf, page 145. ++ * @return 0 if request is ok, errno otherwise. ++ */ ++int mac802154_mlme_scan_req(struct net_device *dev, ++ u8 type, u32 channels, u8 page, u8 duration) ++{ ++ struct mac802154_priv *hw = mac802154_slave_get_priv(dev); ++ struct scan_work *work; ++ ++ pr_debug("%s()\n", __func__); ++ ++ if (page > WPAN_NUM_PAGES) ++ goto inval; ++ if (duration > 14) ++ goto inval; ++ if (channels & ~hw->phy->channels_supported[page]) ++ goto inval; ++ ++ work = kzalloc(sizeof(struct scan_work), GFP_KERNEL); ++ if (!work) ++ goto inval; ++ ++ work->dev = dev; ++ work->channels = channels; ++ work->page = page; ++ work->duration = duration; ++ work->type = type; ++ ++ switch (type) { ++ case IEEE802154_MAC_SCAN_ED: ++ work->scan_ch = scan_ed; ++ break; ++ case IEEE802154_MAC_SCAN_ACTIVE: ++ work->scan_ch = scan_active; ++ break; ++ case IEEE802154_MAC_SCAN_PASSIVE: ++ work->scan_ch = scan_passive; ++ break; ++ case IEEE802154_MAC_SCAN_ORPHAN: ++ work->scan_ch = scan_orphan; ++ break; ++ default: ++ pr_debug("%s(): invalid type %d\n", __func__, type); ++ goto inval; ++ } ++ ++ INIT_WORK(&work->work, scanner); ++ queue_work(hw->dev_workqueue, &work->work); ++ ++ return 0; ++ ++inval: ++ ieee802154_nl_scan_confirm(dev, IEEE802154_INVALID_PARAMETER, type, ++ channels, page, NULL/*, NULL */); ++ return -EINVAL; ++} ++ +diff --git a/net/mac802154/smac.c b/net/mac802154/smac.c +new file mode 100644 +index 0000000..88bf1e1 +--- /dev/null ++++ b/net/mac802154/smac.c +@@ -0,0 +1,128 @@ ++/* ++ * Copyright 2010 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mac802154.h" ++ ++static const u8 smac_header[] = {0x7E, 0xFF}; ++ ++static netdev_tx_t mac802154_smac_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ u8 chan, page; ++ ++ priv = netdev_priv(dev); ++ ++ /* FIXME: locking */ ++ chan = priv->hw->phy->current_channel; ++ page = priv->hw->phy->current_page; ++ ++ if (chan == (u8)-1) /* not init */ ++ return NETDEV_TX_OK; ++ ++ BUG_ON(page >= WPAN_NUM_PAGES); ++ BUG_ON(chan >= 27); ++ ++ memcpy(skb_push(skb, sizeof(smac_header)), smac_header, sizeof(smac_header)); ++ ++ skb->skb_iif = dev->ifindex; ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ return mac802154_tx(priv->hw, skb, page, chan); ++} ++ ++void mac802154_smacs_rx(struct mac802154_priv *priv, struct sk_buff *skb) ++{ ++ struct mac802154_sub_if_data *sdata; ++ ++ if (skb->len < sizeof(smac_header)) ++ return; ++ ++ if (memcmp(skb->data, smac_header, sizeof(smac_header))) ++ return; ++ ++ /* ++ * Currently we are the owner of the skb. ++ * We can change it's data pointers, provided we change them ++ * back at the end. ++ */ ++ ++ skb_pull(skb, sizeof(smac_header)); ++ ++ list_for_each_entry_rcu(sdata, &priv->slaves, list) { ++ struct sk_buff *skb2; ++ ++ if (sdata->type != IEEE802154_DEV_SMAC) ++ continue; ++ ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ skb2->dev = sdata->dev; ++ skb2->pkt_type = PACKET_HOST; ++ ++ if (in_interrupt()) ++ netif_rx(skb2); ++ else ++ netif_rx_ni(skb2); ++ } ++ rcu_read_unlock(); ++ ++ skb_push(skb, sizeof(smac_header)); ++} ++ ++static const struct net_device_ops mac802154_smac_ops = { ++ .ndo_open = mac802154_slave_open, ++ .ndo_stop = mac802154_slave_close, ++ .ndo_start_xmit = mac802154_smac_xmit, ++}; ++ ++void mac802154_smac_setup(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ ++ dev->addr_len = 0; ++ dev->features = NETIF_F_HW_CSUM; ++ dev->hard_header_len = 2; ++ dev->needed_tailroom = 2; /* FCS */ ++ dev->mtu = 123; /* 127 - 2 (FCS) - 2 (header) */ ++ dev->tx_queue_len = 10; ++ dev->type = ARPHRD_SMAC; ++ dev->flags = IFF_NOARP | IFF_BROADCAST; ++ dev->watchdog_timeo = 0; ++ ++ dev->destructor = free_netdev; ++ dev->netdev_ops = &mac802154_smac_ops; ++ dev->ml_priv = &mac802154_mlme_simple; ++ ++ priv = netdev_priv(dev); ++ priv->type = IEEE802154_DEV_SMAC; ++ ++ priv->chan = -1; /* not initialized */ ++ priv->page = 0; /* for compat */ ++} ++ +diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c +new file mode 100644 +index 0000000..0703195 +--- /dev/null ++++ b/net/mac802154/tx.c +@@ -0,0 +1,106 @@ ++/* ++ * Copyright 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ * Sergey Lapin ++ * Maxim Gorbachyov ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mac802154.h" ++ ++struct xmit_work { ++ struct sk_buff *skb; ++ struct work_struct work; ++ struct mac802154_priv *priv; ++ u8 page; ++ u8 chan; ++}; ++ ++static void mac802154_xmit_worker(struct work_struct *work) ++{ ++ struct xmit_work *xw = container_of(work, struct xmit_work, work); ++ int res; ++ ++ BUG_ON(xw->chan == (u8)-1); ++ ++ mutex_lock(&xw->priv->phy->pib_lock); ++ if (xw->priv->phy->current_channel != xw->chan || ++ xw->priv->phy->current_page != xw->page) { ++ res = xw->priv->ops->set_channel(&xw->priv->hw, ++ xw->page, ++ xw->chan); ++ if (res) { ++ pr_debug("set_channel failed\n"); ++ goto out; ++ } ++ } ++ ++ res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb); ++ ++out: ++ mutex_unlock(&xw->priv->phy->pib_lock); ++ ++ /* FIXME: result processing and/or requeue!!! */ ++ dev_kfree_skb(xw->skb); ++ ++ kfree(xw); ++} ++ ++netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, ++ u8 page, u8 chan) ++{ ++ struct xmit_work *work; ++ ++ if (WARN_ON(!(priv->phy->channels_supported[page] & ++ (1 << chan)))) ++ return NETDEV_TX_OK; ++ ++ mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb); ++ ++ if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { ++ u16 crc = crc_ccitt(0, skb->data, skb->len); ++ u8 *data = skb_put(skb, 2); ++ data[0] = crc & 0xff; ++ data[1] = crc >> 8; ++ } ++ ++ if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) { ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } ++ ++ work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC); ++ if (!work) ++ return NETDEV_TX_BUSY; ++ ++ INIT_WORK(&work->work, mac802154_xmit_worker); ++ work->skb = skb; ++ work->priv = priv; ++ work->page = page; ++ work->chan = chan; ++ ++ queue_work(priv->dev_workqueue, &work->work); ++ ++ return NETDEV_TX_OK; ++} +diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c +new file mode 100644 +index 0000000..d5369a8 +--- /dev/null ++++ b/net/mac802154/wpan.c +@@ -0,0 +1,631 @@ ++/* ++ * Copyright 2007, 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Dmitry Eremin-Solenikov ++ * Sergey Lapin ++ * Maxim Gorbachyov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mac802154.h" ++#include "mib.h" ++ ++static netdev_tx_t mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ u8 chan, page; ++ ++ priv = netdev_priv(dev); ++ ++ spin_lock_bh(&priv->mib_lock); ++ chan = priv->chan; ++ page = priv->page; ++ spin_unlock_bh(&priv->mib_lock); ++ ++ if (chan == (u8)-1) /* not init */ ++ return NETDEV_TX_OK; ++ ++ BUG_ON(page >= WPAN_NUM_PAGES); ++ BUG_ON(chan >= 27); ++ ++ skb->skb_iif = dev->ifindex; ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ return mac802154_tx(priv->hw, skb, page, chan); ++} ++ ++static int mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, ++ int cmd) ++{ ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ struct sockaddr_ieee802154 *sa = ++ (struct sockaddr_ieee802154 *)&ifr->ifr_addr; ++ int err = -ENOIOCTLCMD; ++ ++ spin_lock_bh(&priv->mib_lock); ++ ++ switch (cmd) { ++ case SIOCGIFADDR: ++ if (priv->pan_id == IEEE802154_PANID_BROADCAST || ++ priv->short_addr == IEEE802154_ADDR_BROADCAST) { ++ err = -EADDRNOTAVAIL; ++ break; ++ } ++ ++ sa->family = AF_IEEE802154; ++ sa->addr.addr_type = IEEE802154_ADDR_SHORT; ++ sa->addr.pan_id = priv->pan_id; ++ sa->addr.short_addr = priv->short_addr; ++ ++ err = 0; ++ break; ++ case SIOCSIFADDR: ++ dev_warn(&dev->dev, ++ "Using DEBUGing ioctl SIOCSIFADDR isn't recommened!\n"); ++ if (sa->family != AF_IEEE802154 || ++ sa->addr.addr_type != IEEE802154_ADDR_SHORT || ++ sa->addr.pan_id == IEEE802154_PANID_BROADCAST || ++ sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || ++ sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { ++ err = -EINVAL; ++ break; ++ } ++ ++ priv->pan_id = sa->addr.pan_id; ++ priv->short_addr = sa->addr.short_addr; ++ err = 0; ++ break; ++ } ++ spin_unlock_bh(&priv->mib_lock); ++ return err; ++} ++ ++static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) ++{ ++ struct sockaddr *addr = p; ++ ++ if (netif_running(dev)) ++ return -EBUSY; ++ /* FIXME: validate addr */ ++ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); ++ mac802154_dev_set_ieee_addr(dev); ++ return 0; ++} ++ ++static void mac802154_haddr_copy_swap(u8 *dest, const u8 *src) ++{ ++ int i; ++ for (i = 0; i < IEEE802154_ADDR_LEN; i++) ++ dest[IEEE802154_ADDR_LEN - i - 1] = src[i]; ++} ++ ++static int mac802154_header_create(struct sk_buff *skb, ++ struct net_device *dev, ++ unsigned short type, const void *_daddr, ++ const void *_saddr, unsigned len) ++{ ++ u8 head[24] = {}; ++ int pos = 0; ++ ++ u16 fc; ++ const struct ieee802154_addr *saddr = _saddr; ++ const struct ieee802154_addr *daddr = _daddr; ++ struct ieee802154_addr dev_addr; ++ struct mac802154_sub_if_data *priv = netdev_priv(dev); ++ ++ fc = mac_cb_type(skb); ++ if (mac_cb_is_ackreq(skb)) ++ fc |= IEEE802154_FC_ACK_REQ; ++ ++ pos = 2; ++ ++ head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ ++ ++ if (!daddr) ++ return -EINVAL; ++ ++ if (!saddr) { ++ spin_lock_bh(&priv->mib_lock); ++ if (priv->short_addr == IEEE802154_ADDR_BROADCAST || ++ priv->short_addr == IEEE802154_ADDR_UNDEF || ++ priv->pan_id == IEEE802154_PANID_BROADCAST) { ++ dev_addr.addr_type = IEEE802154_ADDR_LONG; ++ memcpy(dev_addr.hwaddr, dev->dev_addr, ++ IEEE802154_ADDR_LEN); ++ } else { ++ dev_addr.addr_type = IEEE802154_ADDR_SHORT; ++ dev_addr.short_addr = priv->short_addr; ++ } ++ ++ dev_addr.pan_id = priv->pan_id; ++ saddr = &dev_addr; ++ ++ spin_unlock_bh(&priv->mib_lock); ++ } ++ ++ if (daddr->addr_type != IEEE802154_ADDR_NONE) { ++ fc |= (daddr->addr_type << IEEE802154_FC_DAMODE_SHIFT); ++ ++ head[pos++] = daddr->pan_id & 0xff; ++ head[pos++] = daddr->pan_id >> 8; ++ ++ if (daddr->addr_type == IEEE802154_ADDR_SHORT) { ++ head[pos++] = daddr->short_addr & 0xff; ++ head[pos++] = daddr->short_addr >> 8; ++ } else { ++ mac802154_haddr_copy_swap(head + pos, daddr->hwaddr); ++ pos += IEEE802154_ADDR_LEN; ++ } ++ } ++ ++ if (saddr->addr_type != IEEE802154_ADDR_NONE) { ++ fc |= (saddr->addr_type << IEEE802154_FC_SAMODE_SHIFT); ++ ++ if ((saddr->pan_id == daddr->pan_id) && ++ (saddr->pan_id != IEEE802154_PANID_BROADCAST)) ++ /* PANID compression/ intra PAN */ ++ fc |= IEEE802154_FC_INTRA_PAN; ++ else { ++ head[pos++] = saddr->pan_id & 0xff; ++ head[pos++] = saddr->pan_id >> 8; ++ } ++ ++ if (saddr->addr_type == IEEE802154_ADDR_SHORT) { ++ head[pos++] = saddr->short_addr & 0xff; ++ head[pos++] = saddr->short_addr >> 8; ++ } else { ++ mac802154_haddr_copy_swap(head + pos, saddr->hwaddr); ++ pos += IEEE802154_ADDR_LEN; ++ } ++ } ++ ++ head[0] = fc; ++ head[1] = fc >> 8; ++ ++ memcpy(skb_push(skb, pos), head, pos); ++ ++ return pos; ++} ++ ++static int mac802154_header_parse(const struct sk_buff *skb, ++ unsigned char *haddr) ++{ ++ const u8 *hdr = skb_mac_header(skb), *tail = skb_tail_pointer(skb); ++ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; ++ u16 fc; ++ int da_type; ++ ++ if (hdr + 3 > tail) ++ goto malformed; ++ ++ fc = hdr[0] | (hdr[1] << 8); ++ ++ hdr += 3; ++ ++ da_type = IEEE802154_FC_DAMODE(fc); ++ addr->addr_type = IEEE802154_FC_SAMODE(fc); ++ ++ switch (da_type) { ++ case IEEE802154_ADDR_NONE: ++ if (fc & IEEE802154_FC_INTRA_PAN) ++ goto malformed; ++ break; ++ ++ case IEEE802154_ADDR_LONG: ++ if (hdr + 2 > tail) ++ goto malformed; ++ if (fc & IEEE802154_FC_INTRA_PAN) { ++ addr->pan_id = hdr[0] | (hdr[1] << 8); ++ hdr += 2; ++ } ++ ++ if (hdr + IEEE802154_ADDR_LEN > tail) ++ goto malformed; ++ hdr += IEEE802154_ADDR_LEN; ++ break; ++ ++ case IEEE802154_ADDR_SHORT: ++ if (hdr + 2 > tail) ++ goto malformed; ++ if (fc & IEEE802154_FC_INTRA_PAN) { ++ addr->pan_id = hdr[0] | (hdr[1] << 8); ++ hdr += 2; ++ } ++ ++ if (hdr + 2 > tail) ++ goto malformed; ++ hdr += 2; ++ break; ++ ++ default: ++ goto malformed; ++ ++ } ++ ++ switch (addr->addr_type) { ++ case IEEE802154_ADDR_NONE: ++ break; ++ ++ case IEEE802154_ADDR_LONG: ++ if (hdr + 2 > tail) ++ goto malformed; ++ if (!(fc & IEEE802154_FC_INTRA_PAN)) { ++ addr->pan_id = hdr[0] | (hdr[1] << 8); ++ hdr += 2; ++ } ++ ++ if (hdr + IEEE802154_ADDR_LEN > tail) ++ goto malformed; ++ mac802154_haddr_copy_swap(addr->hwaddr, hdr); ++ hdr += IEEE802154_ADDR_LEN; ++ break; ++ ++ case IEEE802154_ADDR_SHORT: ++ if (hdr + 2 > tail) ++ goto malformed; ++ if (!(fc & IEEE802154_FC_INTRA_PAN)) { ++ addr->pan_id = hdr[0] | (hdr[1] << 8); ++ hdr += 2; ++ } ++ ++ if (hdr + 2 > tail) ++ goto malformed; ++ addr->short_addr = hdr[0] | (hdr[1] << 8); ++ hdr += 2; ++ break; ++ ++ default: ++ goto malformed; ++ ++ } ++ ++ return sizeof(struct ieee802154_addr); ++ ++malformed: ++ pr_debug("malformed packet\n"); ++ return 0; ++} ++ ++static struct header_ops mac802154_header_ops = { ++ .create = mac802154_header_create, ++ .parse = mac802154_header_parse, ++}; ++ ++static const struct net_device_ops mac802154_wpan_ops = { ++ .ndo_open = mac802154_slave_open, ++ .ndo_stop = mac802154_slave_close, ++ .ndo_start_xmit = mac802154_wpan_xmit, ++ .ndo_do_ioctl = mac802154_wpan_ioctl, ++ .ndo_set_mac_address = mac802154_wpan_mac_addr, ++}; ++ ++void mac802154_wpan_setup(struct net_device *dev) ++{ ++ struct mac802154_sub_if_data *priv; ++ ++ dev->addr_len = IEEE802154_ADDR_LEN; ++ memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); ++ dev->features = NETIF_F_HW_CSUM; ++ dev->hard_header_len = 2 + 1 + 20 + 14; ++ dev->header_ops = &mac802154_header_ops; ++ dev->needed_tailroom = 2; /* FCS */ ++ dev->mtu = 127; ++ dev->tx_queue_len = 10; ++ dev->type = ARPHRD_IEEE802154; ++ dev->flags = IFF_NOARP | IFF_BROADCAST; ++ dev->watchdog_timeo = 0; ++ ++ dev->destructor = free_netdev; ++ dev->netdev_ops = &mac802154_wpan_ops; ++ dev->ml_priv = &mac802154_mlme_wpan.wpan_ops; ++ ++ priv = netdev_priv(dev); ++ priv->type = IEEE802154_DEV_WPAN; ++ ++ priv->chan = -1; /* not initialized */ ++ priv->page = 0; /* for compat */ ++ ++ spin_lock_init(&priv->mib_lock); ++ ++ get_random_bytes(&priv->bsn, 1); ++ get_random_bytes(&priv->dsn, 1); ++ ++ priv->pan_id = IEEE802154_PANID_BROADCAST; ++ priv->short_addr = IEEE802154_ADDR_BROADCAST; ++} ++ ++static int mac802154_process_ack(struct net_device *dev, struct sk_buff *skb) ++{ ++ pr_debug("got ACK for SEQ=%d\n", mac_cb(skb)->seq); ++ ++ kfree_skb(skb); ++ return NET_RX_SUCCESS; ++} ++ ++static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) ++{ ++ if (in_interrupt()) ++ return netif_rx(skb); ++ else ++ return netif_rx_ni(skb); ++} ++ ++static int mac802154_subif_frame(struct mac802154_sub_if_data *sdata, ++ struct sk_buff *skb) ++{ ++ pr_debug("%s Getting packet via slave interface %s\n", ++ __func__, sdata->dev->name); ++ ++ spin_lock_bh(&sdata->mib_lock); ++ ++ switch (mac_cb(skb)->da.addr_type) { ++ case IEEE802154_ADDR_NONE: ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) ++ /* FIXME: check if we are PAN coordinator :) */ ++ skb->pkt_type = PACKET_OTHERHOST; ++ else ++ /* ACK comes with both addresses empty */ ++ skb->pkt_type = PACKET_HOST; ++ break; ++ case IEEE802154_ADDR_LONG: ++ if (mac_cb(skb)->da.pan_id != sdata->pan_id && ++ mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) ++ skb->pkt_type = PACKET_OTHERHOST; ++ else if (!memcmp(mac_cb(skb)->da.hwaddr, sdata->dev->dev_addr, ++ IEEE802154_ADDR_LEN)) ++ skb->pkt_type = PACKET_HOST; ++ else ++ skb->pkt_type = PACKET_OTHERHOST; ++ break; ++ case IEEE802154_ADDR_SHORT: ++ if (mac_cb(skb)->da.pan_id != sdata->pan_id && ++ mac_cb(skb)->da.pan_id != IEEE802154_PANID_BROADCAST) ++ skb->pkt_type = PACKET_OTHERHOST; ++ else if (mac_cb(skb)->da.short_addr == sdata->short_addr) ++ skb->pkt_type = PACKET_HOST; ++ else if (mac_cb(skb)->da.short_addr == ++ IEEE802154_ADDR_BROADCAST) ++ skb->pkt_type = PACKET_BROADCAST; ++ else ++ skb->pkt_type = PACKET_OTHERHOST; ++ break; ++ } ++ ++ spin_unlock_bh(&sdata->mib_lock); ++ ++ skb->dev = sdata->dev; ++ ++ if (skb->pkt_type == PACKET_HOST && mac_cb_is_ackreq(skb) && ++ !(sdata->hw->hw.flags & IEEE802154_HW_AACK)) ++ dev_warn(&sdata->dev->dev, ++ "ACK requested, however AACK not supported.\n"); ++ ++ switch (mac_cb_type(skb)) { ++ case IEEE802154_FC_TYPE_BEACON: ++ return mac802154_process_beacon(sdata->dev, skb); ++ case IEEE802154_FC_TYPE_ACK: ++ return mac802154_process_ack(sdata->dev, skb); ++ case IEEE802154_FC_TYPE_MAC_CMD: ++ return mac802154_process_cmd(sdata->dev, skb); ++ case IEEE802154_FC_TYPE_DATA: ++ return mac802154_process_data(sdata->dev, skb); ++ default: ++ pr_warning("ieee802154: Bad frame received (type = %d)\n", ++ mac_cb_type(skb)); ++ kfree_skb(skb); ++ return NET_RX_DROP; ++ } ++} ++ ++static u8 fetch_skb_u8(struct sk_buff *skb) ++{ ++ u8 ret; ++ ++ BUG_ON(skb->len < 1); ++ ++ ret = skb->data[0]; ++ skb_pull(skb, 1); ++ ++ return ret; ++} ++ ++static u16 fetch_skb_u16(struct sk_buff *skb) ++{ ++ u16 ret; ++ ++ BUG_ON(skb->len < 2); ++ ++ ret = skb->data[0] + (skb->data[1] * 256); ++ skb_pull(skb, 2); ++ return ret; ++} ++ ++static void fetch_skb_u64(struct sk_buff *skb, u8 *dest) ++{ ++ int i; ++ ++ BUG_ON(skb->len < IEEE802154_ADDR_LEN); ++ ++ for (i = 0; i < IEEE802154_ADDR_LEN; i++) ++ dest[IEEE802154_ADDR_LEN - i - 1] = skb->data[i]; ++ skb_pull(skb, IEEE802154_ADDR_LEN); ++} ++ ++#define IEEE802154_FETCH_U8(skb, var) \ ++ do { \ ++ if (skb->len < 1) \ ++ goto exit_error; \ ++ var = fetch_skb_u8(skb); \ ++ } while (0) ++ ++#define IEEE802154_FETCH_U16(skb, var) \ ++ do { \ ++ if (skb->len < 2) \ ++ goto exit_error; \ ++ var = fetch_skb_u16(skb); \ ++ } while (0) ++ ++#define IEEE802154_FETCH_U64(skb, var) \ ++ do { \ ++ if (skb->len < IEEE802154_ADDR_LEN) \ ++ goto exit_error; \ ++ fetch_skb_u64(skb, var); \ ++ } while (0) ++ ++static int parse_frame_start(struct sk_buff *skb) ++{ ++ u8 *head = skb->data; ++ u16 fc; ++ ++ if (skb->len < 3) { ++ pr_debug("frame size %d bytes is too short\n", skb->len); ++ return -EINVAL; ++ } ++ ++ IEEE802154_FETCH_U16(skb, fc); ++ IEEE802154_FETCH_U8(skb, mac_cb(skb)->seq); ++ ++ pr_debug("%s: %04x dsn%02x\n", __func__, fc, head[2]); ++ ++ mac_cb(skb)->flags = IEEE802154_FC_TYPE(fc); ++ ++ if (fc & IEEE802154_FC_ACK_REQ) { ++ pr_debug("%s(): ACKNOWLEDGE required\n", __func__); ++ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; ++ } ++ ++ if (fc & IEEE802154_FC_SECEN) ++ mac_cb(skb)->flags |= MAC_CB_FLAG_SECEN; ++ ++ if (fc & IEEE802154_FC_INTRA_PAN) ++ mac_cb(skb)->flags |= MAC_CB_FLAG_INTRAPAN; ++ ++ /* TODO */ ++ if (mac_cb_is_secen(skb)) { ++ pr_info("security support is not implemented\n"); ++ return -EINVAL; ++ } ++ ++ mac_cb(skb)->sa.addr_type = IEEE802154_FC_SAMODE(fc); ++ if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_NONE) ++ pr_debug("%s(): src addr_type is NONE\n", __func__); ++ ++ mac_cb(skb)->da.addr_type = IEEE802154_FC_DAMODE(fc); ++ if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_NONE) ++ pr_debug("%s(): dst addr_type is NONE\n", __func__); ++ ++ if (IEEE802154_FC_TYPE(fc) == IEEE802154_FC_TYPE_ACK) { ++ /* ACK can only have NONE-type addresses */ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE || ++ mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) ++ return -EINVAL; ++ } ++ ++ if (mac_cb(skb)->da.addr_type != IEEE802154_ADDR_NONE) { ++ IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.pan_id); ++ ++ if (mac_cb_is_intrapan(skb)) { /* ! panid compress */ ++ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n", ++ __func__); ++ mac_cb(skb)->sa.pan_id = mac_cb(skb)->da.pan_id; ++ pr_debug("%s(): src PAN address %04x\n", ++ __func__, mac_cb(skb)->sa.pan_id); ++ } ++ ++ pr_debug("%s(): dst PAN address %04x\n", ++ __func__, mac_cb(skb)->da.pan_id); ++ ++ if (mac_cb(skb)->da.addr_type == IEEE802154_ADDR_SHORT) { ++ IEEE802154_FETCH_U16(skb, mac_cb(skb)->da.short_addr); ++ pr_debug("%s(): dst SHORT address %04x\n", ++ __func__, mac_cb(skb)->da.short_addr); ++ ++ } else { ++ IEEE802154_FETCH_U64(skb, mac_cb(skb)->da.hwaddr); ++ pr_debug("%s(): dst hardware addr\n", __func__); ++ } ++ } ++ ++ if (mac_cb(skb)->sa.addr_type != IEEE802154_ADDR_NONE) { ++ pr_debug("%s(): got src non-NONE address\n", __func__); ++ if (!(mac_cb_is_intrapan(skb))) { /* ! panid compress */ ++ IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.pan_id); ++ pr_debug("%s(): src IEEE802154_FC_INTRA_PAN\n", ++ __func__); ++ } ++ ++ if (mac_cb(skb)->sa.addr_type == IEEE802154_ADDR_SHORT) { ++ IEEE802154_FETCH_U16(skb, mac_cb(skb)->sa.short_addr); ++ pr_debug("%s(): src IEEE802154_ADDR_SHORT\n", ++ __func__); ++ } else { ++ IEEE802154_FETCH_U64(skb, mac_cb(skb)->sa.hwaddr); ++ pr_debug("%s(): src hardware addr\n", __func__); ++ } ++ } ++ ++ return 0; ++ ++exit_error: ++ return -EINVAL; ++} ++ ++void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) ++{ ++ int ret; ++ struct mac802154_sub_if_data *sdata; ++ struct sk_buff *skb2; ++ ++ ret = parse_frame_start(skb); /* 3 bytes pulled after this */ ++ if (ret) { ++ pr_debug("%s(): Got invalid frame\n", __func__); ++ return; ++ } ++ ++ pr_debug("%s() frame %d\n", __func__, mac_cb_type(skb)); ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(sdata, &priv->slaves, list) ++ { ++ if (sdata->type != IEEE802154_DEV_WPAN) ++ continue; ++ ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (skb2) ++ mac802154_subif_frame(sdata, skb2); ++ } ++ ++ rcu_read_unlock(); ++} ++ +diff --git a/net/zigbee/Kconfig b/net/zigbee/Kconfig +new file mode 100644 +index 0000000..85134a9 +--- /dev/null ++++ b/net/zigbee/Kconfig +@@ -0,0 +1,7 @@ ++config ZIGBEE ++ tristate "ZigBee Low-Rate Wireless Personal Area Networks support (EXPERIMENTAL)" ++ depends on EXPERIMENTAL && BROKEN ++ ---help--- ++ ++ Say Y here to compile ZigBee support into the kernel or say M to ++ compile it as modules. +diff --git a/net/zigbee/Makefile b/net/zigbee/Makefile +new file mode 100644 +index 0000000..8c2eee5 +--- /dev/null ++++ b/net/zigbee/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_ZIGBEE) += af_zb.o ++ ++af_zb-objs := af_zigbee.o dgram.o ++ ++EXTRA_CFLAGS += -Wall -DEXPORT_SYMTAB -DCONFIG_FFD -DCONFIG_ZIGBEE_DEBUG -DIEEE80215_DEBUG -DDEBUG +diff --git a/net/zigbee/af_zigbee.c b/net/zigbee/af_zigbee.c +new file mode 100644 +index 0000000..820d668 +--- /dev/null ++++ b/net/zigbee/af_zigbee.c +@@ -0,0 +1,285 @@ ++/* ++ * ZigBee socket interface ++ * ++ * Copyright 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Maxim Yu. Osipov ++ */ ++#include ++#include ++#include ++#include ++#include ++#include /* For TIOCOUTQ/INQ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//#include ++#include ++#include ++ ++#define DBG_DUMP(data, len) { \ ++ int i; \ ++ pr_debug("file %s: function: %s: data: len %d:\n", __FILE__, __func__, len); \ ++ for (i = 0; i < len; i++) {\ ++ pr_debug("%02x: %02x\n", i, (data)[i]); \ ++ } \ ++} ++ ++static int zb_sock_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (sk) { ++ sock->sk = NULL; ++ sk->sk_prot->close(sk, 0); ++ } ++ return 0; ++} ++static int zb_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) ++{ ++ struct sock *sk = sock->sk; ++ ++ return sk->sk_prot->sendmsg(iocb, sk, msg, len); ++} ++ ++static int zb_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (sk->sk_prot->bind) ++ return sk->sk_prot->bind(sk, uaddr, addr_len); ++ ++ return sock_no_bind(sock, uaddr, addr_len); ++} ++ ++static int zb_sock_connect(struct socket *sock, struct sockaddr *uaddr, ++ int addr_len, int flags) ++{ ++ struct sock *sk = sock->sk; ++ ++ if (uaddr->sa_family == AF_UNSPEC) ++ return sk->sk_prot->disconnect(sk, flags); ++ ++ return sk->sk_prot->connect(sk, uaddr, addr_len); ++} ++ ++#if 0 ++static int zb_dev_ioctl(struct sock *sk, struct ifreq __user *arg, unsigned int cmd) ++{ ++ struct ifreq ifr; ++ int ret = -EINVAL; ++ struct net_device *dev; ++ ++ if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) ++ return -EFAULT; ++ ++ ifr.ifr_name[IFNAMSIZ-1] = 0; ++ ++ dev_load(sock_net(sk), ifr.ifr_name); ++ dev = dev_get_by_name(sock_net(sk), ifr.ifr_name); ++ if (dev->type == ARPHRD_ZIGBEE || dev->type == ARPHRD_ZIGBEE_PHY) ++ ret = dev->do_ioctl(dev, &ifr, cmd); ++ ++ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) ++ ret = -EFAULT; ++ dev_put(dev); ++ ++ return ret; ++} ++#endif ++ ++static int zb_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ struct sock *sk = sock->sk; ++ ++ switch (cmd) { ++ case SIOCGSTAMP: ++ return sock_get_timestamp(sk, (struct timeval __user *)arg); ++ case SIOCGSTAMPNS: ++ return sock_get_timestampns(sk, (struct timespec __user *)arg); ++#if 0 ++ case SIOCGIFADDR: ++ case SIOCSIFADDR: ++ return zb_dev_ioctl(sk, (struct ifreq __user *)arg, cmd); ++#endif ++ default: ++ if (!sk->sk_prot->ioctl) ++ return -ENOIOCTLCMD; ++ return sk->sk_prot->ioctl(sk, cmd, arg); ++ } ++} ++ ++static const struct proto_ops zb_dgram_ops = { ++ .family = PF_ZIGBEE, ++ .owner = THIS_MODULE, ++ .release = zb_sock_release, ++ .bind = zb_sock_bind, ++ .connect = zb_sock_connect, ++ .socketpair = sock_no_socketpair, ++ .accept = sock_no_accept, ++ .getname = sock_no_getname, ++ .poll = datagram_poll, ++ .ioctl = zb_sock_ioctl, ++ .listen = sock_no_listen, ++ .shutdown = sock_no_shutdown, ++ .setsockopt = sock_common_setsockopt, ++ .getsockopt = sock_common_getsockopt, ++ .sendmsg = zb_sock_sendmsg, ++ .recvmsg = sock_common_recvmsg, ++ .mmap = sock_no_mmap, ++ .sendpage = sock_no_sendpage, ++#ifdef CONFIG_COMPAT ++ .compat_setsockopt = compat_sock_common_setsockopt, ++ .compat_getsockopt = compat_sock_common_getsockopt, ++#endif ++}; ++ ++ ++/* ++ * Create a socket. Initialise the socket, blank the addresses ++ * set the state. ++ */ ++static int zb_create(struct net *net, struct socket *sock, int protocol) ++{ ++ struct sock *sk; ++ int rc; ++ struct proto *proto; ++ const struct proto_ops *ops; ++ ++ // FIXME: init_net ++ if (net != &init_net) ++ return -EAFNOSUPPORT; ++ ++ if (sock->type == SOCK_DGRAM) { ++ proto = &zb_dgram_prot; ++ ops = &zb_dgram_ops; ++ } ++ else { ++ rc = -ESOCKTNOSUPPORT; ++ goto out; ++ } ++ ++ rc = -ENOMEM; ++ sk = sk_alloc(net, PF_ZIGBEE, GFP_KERNEL, proto); ++ if (!sk) ++ goto out; ++ rc = 0; ++ ++ sock->ops = ops; ++ ++ sock_init_data(sock, sk); ++ // FIXME: sk->sk_destruct ++ sk->sk_family = PF_ZIGBEE; ++ ++#if 0 ++ /* Checksums on by default */ ++ // FIXME: ++ sock_set_flag(sk, SOCK_ZAPPED); ++ ++ // FIXME: ++ if (sk->sk_prot->hash) ++ sk->sk_prot->hash(sk); ++#endif ++ ++ if (sk->sk_prot->init) { ++ rc = sk->sk_prot->init(sk); ++ if (rc) ++ sk_common_release(sk); ++ } ++out: ++ return rc; ++} ++ ++static struct net_proto_family zb_family_ops = { ++ .family = PF_ZIGBEE, ++ .create = zb_create, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Main ZigBEE NWK receive routine. ++ */ ++static int zb_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) ++{ ++ struct nwkhdr *nwkh; ++ u32 len; ++ ++ DBG_DUMP(skb->data, skb->len); ++ pr_debug("got frame, type %d, dev %p\n", dev->type, dev); ++ // FIXME: init_net ++ if (!net_eq(dev_net(dev), &init_net)) ++ goto drop; ++ ++ zb_raw_deliver(dev, skb); ++ ++ if (skb->pkt_type != PACKET_OTHERHOST) ++ return zb_dgram_deliver(dev, skb); ++ ++drop: ++ kfree_skb(skb); ++ return NET_RX_DROP; ++} ++ ++ ++static struct packet_type zb_packet_type = { ++ .type = __constant_htons(ETH_P_ZIGBEE), ++ .func = zb_rcv, ++}; ++ ++static int __init af_zb_init(void) ++{ ++ int rc = -EINVAL; ++ ++ rc = proto_register(&zb_dgram_prot, 1); ++ if (rc) ++ goto err; ++ ++ /* Tell SOCKET that we are alive */ ++ rc = sock_register(&zb_family_ops); ++ ++ if (rc) ++ goto err; ++ ++ dev_add_pack(&zb_packet_type); ++ ++ rc = 0; ++ goto out; ++ ++err: ++ proto_unregister(&zb_dgram_prot); ++out: ++ return rc; ++} ++ ++static void af_zb_remove(void) ++{ ++ dev_remove_pack(&zb_packet_type); ++ sock_unregister(PF_ZIGBEE); ++ proto_unregister(&zb_dgram_prot); ++} ++ ++module_init(af_zb_init); ++module_exit(af_zb_remove); ++ ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS_NETPROTO(PF_ZIGBEE); +diff --git a/net/zigbee/dgram.c b/net/zigbee/dgram.c +new file mode 100644 +index 0000000..4d9b682 +--- /dev/null ++++ b/net/zigbee/dgram.c +@@ -0,0 +1,401 @@ ++/* ++ * ZigBee socket interface ++ * ++ * Copyright 2008, 2009 Siemens AG ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Written by: ++ * Sergey Lapin ++ * Dmitry Eremin-Solenikov ++ * Maxim Yu. Osipov ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++//#include ++//#include ++//#include ++#include ++#include ++ ++static HLIST_HEAD(dgram_head); ++static DEFINE_RWLOCK(dgram_lock); ++ ++struct dgram_sock { ++ struct sock sk; ++ ++ int bound; ++ struct ieee80215_addr src_addr; ++ struct ieee80215_addr dst_addr; ++}; ++ ++static void dgram_hash(struct sock *sk) ++{ ++ write_lock_bh(&dgram_lock); ++ sk_add_node(sk, &dgram_head); ++ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); ++ write_unlock_bh(&dgram_lock); ++} ++ ++static void dgram_unhash(struct sock *sk) ++{ ++ write_lock_bh(&dgram_lock); ++ if (sk_del_node_init(sk)) ++ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); ++ write_unlock_bh(&dgram_lock); ++} ++ ++static int dgram_init(struct sock *sk) ++{ ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ ++ ro->dst_addr.addr_type = IEEE80215_ADDR_SHORT; ++// ro->dst_addr.pan_id = 0xffff; ++ ro->dst_addr.short_addr = 0xffff; ++ return 0; ++} ++ ++static void dgram_close(struct sock *sk, long timeout) ++{ ++ sk_common_release(sk); ++} ++ ++static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len) ++{ ++ struct sockaddr_ieee80215 *addr = (struct sockaddr_ieee80215 *)uaddr; ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ int err = 0; ++ ++ if (ro->bound) ++ return -EINVAL; ++ ++ if (len < sizeof(*addr)) ++ return -EINVAL; ++ ++ if ((addr->family != AF_ZIGBEE) || ++ (addr->addr.addr_type != IEEE80215_ADDR_SHORT)) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ /* ++ * FIXME: should check here that address is not already in use ++ * Problem that this address is not independent - this is the ++ * address of the lower layer ++ */ ++#if 0 ++ dev = ieee80215_get_dev(sock_net(sk), &addr->addr); ++ if (!dev) { ++ err = -ENODEV; ++ goto out; ++ } ++ ++ if (dev->type != ARPHRD_IEEE80215) { ++ err = -ENODEV; ++ goto out_put; ++ } ++#endif ++ memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee80215_addr)); ++ ++ ro->bound = 1; ++#if 0 ++out_put: ++ dev_put(dev); ++out: ++#endif ++ release_sock(sk); ++ ++ return err; ++} ++ ++static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case SIOCOUTQ: ++ { ++ int amount = atomic_read(&sk->sk_wmem_alloc); ++ return put_user(amount, (int __user *)arg); ++ } ++ ++ case SIOCINQ: ++ { ++ struct sk_buff *skb; ++ unsigned long amount = 0; ++ ++ spin_lock_bh(&sk->sk_receive_queue.lock); ++ skb = skb_peek(&sk->sk_receive_queue); ++ if (skb != NULL) { ++ /* ++ * We will only return the amount ++ * of this packet since that is all ++ * that will be read. ++ */ ++ amount = skb->len - sizeof(struct nwkhdr); ++ } ++ spin_unlock_bh(&sk->sk_receive_queue.lock); ++ return put_user(amount, (int __user *)arg); ++ ++ } ++#if 0 ++ /* May be implement here the commands */ ++ case IEEE80215_SIOC_NETWORK_DISCOVERY: ++ return ioctl_network_discovery(sk, (struct ieee80215_user_data __user *) arg); ++ break; ++ case IEEE80215_SIOC_NETWORK_FORMATION: ++ return ioctl_network_formation(sk, (struct ieee80215_user_data __user *) arg); ++ break; ++ case IEEE80215_SIOC_PERMIT_JOINING: ++ return ioctl_permit_joining(sk, (struct ieee80215_user_data __user *) arg); ++ break; ++ case IEEE80215_SIOC_START_ROUTER: ++ return ioctl_start_router(sk, (struct ieee80215_user_data __user *) arg); ++ break; ++ case IEEE80215_SIOC_JOIN: ++ return ioctl_mac_join(sk, (struct ieee80215_user_data __user *) arg); ++ break; ++ case IEEE80215_SIOC_MAC_CMD: ++ return ioctl_mac_cmd(sk, (struct ieee80215_user_data __user *) arg); ++ ++ break; ++#endif ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++// FIXME: autobind ++static int dgram_connect(struct sock *sk, struct sockaddr *uaddr, ++ int len) ++{ ++ struct sockaddr_ieee80215 *addr = (struct sockaddr_ieee80215 *)uaddr; ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ ++ int err = 0; ++ ++ if (len < sizeof(*addr)) ++ return -EINVAL; ++ ++ if ((addr->family != AF_ZIGBEE) || ++ (addr->addr.addr_type != IEEE80215_ADDR_SHORT)) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ ++ if (!ro->bound) { ++ err = -ENETUNREACH; ++ goto out; ++ } ++ ++ memcpy(&ro->dst_addr, &addr->addr, sizeof(struct ieee80215_addr)); ++ ++out: ++ release_sock(sk); ++ return err; ++} ++ ++static int dgram_disconnect(struct sock *sk, int flags) ++{ ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ ++ lock_sock(sk); ++ ++ ro->dst_addr.addr_type = IEEE80215_ADDR_SHORT; ++// ro->dst_addr.pan_id = 0xffff; ++ ro->dst_addr.short_addr = 0xffff; ++ ++ release_sock(sk); ++ ++ return 0; ++} ++ ++static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ++ size_t size) ++{ ++ struct net_device *dev; ++ unsigned mtu; ++ struct sk_buff *skb; ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ ++ int err; ++ struct ieee80215_priv *hw; ++ ++ if (msg->msg_flags & MSG_OOB) { ++ pr_debug("msg->msg_flags = 0x%x\n", msg->msg_flags); ++ return -EOPNOTSUPP; ++ } ++ ++ if (!ro->bound) ++ dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE80215); ++ else ++ dev = ieee80215_get_dev(sock_net(sk), &ro->src_addr); ++ ++ if (!dev) { ++ pr_debug("no dev\n"); ++ return -ENXIO; ++ } ++ hw = ieee80215_slave_get_hw(dev); ++ mtu = dev->mtu; ++ pr_debug("name = %s, mtu = %u\n", dev->name, mtu); ++ ++ skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + size, msg->msg_flags & MSG_DONTWAIT, ++ &err); ++ if (!skb) { ++ dev_put(dev); ++ return err; ++ } ++ skb_reserve(skb, LL_RESERVED_SPACE(dev)); ++ ++ skb_reset_network_header(skb); ++ ++ MAC_CB(skb)->flags = IEEE80215_FC_TYPE_DATA | MAC_CB_FLAG_ACKREQ; ++ MAC_CB(skb)->seq = hw->dsn; ++ err = dev_hard_header(skb, dev, ETH_P_IEEE80215, &ro->dst_addr, ro->bound ? &ro->src_addr : NULL, size); ++ if (err < 0) { ++ kfree_skb(skb); ++ dev_put(dev); ++ return err; ++ } ++ ++ skb_reset_mac_header(skb); ++ ++ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); ++ if (err < 0) { ++ kfree_skb(skb); ++ dev_put(dev); ++ return err; ++ } ++ ++ if (size > mtu) { ++ pr_debug("size = %u, mtu = %u\n", size, mtu); ++ return -EINVAL; ++ } ++ ++ skb->dev = dev; ++ skb->sk = sk; ++ skb->protocol = htons(ETH_P_IEEE80215); ++ ++ err = dev_queue_xmit(skb); ++ hw->dsn++; ++ ++ dev_put(dev); ++ ++ if (err) ++ return err; ++ ++ return size; ++} ++ ++static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ++ size_t len, int noblock, int flags, int *addr_len) ++{ ++ size_t copied = 0; ++ int err = -EOPNOTSUPP; ++ struct sk_buff *skb; ++ ++ skb = skb_recv_datagram(sk, flags, noblock, &err); ++ if (!skb) ++ goto out; ++ ++ copied = skb->len; ++ if (len < copied) { ++ msg->msg_flags |= MSG_TRUNC; ++ copied = len; ++ } ++ ++ // FIXME: skip headers if necessary ?! ++ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); ++ if (err) ++ goto done; ++ ++ sock_recv_timestamp(msg, sk, skb); ++ ++ if (flags & MSG_TRUNC) ++ copied = skb->len; ++done: ++ skb_free_datagram(sk, skb); ++out: ++ if (err) ++ return err; ++ return copied; ++} ++ ++static int dgram_rcv_skb(struct sock *sk, struct sk_buff *skb) ++{ ++ if (sock_queue_rcv_skb(sk, skb) < 0) { ++ atomic_inc(&sk->sk_drops); ++ kfree_skb(skb); ++ return NET_RX_DROP; ++ } ++ ++ return NET_RX_SUCCESS; ++} ++ ++int ieee80215_dgram_deliver(struct net_device *dev, struct sk_buff *skb) ++{ ++ struct sock *sk, *prev = NULL; ++ struct hlist_node*node; ++ int ret = NET_RX_SUCCESS; ++ ++ /* Data frame processing */ ++ ++ read_lock(&dgram_lock); ++ sk_for_each(sk, node, &dgram_head) { ++ struct dgram_sock *ro = container_of(sk, struct dgram_sock, sk); ++ if (!ro->bound || ++ (ro->src_addr.addr_type == IEEE80215_ADDR_LONG && ++ !memcmp(ro->src_addr.hwaddr, dev->dev_addr, IEEE80215_ADDR_LEN)) || ++ (ro->src_addr.addr_type == IEEE80215_ADDR_SHORT && ++ ieee80215_dev_get_pan_id(dev) == ro->src_addr.pan_id && ++ ieee80215_dev_get_short_addr(dev) == ro->src_addr.short_addr)) { ++ if (prev) { ++ struct sk_buff *clone; ++ clone = skb_clone(skb, GFP_ATOMIC); ++ if (clone) ++ dgram_rcv_skb(prev, clone); ++ } ++ ++ prev = sk; ++ } ++ } ++ ++ if (prev) ++ dgram_rcv_skb(prev, skb); ++ else { ++ kfree_skb(skb); ++ ret = NET_RX_DROP; ++ } ++ read_unlock(&dgram_lock); ++ ++ return ret; ++} ++ ++struct proto ieee80215_dgram_prot = { ++ .name = "ZigBEE", ++ .owner = THIS_MODULE, ++ .obj_size = sizeof(struct dgram_sock), ++ .init = dgram_init, ++ .close = dgram_close, ++ .bind = dgram_bind, ++ .sendmsg = dgram_sendmsg, ++ .recvmsg = dgram_recvmsg, ++ .hash = dgram_hash, ++ .unhash = dgram_unhash, ++ .connect = dgram_connect, ++ .disconnect = dgram_disconnect, ++ .ioctl = dgram_ioctl, ++}; ++ +-- +1.7.5.4 + diff --git a/target/linux/xburst/patches-3.3/800-WPAN-patches.patch b/target/linux/xburst/patches-3.3/800-WPAN-patches.patch new file mode 100644 index 000000000..f75d92a02 --- /dev/null +++ b/target/linux/xburst/patches-3.3/800-WPAN-patches.patch @@ -0,0 +1,473 @@ +From ca7f714fc8c9981eda5d85594333f955f75510f7 Mon Sep 17 00:00:00 2001 +From: Xiangfu +Date: Tue, 5 Jun 2012 11:32:43 +0800 +Subject: [PATCH 1/3] 002 + +--- + drivers/ieee802154/Kconfig | 55 +++++++++++++++++++++++++++++++++++++- + drivers/ieee802154/Makefile | 11 ++++++++ + drivers/ieee802154/fakehard.c | 12 ++++++++- + include/linux/if.h | 2 + + include/linux/if_arp.h | 2 + + include/linux/nl802154.h | 10 +++++- + include/net/ieee802154_netdev.h | 17 ++++++++++-- + include/net/wpan-phy.h | 8 +++-- + net/Kconfig | 2 + + net/Makefile | 1 + + net/ieee802154/Kconfig | 8 +++++ + net/ieee802154/Makefile | 3 ++ + net/ieee802154/dgram.c | 3 +- + net/ieee802154/ieee802154.h | 4 +++ + net/ieee802154/nl-mac.c | 2 +- + net/ieee802154/nl-phy.c | 20 ++++++++++++-- + net/ieee802154/nl_policy.c | 1 + + net/ieee802154/wpan-class.c | 2 +- + 18 files changed, 145 insertions(+), 18 deletions(-) + +diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig +index 9b9f43a..5345e40 100644 +--- a/drivers/ieee802154/Kconfig ++++ b/drivers/ieee802154/Kconfig +@@ -10,13 +10,64 @@ menuconfig IEEE802154_DRIVERS + If you say N, all options in this submenu will be skipped and + disabled. + ++config IEEE802154_DRIVER_DEBUG ++ bool "Driver debugging messages" ++ depends on IEEE802154_DRIVERS ++ default y ++ help ++ Say Y here to make the IEEE 802.15.4 drivers generate extensive ++ debugging messages. ++ + config IEEE802154_FAKEHARD + tristate "Fake LR-WPAN driver with several interconnected devices" + depends on IEEE802154_DRIVERS + ---help--- + Say Y here to enable the fake driver that serves as an example +- of HardMAC device driver. ++ of HardMAC device driver. + +- This driver can also be built as a module. To do so say M here. ++ This driver can also be built as a module. To do so say M here. + The module will be called 'fakehard'. + ++ ++config IEEE802154_FAKELB ++ depends on IEEE802154_DRIVERS && MAC802154 ++ tristate "Fake LR-WPAN driver with several interconnected devices" ++ ---help--- ++ Say Y here to enable the fake driver that can emulate a net ++ of several interconnected radio devices. ++ ++ This driver can also be built as a module. To do so say M here. ++ The module will be called 'fakelb'. ++ ++config IEEE802154_SERIAL ++ depends on IEEE802154_DRIVERS && MAC802154 ++ tristate "Simple LR-WPAN UART driver" ++ ++config IEEE802154_AT86RF230 ++ depends on IEEE802154_DRIVERS && MAC802154 ++ tristate "AT86RF230 transceiver driver" ++ depends on SPI ++ ++config SPI_ATBEN ++ tristate "ATBEN 8:10 SPI interface" ++ depends on JZ4740_QI_LB60 && IEEE802154_AT86RF230 ++ help ++ Bit-banging SPI driver for the 8:10 interface of the Ben NanoNote ++ when equipped with an ATBEN board. ++ ++config SPI_ATUSB ++ tristate "ATUSB SPI interface" ++ depends on USB && IEEE802154_AT86RF230 ++ help ++ SPI-over-USB driver for the ATUSB IEEE 802.15.4 board. ++ ++config IEEE802154_CC2420 ++ tristate "CC2420 driver" ++ depends on SPI ++ depends on IEEE802154_DRIVERS ++ ++config IEEE802154_ADF7242 ++ tristate "ADF7242 transceiver driver" ++ depends on IEEE802154_DRIVERS && MAC802154 ++ depends on SPI ++ +diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile +index 800a389..4f669bc 100644 +--- a/drivers/ieee802154/Makefile ++++ b/drivers/ieee802154/Makefile +@@ -1 +1,12 @@ + obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o ++obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o ++obj-$(CONFIG_IEEE802154_SERIAL) += serial.o ++obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o ++obj-$(CONFIG_IEEE802154_CC2420) += cc2420.o ++obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o ++obj-$(CONFIG_SPI_ATBEN) += spi_atben.o ++obj-$(CONFIG_SPI_ATUSB) += spi_atusb.o ++ ++ccflags-y := -DDEBUG -DCONFIG_FFD ++ccflags-$(CONFIG_IEEE802154_DRIVER_DEBUG) += -DDEBUG ++ccflags-y += -DCONFIG_FFD +diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c +index 73d4531..996e1db 100644 +--- a/drivers/ieee802154/fakehard.c ++++ b/drivers/ieee802154/fakehard.c +@@ -259,7 +259,7 @@ static struct ieee802154_mlme_ops fake_mlme = { + .start_req = fake_start_req, + .scan_req = fake_scan_req, + +- .get_phy = fake_get_phy, ++ .wpan_ops.get_phy = fake_get_phy, + + .get_pan_id = fake_get_pan_id, + .get_short_addr = fake_get_short_addr, +@@ -391,6 +391,16 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev) + priv = netdev_priv(dev); + priv->phy = phy; + ++ /* ++ * If the name is a format string the caller wants us to do a ++ * name allocation. ++ */ ++ if (strchr(dev->name, '%')) { ++ err = dev_alloc_name(dev, dev->name); ++ if (err < 0) ++ goto out; ++ } ++ + wpan_phy_set_dev(phy, &pdev->dev); + SET_NETDEV_DEV(dev, &phy->dev); + +diff --git a/include/linux/if.h b/include/linux/if.h +index cffa754..9f402a1 100644 +--- a/include/linux/if.h ++++ b/include/linux/if.h +@@ -82,6 +82,8 @@ + #define IFF_TEAM_PORT 0x40000 /* device used as team port */ + #define IFF_NO_IP_ALIGN 0x80000 /* do not ip-align allocated rx pkts */ + ++#define IFF_IEEE802154_COORD 0x400 /* IEEE802.15.4 PAN coordinator */ ++ + #define IF_GET_IFACE 0x0001 /* for querying only */ + #define IF_GET_PROTO 0x0002 + +diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h +index 6d722f4..47a57d8 100644 +--- a/include/linux/if_arp.h ++++ b/include/linux/if_arp.h +@@ -87,6 +87,8 @@ + #define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ + #define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ + #define ARPHRD_IEEE802154 804 ++#define ARPHRD_IEEE802154_MONITOR 805 ++#define ARPHRD_SMAC 806 /* Freescale Simple MAC */ + + #define ARPHRD_PHONET 820 /* PhoNet media type */ + #define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ +diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h +index 33d9f51..a379fc7 100644 +--- a/include/linux/nl802154.h ++++ b/include/linux/nl802154.h +@@ -68,14 +68,13 @@ enum { + IEEE802154_ATTR_CHANNEL_PAGE_LIST, + + IEEE802154_ATTR_PHY_NAME, ++ IEEE802154_ATTR_DEV_TYPE, + + __IEEE802154_ATTR_MAX, + }; + + #define IEEE802154_ATTR_MAX (__IEEE802154_ATTR_MAX - 1) + +-extern const struct nla_policy ieee802154_policy[]; +- + /* commands */ + /* REQ should be responded with CONF + * and INDIC with RESP +@@ -126,4 +125,11 @@ enum { + + #define IEEE802154_CMD_MAX (__IEEE802154_CMD_MAX - 1) + ++enum { ++ IEEE802154_DEV_WPAN, ++ IEEE802154_DEV_MONITOR, ++ IEEE802154_DEV_SMAC, ++ __IEEE802154_DEV_MAX, ++}; ++ + #endif +diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h +index 5743055..e12ce9a 100644 +--- a/include/net/ieee802154_netdev.h ++++ b/include/net/ieee802154_netdev.h +@@ -26,6 +26,8 @@ + #ifndef IEEE802154_NETDEVICE_H + #define IEEE802154_NETDEVICE_H + ++#include ++ + /* + * A control block of skb passed between the ARPHRD_IEEE802154 device + * and other stack parts. +@@ -81,7 +83,12 @@ struct wpan_phy; + * get_phy should increment the reference counting on returned phy. + * Use wpan_wpy_put to put that reference. + */ ++struct simple_mlme_ops { ++ struct wpan_phy *(*get_phy)(const struct net_device *dev); ++}; + struct ieee802154_mlme_ops { ++ struct simple_mlme_ops wpan_ops; ++ + int (*assoc_req)(struct net_device *dev, + struct ieee802154_addr *addr, + u8 channel, u8 page, u8 cap); +@@ -98,8 +105,6 @@ struct ieee802154_mlme_ops { + int (*scan_req)(struct net_device *dev, + u8 type, u32 channels, u8 page, u8 duration); + +- struct wpan_phy *(*get_phy)(const struct net_device *dev); +- + /* + * FIXME: these should become the part of PIB/MIB interface. + * However we still don't have IB interface of any kind +@@ -110,12 +115,18 @@ struct ieee802154_mlme_ops { + u8 (*get_bsn)(const struct net_device *dev); + }; + +-static inline struct ieee802154_mlme_ops *ieee802154_mlme_ops( ++static inline struct simple_mlme_ops *simple_mlme_ops( + const struct net_device *dev) + { + return dev->ml_priv; + } + ++static inline struct ieee802154_mlme_ops *ieee802154_mlme_ops( ++ const struct net_device *dev) ++{ ++ return container_of(dev->ml_priv, struct ieee802154_mlme_ops, wpan_ops); ++} ++ + #endif + + +diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h +index d86fffd..9e119c5 100644 +--- a/include/net/wpan-phy.h ++++ b/include/net/wpan-phy.h +@@ -24,17 +24,19 @@ + #include + #include + ++#define WPAN_NUM_PAGES 32 ++ + struct wpan_phy { + struct mutex pib_lock; + + /* +- * This is a PIB according to 802.15.4-2006. ++ * This is a PIB acording to 802.15.4-2006. + * We do not provide timing-related variables, as they + * aren't used outside of driver + */ + u8 current_channel; + u8 current_page; +- u32 channels_supported[32]; ++ u32 channels_supported[WPAN_NUM_PAGES]; + u8 transmit_power; + u8 cca_mode; + +@@ -42,7 +44,7 @@ struct wpan_phy { + int idx; + + struct net_device *(*add_iface)(struct wpan_phy *phy, +- const char *name); ++ const char *name, int type); + void (*del_iface)(struct wpan_phy *phy, struct net_device *dev); + + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); +diff --git a/net/Kconfig b/net/Kconfig +index b3904e8..578eb11 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -217,6 +217,8 @@ source "net/econet/Kconfig" + source "net/wanrouter/Kconfig" + source "net/phonet/Kconfig" + source "net/ieee802154/Kconfig" ++source "net/mac802154/Kconfig" ++source "net/zigbee/Kconfig" + source "net/sched/Kconfig" + source "net/dcb/Kconfig" + source "net/dns_resolver/Kconfig" +diff --git a/net/Makefile b/net/Makefile +index ad432fa..2a97cde 100644 +--- a/net/Makefile ++++ b/net/Makefile +@@ -60,6 +60,7 @@ ifneq ($(CONFIG_DCB),) + obj-y += dcb/ + endif + obj-$(CONFIG_IEEE802154) += ieee802154/ ++obj-$(CONFIG_MAC802154) += mac802154/ + + ifeq ($(CONFIG_NET),y) + obj-$(CONFIG_SYSCTL) += sysctl_net.o +diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig +index 7dee650..3dea3c1 100644 +--- a/net/ieee802154/Kconfig ++++ b/net/ieee802154/Kconfig +@@ -16,3 +16,11 @@ config IEEE802154_6LOWPAN + depends on IEEE802154 && IPV6 + ---help--- + IPv6 compression over IEEE 802.15.4. ++ ++config IEEE802154_PROTO_DEBUG ++ bool "IEEE 802.15.4 protocol stack debugging messages" ++ depends on IEEE802154 ++ default y ++ help ++ Say Y here to make the IEEE 802.15.4 protocol stack generate ++ extensive debugging messages. +diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile +index d7716d6..e2b6735 100644 +--- a/net/ieee802154/Makefile ++++ b/net/ieee802154/Makefile +@@ -3,3 +3,6 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o + + ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o + af_802154-y := af_ieee802154.o raw.o dgram.o ++ ++ccflags-$(CONFIG_IEEE802154_PROTO_DEBUG) += -DDEBUG ++ccflags-y += -Wall +diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c +index 1b09eaa..7883fa6 100644 +--- a/net/ieee802154/dgram.c ++++ b/net/ieee802154/dgram.c +@@ -130,8 +130,7 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg) + switch (cmd) { + case SIOCOUTQ: + { +- int amount = sk_wmem_alloc_get(sk); +- ++ int amount = atomic_read(&sk->sk_wmem_alloc); + return put_user(amount, (int __user *)arg); + } + +diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h +index aadec42..e78d6c6 100644 +--- a/net/ieee802154/ieee802154.h ++++ b/net/ieee802154/ieee802154.h +@@ -21,6 +21,10 @@ + int __init ieee802154_nl_init(void); + void __exit ieee802154_nl_exit(void); + ++#include ++ ++extern const struct nla_policy ieee802154_policy[]; ++ + #define IEEE802154_OP(_cmd, _func) \ + { \ + .cmd = _cmd, \ +diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c +index adaf462..1d23aa6 100644 +--- a/net/ieee802154/nl-mac.c ++++ b/net/ieee802154/nl-mac.c +@@ -263,7 +263,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, + if (!hdr) + goto out; + +- phy = ieee802154_mlme_ops(dev)->get_phy(dev); ++ phy = simple_mlme_ops(dev)->get_phy(dev); + BUG_ON(!phy); + + NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); +diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c +index c64a38d..76ef7a3 100644 +--- a/net/ieee802154/nl-phy.c ++++ b/net/ieee802154/nl-phy.c +@@ -57,7 +57,7 @@ static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 pid, + + NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, phy->current_page); + NLA_PUT_U8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel); +- for (i = 0; i < 32; i++) { ++ for (i = 0; i < WPAN_NUM_PAGES; i++) { + if (phy->channels_supported[i]) + buf[pages++] = phy->channels_supported[i] | (i << 27); + } +@@ -179,6 +179,7 @@ static int ieee802154_add_iface(struct sk_buff *skb, + const char *devname; + int rc = -ENOBUFS; + struct net_device *dev; ++ int type = IEEE802154_DEV_WPAN; + + pr_debug("%s\n", __func__); + +@@ -201,6 +202,19 @@ static int ieee802154_add_iface(struct sk_buff *skb, + if (strlen(devname) >= IFNAMSIZ) + return -ENAMETOOLONG; + ++ if (info->attrs[IEEE802154_ATTR_HW_ADDR] && ++ nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) != ++ IEEE802154_ADDR_LEN) { ++ return -EINVAL; ++ } ++ ++ if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) { ++ type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]); ++ if (type > __IEEE802154_DEV_MAX) { ++ return -EINVAL; ++ } ++ } ++ + phy = wpan_phy_find(name); + if (!phy) + return -ENODEV; +@@ -221,7 +235,7 @@ static int ieee802154_add_iface(struct sk_buff *skb, + goto nla_put_failure; + } + +- dev = phy->add_iface(phy, devname); ++ dev = phy->add_iface(phy, devname, type); + if (IS_ERR(dev)) { + rc = PTR_ERR(dev); + goto nla_put_failure; +@@ -288,7 +302,7 @@ static int ieee802154_del_iface(struct sk_buff *skb, + if (!dev) + return -ENODEV; + +- phy = ieee802154_mlme_ops(dev)->get_phy(dev); ++ phy = simple_mlme_ops(dev)->get_phy(dev); + BUG_ON(!phy); + + rc = -EINVAL; +diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c +index 6adda4d..235cd65 100644 +--- a/net/ieee802154/nl_policy.c ++++ b/net/ieee802154/nl_policy.c +@@ -28,6 +28,7 @@ const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = { + [IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, }, + [IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, }, + [IEEE802154_ATTR_PHY_NAME] = { .type = NLA_STRING, }, ++ [IEEE802154_ATTR_DEV_TYPE] = { .type = NLA_U8, }, + + [IEEE802154_ATTR_STATUS] = { .type = NLA_U8, }, + [IEEE802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, }, +diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c +index 1627ef2..380fe1a 100644 +--- a/net/ieee802154/wpan-class.c ++++ b/net/ieee802154/wpan-class.c +@@ -56,7 +56,7 @@ static ssize_t channels_supported_show(struct device *dev, + int i, len = 0; + + mutex_lock(&phy->pib_lock); +- for (i = 0; i < 32; i++) { ++ for (i = 0; i < WPAN_NUM_PAGES; i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "%#09x\n", phy->channels_supported[i]); + if (ret < 0) +-- +1.7.5.4 +