@ -21,6 +21,8 @@
# include <linux/skbuff.h>
# include <linux/of_gpio.h>
# include <linux/ieee802154.h>
# include <linux/crc-ccitt.h>
# include <asm/unaligned.h>
# include <net/mac802154.h>
# include <net/cfg802154.h>
@ -189,6 +191,18 @@
# define CC2520_RXFIFOCNT 0x3E
# define CC2520_TXFIFOCNT 0x3F
/* CC2520_FRMFILT0 */
# define FRMFILT0_FRAME_FILTER_EN BIT(0)
# define FRMFILT0_PAN_COORDINATOR BIT(1)
/* CC2520_FRMCTRL0 */
# define FRMCTRL0_AUTOACK BIT(5)
# define FRMCTRL0_AUTOCRC BIT(6)
/* CC2520_FRMCTRL1 */
# define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0)
# define FRMCTRL1_IGNORE_TX_UNDERF BIT(1)
/* Driver private information */
struct cc2520_private {
struct spi_device * spi ; /* SPI device structure */
@ -201,6 +215,7 @@ struct cc2520_private {
struct work_struct fifop_irqwork ; /* Workqueue for FIFOP */
spinlock_t lock ; /* Lock for is_tx*/
struct completion tx_complete ; /* Work completion for Tx */
bool promiscuous ; /* Flag for promiscuous mode */
} ;
/* Generic Functions */
@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
}
static int
cc2520_write_txfifo ( struct cc2520_private * priv , u8 * data , u8 len )
cc2520_write_txfifo ( struct cc2520_private * priv , u8 pkt_len , u8 * data , u8 len )
{
int status ;
/* length byte must include FCS even
* if it is calculated in the hardware
*/
int len_byte = len + 2 ;
int len_byte = pkt_ len;
struct spi_message msg ;
@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
}
static int
cc2520_read_rxfifo ( struct cc2520_private * priv , u8 * data , u8 len , u8 * lqi )
cc2520_read_rxfifo ( struct cc2520_private * priv , u8 * data , u8 len )
{
int status ;
struct spi_message msg ;
@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
unsigned long flags ;
int rc ;
u8 status = 0 ;
u8 pkt_len ;
/* In promiscuous mode we disable AUTOCRC so we can get the raw CRC
* values on RX . This means we need to manually add the CRC on TX .
*/
if ( priv - > promiscuous ) {
u16 crc = crc_ccitt ( 0 , skb - > data , skb - > len ) ;
put_unaligned_le16 ( crc , skb_put ( skb , 2 ) ) ;
pkt_len = skb - > len ;
} else {
pkt_len = skb - > len + 2 ;
}
rc = cc2520_cmd_strobe ( priv , CC2520_CMD_SFLUSHTX ) ;
if ( rc )
goto err_tx ;
rc = cc2520_write_txfifo ( priv , skb - > data , skb - > len ) ;
rc = cc2520_write_txfifo ( priv , pkt_len , skb - > data , skb - > len ) ;
if ( rc )
goto err_tx ;
@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv)
u8 len = 0 , lqi = 0 , bytes = 1 ;
struct sk_buff * skb ;
cc2520_read_rxfifo ( priv , & len , bytes , & lqi ) ;
/* Read single length byte from the radio. */
cc2520_read_rxfifo ( priv , & len , bytes ) ;
if ( len < 2 | | len > IEEE802154_MTU )
return - EINVAL ;
if ( ! ieee802154_is_valid_psdu_len ( len ) ) {
/* Corrupted frame received, clear frame buffer by
* reading entire buffer .
*/
dev_dbg ( & priv - > spi - > dev , " corrupted frame received \n " ) ;
len = IEEE802154_MTU ;
}
skb = dev_alloc_skb ( len ) ;
if ( ! skb )
return - ENOMEM ;
if ( cc2520_read_rxfifo ( priv , skb_put ( skb , len ) , len , & lqi ) ) {
if ( cc2520_read_rxfifo ( priv , skb_put ( skb , len ) , len ) ) {
dev_dbg ( & priv - > spi - > dev , " frame reception failed \n " ) ;
kfree_skb ( skb ) ;
return - EINVAL ;
}
skb_trim ( skb , skb - > len - 2 ) ;
/* In promiscuous mode, we configure the radio to include the
* CRC ( AUTOCRC = = 0 ) and we pass on the packet unconditionally . If not
* in promiscuous mode , we check the CRC here , but leave the
* RSSI / LQI / CRC_OK bytes as they will get removed in the mac layer .
*/
if ( ! priv - > promiscuous ) {
bool crc_ok ;
/* Check if the CRC is valid. With AUTOCRC set, the most
* significant bit of the last byte returned from the CC2520
* is CRC_OK flag . See section 20.3 .4 of the datasheet .
*/
crc_ok = skb - > data [ len - 1 ] & BIT ( 7 ) ;
/* If we failed CRC drop the packet in the driver layer. */
if ( ! crc_ok ) {
dev_dbg ( & priv - > spi - > dev , " CRC check failed \n " ) ;
kfree_skb ( skb ) ;
return - EINVAL ;
}
/* To calculate LQI, the lower 7 bits of the last byte (the
* correlation value provided by the radio ) must be scaled to
* the range 0 - 255. According to section 20.6 , the correlation
* value ranges from 50 - 110. Ideally this would be calibrated
* per hardware design , but we use roughly the datasheet values
* to get close enough while avoiding floating point .
*/
lqi = skb - > data [ len - 1 ] & 0x7f ;
if ( lqi < 50 )
lqi = 50 ;
else if ( lqi > 113 )
lqi = 113 ;
lqi = ( lqi - 50 ) * 4 ;
}
ieee802154_rx_irqsafe ( priv - > hw , skb , lqi ) ;
@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw,
}
if ( changed & IEEE802154_AFILT_PANC_CHANGED ) {
u8 frmfilt0 ;
dev_vdbg ( & priv - > spi - > dev ,
" cc2520_filter called for panc change \n " ) ;
cc2520_read_register ( priv , CC2520_FRMFILT0 , & frmfilt0 ) ;
if ( filt - > pan_coord )
ret = cc2520_write_register ( priv , CC2520_FRMFILT0 ,
0x02 ) ;
frmfilt0 | = FRMFILT0_PAN_COORDINATOR ;
else
ret = cc2520_write_register ( priv , CC2520_FRMFILT0 ,
0x00 ) ;
frmfilt0 & = ~ FRMFILT0_PAN_COORDINATOR ;
ret = cc2520_write_register ( priv , CC2520_FRMFILT0 , frmfilt0 ) ;
}
return ret ;
@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
return cc2520_cc2591_set_tx_power ( priv , mbm ) ;
}
static int
cc2520_set_promiscuous_mode ( struct ieee802154_hw * hw , bool on )
{
struct cc2520_private * priv = hw - > priv ;
u8 frmfilt0 ;
dev_dbg ( & priv - > spi - > dev , " %s : mode %d \n " , __func__ , on ) ;
priv - > promiscuous = on ;
cc2520_read_register ( priv , CC2520_FRMFILT0 , & frmfilt0 ) ;
if ( on ) {
/* Disable automatic ACK, automatic CRC, and frame filtering. */
cc2520_write_register ( priv , CC2520_FRMCTRL0 , 0 ) ;
frmfilt0 & = ~ FRMFILT0_FRAME_FILTER_EN ;
} else {
cc2520_write_register ( priv , CC2520_FRMCTRL0 , FRMCTRL0_AUTOACK |
FRMCTRL0_AUTOCRC ) ;
frmfilt0 | = FRMFILT0_FRAME_FILTER_EN ;
}
return cc2520_write_register ( priv , CC2520_FRMFILT0 , frmfilt0 ) ;
}
static const struct ieee802154_ops cc2520_ops = {
. owner = THIS_MODULE ,
. start = cc2520_start ,
@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = {
. set_channel = cc2520_set_channel ,
. set_hw_addr_filt = cc2520_filter ,
. set_txpower = cc2520_set_txpower ,
. set_promiscuous_mode = cc2520_set_promiscuous_mode ,
} ;
static int cc2520_register ( struct cc2520_private * priv )
@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv)
/* We do support only 2.4 Ghz */
priv - > hw - > phy - > supported . channels [ 0 ] = 0x7FFF800 ;
priv - > hw - > flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT ;
priv - > hw - > flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
IEEE802154_HW_PROMISCUOUS ;
priv - > hw - > phy - > flags = WPAN_PHY_FLAG_TXPOWER ;
@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv)
}
/* Registers default value: section 28.1 in Datasheet */
/* Set the CCA threshold to -50 dBm. This seems to have been copied
* from the TinyOS CC2520 driver and is much higher than the - 84 dBm
* threshold suggested in the datasheet .
*/
ret = cc2520_write_register ( priv , CC2520_CCACTRL0 , 0x1A ) ;
if ( ret )
goto err_ret ;
@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
if ( ret )
goto err_ret ;
ret = cc2520_write_register ( priv , CC2520_FRMCTRL0 , 0x60 ) ;
if ( ret )
goto err_ret ;
ret = cc2520_write_register ( priv , CC2520_FRMCTRL1 , 0x03 ) ;
if ( ret )
goto err_ret ;
ret = cc2520_write_register ( priv , CC2520_FRMFILT0 , 0x00 ) ;
/* Configure registers correctly for this driver. */
ret = cc2520_write_register ( priv , CC2520_FRMCTRL1 ,
FRMCTRL1_SET_RXENMASK_ON_TX |
FRMCTRL1_IGNORE_TX_UNDERF ) ;
if ( ret )
goto err_ret ;