1ecfacacfSValentin Vidic // SPDX-License-Identifier: GPL-2.0+ 2874bcba6SMarcus Wolf /* 3874bcba6SMarcus Wolf * userspace interface for pi433 radio module 4874bcba6SMarcus Wolf * 5874bcba6SMarcus Wolf * Pi433 is a 433MHz radio module for the Raspberry Pi. 6874bcba6SMarcus Wolf * It is based on the HopeRf Module RFM69CW. Therefore inside of this 7874bcba6SMarcus Wolf * driver, you'll find an abstraction of the rf69 chip. 8874bcba6SMarcus Wolf * 9874bcba6SMarcus Wolf * If needed, this driver could be extended, to also support other 10874bcba6SMarcus Wolf * devices, basing on HopeRfs rf69. 11874bcba6SMarcus Wolf * 12874bcba6SMarcus Wolf * The driver can also be extended, to support other modules of 13874bcba6SMarcus Wolf * HopeRf with a similar interace - e. g. RFM69HCW, RFM12, RFM95, ... 14874bcba6SMarcus Wolf * 15874bcba6SMarcus Wolf * Copyright (C) 2016 Wolf-Entwicklungen 16874bcba6SMarcus Wolf * Marcus Wolf <linux@wolf-entwicklungen.de> 17874bcba6SMarcus Wolf */ 18874bcba6SMarcus Wolf 19874bcba6SMarcus Wolf #undef DEBUG 20874bcba6SMarcus Wolf 21874bcba6SMarcus Wolf #include <linux/init.h> 22874bcba6SMarcus Wolf #include <linux/module.h> 23874bcba6SMarcus Wolf #include <linux/idr.h> 24874bcba6SMarcus Wolf #include <linux/ioctl.h> 25874bcba6SMarcus Wolf #include <linux/uaccess.h> 26874bcba6SMarcus Wolf #include <linux/fs.h> 27874bcba6SMarcus Wolf #include <linux/device.h> 28874bcba6SMarcus Wolf #include <linux/cdev.h> 29874bcba6SMarcus Wolf #include <linux/err.h> 30874bcba6SMarcus Wolf #include <linux/kfifo.h> 31874bcba6SMarcus Wolf #include <linux/errno.h> 32874bcba6SMarcus Wolf #include <linux/mutex.h> 33874bcba6SMarcus Wolf #include <linux/of.h> 34874bcba6SMarcus Wolf #include <linux/of_device.h> 35874bcba6SMarcus Wolf #include <linux/interrupt.h> 36874bcba6SMarcus Wolf #include <linux/irq.h> 37874bcba6SMarcus Wolf #include <linux/gpio/consumer.h> 38874bcba6SMarcus Wolf #include <linux/kthread.h> 39874bcba6SMarcus Wolf #include <linux/wait.h> 40874bcba6SMarcus Wolf #include <linux/spi/spi.h> 41874bcba6SMarcus Wolf #ifdef CONFIG_COMPAT 427b4c570eSDerek Robson #include <linux/compat.h> 43874bcba6SMarcus Wolf #endif 44874bcba6SMarcus Wolf 45874bcba6SMarcus Wolf #include "pi433_if.h" 46874bcba6SMarcus Wolf #include "rf69.h" 47874bcba6SMarcus Wolf 489ab7bc50SValentin Vidic #define N_PI433_MINORS BIT(MINORBITS) /*32*/ /* ... up to 256 */ 49874bcba6SMarcus Wolf #define MAX_MSG_SIZE 900 /* min: FIFO_SIZE! */ 50874bcba6SMarcus Wolf #define MSG_FIFO_SIZE 65536 /* 65536 = 2^16 */ 51874bcba6SMarcus Wolf #define NUM_DIO 2 52874bcba6SMarcus Wolf 53874bcba6SMarcus Wolf static dev_t pi433_dev; 54874bcba6SMarcus Wolf static DEFINE_IDR(pi433_idr); 55874bcba6SMarcus Wolf static DEFINE_MUTEX(minor_lock); /* Protect idr accesses */ 56874bcba6SMarcus Wolf 57874bcba6SMarcus Wolf static struct class *pi433_class; /* mainly for udev to create /dev/pi433 */ 58874bcba6SMarcus Wolf 5936892816SSophie Matter /* 6036892816SSophie Matter * tx config is instance specific 61056eeda2SDerek Robson * so with each open a new tx config struct is needed 62056eeda2SDerek Robson */ 6336892816SSophie Matter /* 6436892816SSophie Matter * rx config is device specific 65056eeda2SDerek Robson * so we have just one rx config, ebedded in device struct 66056eeda2SDerek Robson */ 67874bcba6SMarcus Wolf struct pi433_device { 68874bcba6SMarcus Wolf /* device handling related values */ 69874bcba6SMarcus Wolf dev_t devt; 70874bcba6SMarcus Wolf int minor; 71874bcba6SMarcus Wolf struct device *dev; 72874bcba6SMarcus Wolf struct cdev *cdev; 73874bcba6SMarcus Wolf struct spi_device *spi; 74874bcba6SMarcus Wolf 75874bcba6SMarcus Wolf /* irq related values */ 76874bcba6SMarcus Wolf struct gpio_desc *gpiod[NUM_DIO]; 77874bcba6SMarcus Wolf int irq_num[NUM_DIO]; 78874bcba6SMarcus Wolf u8 irq_state[NUM_DIO]; 79874bcba6SMarcus Wolf 80874bcba6SMarcus Wolf /* tx related values */ 81874bcba6SMarcus Wolf STRUCT_KFIFO_REC_1(MSG_FIFO_SIZE) tx_fifo; 825451dab9SValentin Vidic struct mutex tx_fifo_lock; /* serialize userspace writers */ 83874bcba6SMarcus Wolf struct task_struct *tx_task_struct; 84874bcba6SMarcus Wolf wait_queue_head_t tx_wait_queue; 85874bcba6SMarcus Wolf u8 free_in_fifo; 8662f39d49SArnd Bergmann char buffer[MAX_MSG_SIZE]; 87874bcba6SMarcus Wolf 88874bcba6SMarcus Wolf /* rx related values */ 89874bcba6SMarcus Wolf struct pi433_rx_cfg rx_cfg; 90874bcba6SMarcus Wolf u8 *rx_buffer; 91874bcba6SMarcus Wolf unsigned int rx_buffer_size; 92874bcba6SMarcus Wolf u32 rx_bytes_to_drop; 93874bcba6SMarcus Wolf u32 rx_bytes_dropped; 94874bcba6SMarcus Wolf unsigned int rx_position; 956b2ad163SPaulo Miguel Almeida struct mutex rx_lock; /* protects rx_* variable accesses */ 96874bcba6SMarcus Wolf wait_queue_head_t rx_wait_queue; 97874bcba6SMarcus Wolf 98874bcba6SMarcus Wolf /* fifo wait queue */ 99874bcba6SMarcus Wolf struct task_struct *fifo_task_struct; 100874bcba6SMarcus Wolf wait_queue_head_t fifo_wait_queue; 101874bcba6SMarcus Wolf 102874bcba6SMarcus Wolf /* flags */ 103874bcba6SMarcus Wolf bool rx_active; 104874bcba6SMarcus Wolf bool tx_active; 105874bcba6SMarcus Wolf bool interrupt_rx_allowed; 106874bcba6SMarcus Wolf }; 107874bcba6SMarcus Wolf 108874bcba6SMarcus Wolf struct pi433_instance { 109874bcba6SMarcus Wolf struct pi433_device *device; 110874bcba6SMarcus Wolf struct pi433_tx_cfg tx_cfg; 111874bcba6SMarcus Wolf }; 112874bcba6SMarcus Wolf 113874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 114874bcba6SMarcus Wolf 115874bcba6SMarcus Wolf /* GPIO interrupt handlers */ 116ee26e236SCihangir Akturk static irqreturn_t DIO0_irq_handler(int irq, void *dev_id) 117874bcba6SMarcus Wolf { 118874bcba6SMarcus Wolf struct pi433_device *device = dev_id; 119874bcba6SMarcus Wolf 12091086b82SValentin Vidic if (device->irq_state[DIO0] == DIO_PACKET_SENT) { 121874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 1225a60d7baSHaneen Mohammed dev_dbg(device->dev, "DIO0 irq: Packet sent\n"); 123874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 12491086b82SValentin Vidic } else if (device->irq_state[DIO0] == DIO_RSSI_DIO0) { 1255a60d7baSHaneen Mohammed dev_dbg(device->dev, "DIO0 irq: RSSI level over threshold\n"); 126874bcba6SMarcus Wolf wake_up_interruptible(&device->rx_wait_queue); 12791086b82SValentin Vidic } else if (device->irq_state[DIO0] == DIO_PAYLOAD_READY) { 1283d7f3bf2SSimon Sandström dev_dbg(device->dev, "DIO0 irq: Payload ready\n"); 129874bcba6SMarcus Wolf device->free_in_fifo = 0; 130874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 131874bcba6SMarcus Wolf } 132874bcba6SMarcus Wolf 133ee26e236SCihangir Akturk return IRQ_HANDLED; 134874bcba6SMarcus Wolf } 135874bcba6SMarcus Wolf 136ee26e236SCihangir Akturk static irqreturn_t DIO1_irq_handler(int irq, void *dev_id) 137874bcba6SMarcus Wolf { 138874bcba6SMarcus Wolf struct pi433_device *device = dev_id; 139874bcba6SMarcus Wolf 14091086b82SValentin Vidic if (device->irq_state[DIO1] == DIO_FIFO_NOT_EMPTY_DIO1) { 141874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 14291086b82SValentin Vidic } else if (device->irq_state[DIO1] == DIO_FIFO_LEVEL) { 1436b167a67SValentin Vidic if (device->rx_active) 1446b167a67SValentin Vidic device->free_in_fifo = FIFO_THRESHOLD - 1; 1456b167a67SValentin Vidic else 1466b167a67SValentin Vidic device->free_in_fifo = FIFO_SIZE - FIFO_THRESHOLD - 1; 147874bcba6SMarcus Wolf } 1485a60d7baSHaneen Mohammed dev_dbg(device->dev, 1495a60d7baSHaneen Mohammed "DIO1 irq: %d bytes free in fifo\n", device->free_in_fifo); 150874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 151874bcba6SMarcus Wolf 152ee26e236SCihangir Akturk return IRQ_HANDLED; 153874bcba6SMarcus Wolf } 154874bcba6SMarcus Wolf 155874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 156874bcba6SMarcus Wolf 157874bcba6SMarcus Wolf static int 158874bcba6SMarcus Wolf rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg) 159874bcba6SMarcus Wolf { 160125a452cSElia Geretto int ret; 161874bcba6SMarcus Wolf int payload_length; 162874bcba6SMarcus Wolf 163874bcba6SMarcus Wolf /* receiver config */ 164038a5b4eSNguyen Phan Quang Minh ret = rf69_set_frequency(dev->spi, rx_cfg->frequency); 165038a5b4eSNguyen Phan Quang Minh if (ret < 0) 166038a5b4eSNguyen Phan Quang Minh return ret; 167038a5b4eSNguyen Phan Quang Minh ret = rf69_set_bit_rate(dev->spi, rx_cfg->bit_rate); 168038a5b4eSNguyen Phan Quang Minh if (ret < 0) 169038a5b4eSNguyen Phan Quang Minh return ret; 170038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation(dev->spi, rx_cfg->modulation); 171038a5b4eSNguyen Phan Quang Minh if (ret < 0) 172038a5b4eSNguyen Phan Quang Minh return ret; 173038a5b4eSNguyen Phan Quang Minh ret = rf69_set_antenna_impedance(dev->spi, rx_cfg->antenna_impedance); 174038a5b4eSNguyen Phan Quang Minh if (ret < 0) 175038a5b4eSNguyen Phan Quang Minh return ret; 176038a5b4eSNguyen Phan Quang Minh ret = rf69_set_rssi_threshold(dev->spi, rx_cfg->rssi_threshold); 177038a5b4eSNguyen Phan Quang Minh if (ret < 0) 178038a5b4eSNguyen Phan Quang Minh return ret; 179038a5b4eSNguyen Phan Quang Minh ret = rf69_set_ook_threshold_dec(dev->spi, rx_cfg->threshold_decrement); 180038a5b4eSNguyen Phan Quang Minh if (ret < 0) 181038a5b4eSNguyen Phan Quang Minh return ret; 18220e5f042SEisha Chen-yen-su ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse, 18320e5f042SEisha Chen-yen-su rx_cfg->bw_exponent); 184038a5b4eSNguyen Phan Quang Minh if (ret < 0) 185038a5b4eSNguyen Phan Quang Minh return ret; 18620e5f042SEisha Chen-yen-su ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse, 18720e5f042SEisha Chen-yen-su rx_cfg->bw_exponent); 188038a5b4eSNguyen Phan Quang Minh if (ret < 0) 189038a5b4eSNguyen Phan Quang Minh return ret; 190038a5b4eSNguyen Phan Quang Minh ret = rf69_set_dagc(dev->spi, rx_cfg->dagc); 191038a5b4eSNguyen Phan Quang Minh if (ret < 0) 192038a5b4eSNguyen Phan Quang Minh return ret; 193874bcba6SMarcus Wolf 194874bcba6SMarcus Wolf dev->rx_bytes_to_drop = rx_cfg->bytes_to_drop; 195874bcba6SMarcus Wolf 196874bcba6SMarcus Wolf /* packet config */ 197874bcba6SMarcus Wolf /* enable */ 19891086b82SValentin Vidic if (rx_cfg->enable_sync == OPTION_ON) { 199966debe0SSimon Sandström ret = rf69_enable_sync(dev->spi); 200966debe0SSimon Sandström if (ret < 0) 201966debe0SSimon Sandström return ret; 202966debe0SSimon Sandström 20320e5f042SEisha Chen-yen-su ret = rf69_set_fifo_fill_condition(dev->spi, 20453e0b83dSValentin Vidic after_sync_interrupt); 205038a5b4eSNguyen Phan Quang Minh if (ret < 0) 206038a5b4eSNguyen Phan Quang Minh return ret; 20791086b82SValentin Vidic } else { 208966debe0SSimon Sandström ret = rf69_disable_sync(dev->spi); 209966debe0SSimon Sandström if (ret < 0) 210966debe0SSimon Sandström return ret; 211966debe0SSimon Sandström 212038a5b4eSNguyen Phan Quang Minh ret = rf69_set_fifo_fill_condition(dev->spi, always); 213038a5b4eSNguyen Phan Quang Minh if (ret < 0) 214038a5b4eSNguyen Phan Quang Minh return ret; 215874bcba6SMarcus Wolf } 216d423c809SSimon Sandström if (rx_cfg->enable_length_byte == OPTION_ON) { 217c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_var); 218125a452cSElia Geretto if (ret < 0) 219125a452cSElia Geretto return ret; 220125a452cSElia Geretto } else { 221c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_fix); 222125a452cSElia Geretto if (ret < 0) 223125a452cSElia Geretto return ret; 224125a452cSElia Geretto } 225e69a0500SValentin Vidic ret = rf69_set_address_filtering(dev->spi, 22620e5f042SEisha Chen-yen-su rx_cfg->enable_address_filtering); 227038a5b4eSNguyen Phan Quang Minh if (ret < 0) 228038a5b4eSNguyen Phan Quang Minh return ret; 22939252a4bSSimon Sandström 23039252a4bSSimon Sandström if (rx_cfg->enable_crc == OPTION_ON) { 23139252a4bSSimon Sandström ret = rf69_enable_crc(dev->spi); 23239252a4bSSimon Sandström if (ret < 0) 23339252a4bSSimon Sandström return ret; 23439252a4bSSimon Sandström } else { 23539252a4bSSimon Sandström ret = rf69_disable_crc(dev->spi); 23639252a4bSSimon Sandström if (ret < 0) 23739252a4bSSimon Sandström return ret; 23839252a4bSSimon Sandström } 239874bcba6SMarcus Wolf 240874bcba6SMarcus Wolf /* lengths */ 241038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_size(dev->spi, rx_cfg->sync_length); 242038a5b4eSNguyen Phan Quang Minh if (ret < 0) 243038a5b4eSNguyen Phan Quang Minh return ret; 24491086b82SValentin Vidic if (rx_cfg->enable_length_byte == OPTION_ON) { 245038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, 0xff); 246038a5b4eSNguyen Phan Quang Minh if (ret < 0) 247038a5b4eSNguyen Phan Quang Minh return ret; 24891086b82SValentin Vidic } else if (rx_cfg->fixed_message_length != 0) { 249874bcba6SMarcus Wolf payload_length = rx_cfg->fixed_message_length; 2506b167a67SValentin Vidic if (rx_cfg->enable_length_byte == OPTION_ON) 2516b167a67SValentin Vidic payload_length++; 252cd9d5291SValentin Vidic if (rx_cfg->enable_address_filtering != filtering_off) 2536b167a67SValentin Vidic payload_length++; 254038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, payload_length); 255038a5b4eSNguyen Phan Quang Minh if (ret < 0) 256038a5b4eSNguyen Phan Quang Minh return ret; 25791086b82SValentin Vidic } else { 258038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, 0); 259038a5b4eSNguyen Phan Quang Minh if (ret < 0) 260038a5b4eSNguyen Phan Quang Minh return ret; 261874bcba6SMarcus Wolf } 262874bcba6SMarcus Wolf 263874bcba6SMarcus Wolf /* values */ 26491086b82SValentin Vidic if (rx_cfg->enable_sync == OPTION_ON) { 265038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_values(dev->spi, rx_cfg->sync_pattern); 266038a5b4eSNguyen Phan Quang Minh if (ret < 0) 267038a5b4eSNguyen Phan Quang Minh return ret; 268874bcba6SMarcus Wolf } 269cd9d5291SValentin Vidic if (rx_cfg->enable_address_filtering != filtering_off) { 270038a5b4eSNguyen Phan Quang Minh ret = rf69_set_node_address(dev->spi, rx_cfg->node_address); 271038a5b4eSNguyen Phan Quang Minh if (ret < 0) 272038a5b4eSNguyen Phan Quang Minh return ret; 27320e5f042SEisha Chen-yen-su ret = rf69_set_broadcast_address(dev->spi, 27420e5f042SEisha Chen-yen-su rx_cfg->broadcast_address); 275038a5b4eSNguyen Phan Quang Minh if (ret < 0) 276038a5b4eSNguyen Phan Quang Minh return ret; 277874bcba6SMarcus Wolf } 278874bcba6SMarcus Wolf 279874bcba6SMarcus Wolf return 0; 280874bcba6SMarcus Wolf } 281874bcba6SMarcus Wolf 282874bcba6SMarcus Wolf static int 283874bcba6SMarcus Wolf rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg) 284874bcba6SMarcus Wolf { 285125a452cSElia Geretto int ret; 286125a452cSElia Geretto 287038a5b4eSNguyen Phan Quang Minh ret = rf69_set_frequency(dev->spi, tx_cfg->frequency); 288038a5b4eSNguyen Phan Quang Minh if (ret < 0) 289038a5b4eSNguyen Phan Quang Minh return ret; 290038a5b4eSNguyen Phan Quang Minh ret = rf69_set_bit_rate(dev->spi, tx_cfg->bit_rate); 291038a5b4eSNguyen Phan Quang Minh if (ret < 0) 292038a5b4eSNguyen Phan Quang Minh return ret; 293038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation(dev->spi, tx_cfg->modulation); 294038a5b4eSNguyen Phan Quang Minh if (ret < 0) 295038a5b4eSNguyen Phan Quang Minh return ret; 296038a5b4eSNguyen Phan Quang Minh ret = rf69_set_deviation(dev->spi, tx_cfg->dev_frequency); 297038a5b4eSNguyen Phan Quang Minh if (ret < 0) 298038a5b4eSNguyen Phan Quang Minh return ret; 299038a5b4eSNguyen Phan Quang Minh ret = rf69_set_pa_ramp(dev->spi, tx_cfg->pa_ramp); 300038a5b4eSNguyen Phan Quang Minh if (ret < 0) 301038a5b4eSNguyen Phan Quang Minh return ret; 302038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation_shaping(dev->spi, tx_cfg->mod_shaping); 303038a5b4eSNguyen Phan Quang Minh if (ret < 0) 304038a5b4eSNguyen Phan Quang Minh return ret; 305038a5b4eSNguyen Phan Quang Minh ret = rf69_set_tx_start_condition(dev->spi, tx_cfg->tx_start_condition); 306038a5b4eSNguyen Phan Quang Minh if (ret < 0) 307038a5b4eSNguyen Phan Quang Minh return ret; 308874bcba6SMarcus Wolf 309874bcba6SMarcus Wolf /* packet format enable */ 31091086b82SValentin Vidic if (tx_cfg->enable_preamble == OPTION_ON) { 31120e5f042SEisha Chen-yen-su ret = rf69_set_preamble_length(dev->spi, 31220e5f042SEisha Chen-yen-su tx_cfg->preamble_length); 313038a5b4eSNguyen Phan Quang Minh if (ret < 0) 314038a5b4eSNguyen Phan Quang Minh return ret; 31591086b82SValentin Vidic } else { 316038a5b4eSNguyen Phan Quang Minh ret = rf69_set_preamble_length(dev->spi, 0); 317038a5b4eSNguyen Phan Quang Minh if (ret < 0) 318038a5b4eSNguyen Phan Quang Minh return ret; 319874bcba6SMarcus Wolf } 320966debe0SSimon Sandström 321966debe0SSimon Sandström if (tx_cfg->enable_sync == OPTION_ON) { 32281a6e1ccSSidong Yang ret = rf69_set_sync_size(dev->spi, tx_cfg->sync_length); 32381a6e1ccSSidong Yang if (ret < 0) 32481a6e1ccSSidong Yang return ret; 32581a6e1ccSSidong Yang ret = rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern); 32681a6e1ccSSidong Yang if (ret < 0) 32781a6e1ccSSidong Yang return ret; 328966debe0SSimon Sandström ret = rf69_enable_sync(dev->spi); 329966debe0SSimon Sandström if (ret < 0) 330966debe0SSimon Sandström return ret; 331966debe0SSimon Sandström } else { 332966debe0SSimon Sandström ret = rf69_disable_sync(dev->spi); 333966debe0SSimon Sandström if (ret < 0) 334966debe0SSimon Sandström return ret; 335966debe0SSimon Sandström } 336966debe0SSimon Sandström 337d423c809SSimon Sandström if (tx_cfg->enable_length_byte == OPTION_ON) { 338c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_var); 339125a452cSElia Geretto if (ret < 0) 340125a452cSElia Geretto return ret; 341125a452cSElia Geretto } else { 342c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_fix); 343125a452cSElia Geretto if (ret < 0) 344125a452cSElia Geretto return ret; 345125a452cSElia Geretto } 34639252a4bSSimon Sandström 34739252a4bSSimon Sandström if (tx_cfg->enable_crc == OPTION_ON) { 34839252a4bSSimon Sandström ret = rf69_enable_crc(dev->spi); 34939252a4bSSimon Sandström if (ret < 0) 35039252a4bSSimon Sandström return ret; 35139252a4bSSimon Sandström } else { 35239252a4bSSimon Sandström ret = rf69_disable_crc(dev->spi); 35339252a4bSSimon Sandström if (ret < 0) 35439252a4bSSimon Sandström return ret; 35539252a4bSSimon Sandström } 356874bcba6SMarcus Wolf 357874bcba6SMarcus Wolf return 0; 358874bcba6SMarcus Wolf } 359874bcba6SMarcus Wolf 360874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 361874bcba6SMarcus Wolf 362874bcba6SMarcus Wolf static int 363874bcba6SMarcus Wolf pi433_start_rx(struct pi433_device *dev) 364874bcba6SMarcus Wolf { 365874bcba6SMarcus Wolf int retval; 366874bcba6SMarcus Wolf 367874bcba6SMarcus Wolf /* return without action, if no pending read request */ 368874bcba6SMarcus Wolf if (!dev->rx_active) 369874bcba6SMarcus Wolf return 0; 370874bcba6SMarcus Wolf 371874bcba6SMarcus Wolf /* setup for receiving */ 372874bcba6SMarcus Wolf retval = rf69_set_rx_cfg(dev, &dev->rx_cfg); 3736b167a67SValentin Vidic if (retval) 3746b167a67SValentin Vidic return retval; 375874bcba6SMarcus Wolf 376874bcba6SMarcus Wolf /* setup rssi irq */ 377038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO0, DIO_RSSI_DIO0); 378038a5b4eSNguyen Phan Quang Minh if (retval < 0) 379038a5b4eSNguyen Phan Quang Minh return retval; 3803d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_RSSI_DIO0; 381874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 382874bcba6SMarcus Wolf 383874bcba6SMarcus Wolf /* setup fifo level interrupt */ 384038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD); 385038a5b4eSNguyen Phan Quang Minh if (retval < 0) 386038a5b4eSNguyen Phan Quang Minh return retval; 387038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO1, DIO_FIFO_LEVEL); 388038a5b4eSNguyen Phan Quang Minh if (retval < 0) 389038a5b4eSNguyen Phan Quang Minh return retval; 3903d7f3bf2SSimon Sandström dev->irq_state[DIO1] = DIO_FIFO_LEVEL; 391874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING); 392874bcba6SMarcus Wolf 393874bcba6SMarcus Wolf /* set module to receiving mode */ 394038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(dev->spi, receive); 395038a5b4eSNguyen Phan Quang Minh if (retval < 0) 396038a5b4eSNguyen Phan Quang Minh return retval; 397874bcba6SMarcus Wolf 398874bcba6SMarcus Wolf return 0; 399874bcba6SMarcus Wolf } 400874bcba6SMarcus Wolf 401874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 402874bcba6SMarcus Wolf 4037de77a39SJoseph Wright static int 404874bcba6SMarcus Wolf pi433_receive(void *data) 405874bcba6SMarcus Wolf { 406874bcba6SMarcus Wolf struct pi433_device *dev = data; 407038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = dev->spi; 408874bcba6SMarcus Wolf int bytes_to_read, bytes_total; 409874bcba6SMarcus Wolf int retval; 410874bcba6SMarcus Wolf 411874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 412874bcba6SMarcus Wolf 413874bcba6SMarcus Wolf /* wait for any tx to finish */ 414874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for any tx to finish"); 415874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active); 41691086b82SValentin Vidic if (retval) { 41791086b82SValentin Vidic /* wait was interrupted */ 418874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 419874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 420874bcba6SMarcus Wolf return retval; 421874bcba6SMarcus Wolf } 422874bcba6SMarcus Wolf 423874bcba6SMarcus Wolf /* prepare status vars */ 424874bcba6SMarcus Wolf dev->free_in_fifo = FIFO_SIZE; 425874bcba6SMarcus Wolf dev->rx_position = 0; 426874bcba6SMarcus Wolf dev->rx_bytes_dropped = 0; 427874bcba6SMarcus Wolf 428874bcba6SMarcus Wolf /* setup radio module to listen for something "in the air" */ 429874bcba6SMarcus Wolf retval = pi433_start_rx(dev); 430874bcba6SMarcus Wolf if (retval) 431874bcba6SMarcus Wolf return retval; 432874bcba6SMarcus Wolf 433874bcba6SMarcus Wolf /* now check RSSI, if low wait for getting high (RSSI interrupt) */ 4341cd41fc3SValentin Vidic while (!rf69_get_flag(dev->spi, rssi_exceeded_threshold)) { 435874bcba6SMarcus Wolf /* allow tx to interrupt us while waiting for high RSSI */ 436874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 437874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 438874bcba6SMarcus Wolf 439874bcba6SMarcus Wolf /* wait for RSSI level to become high */ 440874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for high RSSI level"); 441874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, 442874bcba6SMarcus Wolf rf69_get_flag(dev->spi, 4431cd41fc3SValentin Vidic rssi_exceeded_threshold)); 4446b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4456b167a67SValentin Vidic goto abort; 446874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 447874bcba6SMarcus Wolf 448874bcba6SMarcus Wolf /* cross check for ongoing tx */ 4496b167a67SValentin Vidic if (!dev->tx_active) 4506b167a67SValentin Vidic break; 451874bcba6SMarcus Wolf } 452874bcba6SMarcus Wolf 453874bcba6SMarcus Wolf /* configure payload ready irq */ 454038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PAYLOAD_READY); 455038a5b4eSNguyen Phan Quang Minh if (retval < 0) 456038a5b4eSNguyen Phan Quang Minh goto abort; 4573d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_PAYLOAD_READY; 458874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 459874bcba6SMarcus Wolf 460874bcba6SMarcus Wolf /* fixed or unlimited length? */ 46191086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length != 0) { 46291086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length > dev->rx_buffer_size) { 463874bcba6SMarcus Wolf retval = -1; 464874bcba6SMarcus Wolf goto abort; 465874bcba6SMarcus Wolf } 466874bcba6SMarcus Wolf bytes_total = dev->rx_cfg.fixed_message_length; 46720e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d by fixed length", 46820e5f042SEisha Chen-yen-su bytes_total); 46991086b82SValentin Vidic } else { 470874bcba6SMarcus Wolf bytes_total = dev->rx_buffer_size; 47120e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d as requested by read", 47220e5f042SEisha Chen-yen-su bytes_total); 473874bcba6SMarcus Wolf } 474874bcba6SMarcus Wolf 475874bcba6SMarcus Wolf /* length byte enabled? */ 47691086b82SValentin Vidic if (dev->rx_cfg.enable_length_byte == OPTION_ON) { 477874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 478874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 4796b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4806b167a67SValentin Vidic goto abort; 481874bcba6SMarcus Wolf 482874bcba6SMarcus Wolf rf69_read_fifo(spi, (u8 *)&bytes_total, 1); 48369af5d92SSrishti Sharma if (bytes_total > dev->rx_buffer_size) { 484874bcba6SMarcus Wolf retval = -1; 485874bcba6SMarcus Wolf goto abort; 486874bcba6SMarcus Wolf } 487874bcba6SMarcus Wolf dev->free_in_fifo++; 48820e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte", 48920e5f042SEisha Chen-yen-su bytes_total); 490874bcba6SMarcus Wolf } 491874bcba6SMarcus Wolf 492874bcba6SMarcus Wolf /* address byte enabled? */ 493cd9d5291SValentin Vidic if (dev->rx_cfg.enable_address_filtering != filtering_off) { 494874bcba6SMarcus Wolf u8 dummy; 495874bcba6SMarcus Wolf 496874bcba6SMarcus Wolf bytes_total--; 497874bcba6SMarcus Wolf 498874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 499874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5006b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5016b167a67SValentin Vidic goto abort; 502874bcba6SMarcus Wolf 503874bcba6SMarcus Wolf rf69_read_fifo(spi, &dummy, 1); 504874bcba6SMarcus Wolf dev->free_in_fifo++; 505874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: address byte stripped off"); 506874bcba6SMarcus Wolf } 507874bcba6SMarcus Wolf 508874bcba6SMarcus Wolf /* get payload */ 50991086b82SValentin Vidic while (dev->rx_position < bytes_total) { 5100b9acf7dSValentin Vidic if (!rf69_get_flag(dev->spi, payload_ready)) { 511874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 512874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5136b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5146b167a67SValentin Vidic goto abort; 515874bcba6SMarcus Wolf } 516874bcba6SMarcus Wolf 517874bcba6SMarcus Wolf /* need to drop bytes or acquire? */ 518874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 519f1345b2fSEisha Chen-yen-su bytes_to_read = dev->rx_bytes_to_drop - 520f1345b2fSEisha Chen-yen-su dev->rx_bytes_dropped; 521874bcba6SMarcus Wolf else 522874bcba6SMarcus Wolf bytes_to_read = bytes_total - dev->rx_position; 523874bcba6SMarcus Wolf 524874bcba6SMarcus Wolf /* access the fifo */ 525874bcba6SMarcus Wolf if (bytes_to_read > FIFO_SIZE - dev->free_in_fifo) 526874bcba6SMarcus Wolf bytes_to_read = FIFO_SIZE - dev->free_in_fifo; 527874bcba6SMarcus Wolf retval = rf69_read_fifo(spi, 528874bcba6SMarcus Wolf &dev->rx_buffer[dev->rx_position], 529874bcba6SMarcus Wolf bytes_to_read); 5306b167a67SValentin Vidic if (retval) /* read failed */ 5316b167a67SValentin Vidic goto abort; 5326b167a67SValentin Vidic 533874bcba6SMarcus Wolf dev->free_in_fifo += bytes_to_read; 534874bcba6SMarcus Wolf 535874bcba6SMarcus Wolf /* adjust status vars */ 536874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 537874bcba6SMarcus Wolf dev->rx_bytes_dropped += bytes_to_read; 538874bcba6SMarcus Wolf else 539874bcba6SMarcus Wolf dev->rx_position += bytes_to_read; 540874bcba6SMarcus Wolf } 541874bcba6SMarcus Wolf 5422ebd34caSHarsha Sharma /* rx done, wait was interrupted or error occurred */ 543874bcba6SMarcus Wolf abort: 544874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 545038a5b4eSNguyen Phan Quang Minh if (rf69_set_mode(dev->spi, standby)) 546038a5b4eSNguyen Phan Quang Minh pr_err("rf69_set_mode(): radio module failed to go standby\n"); 547874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 548874bcba6SMarcus Wolf 549874bcba6SMarcus Wolf if (retval) 550874bcba6SMarcus Wolf return retval; 551874bcba6SMarcus Wolf else 552874bcba6SMarcus Wolf return bytes_total; 553874bcba6SMarcus Wolf } 554874bcba6SMarcus Wolf 5557de77a39SJoseph Wright static int 556874bcba6SMarcus Wolf pi433_tx_thread(void *data) 557874bcba6SMarcus Wolf { 558874bcba6SMarcus Wolf struct pi433_device *device = data; 559038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = device->spi; 560874bcba6SMarcus Wolf struct pi433_tx_cfg tx_cfg; 561874bcba6SMarcus Wolf size_t size; 562874bcba6SMarcus Wolf bool rx_interrupted = false; 563874bcba6SMarcus Wolf int position, repetitions; 564874bcba6SMarcus Wolf int retval; 565874bcba6SMarcus Wolf 56691086b82SValentin Vidic while (1) { 567874bcba6SMarcus Wolf /* wait for fifo to be populated or for request to terminate*/ 568874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: going to wait for new messages"); 569874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 570874bcba6SMarcus Wolf (!kfifo_is_empty(&device->tx_fifo) || 571874bcba6SMarcus Wolf kthread_should_stop())); 572874bcba6SMarcus Wolf if (kthread_should_stop()) 573874bcba6SMarcus Wolf return 0; 574874bcba6SMarcus Wolf 57536892816SSophie Matter /* 57636892816SSophie Matter * get data from fifo in the following order: 577056eeda2SDerek Robson * - tx_cfg 578056eeda2SDerek Robson * - size of message 579056eeda2SDerek Robson * - message 580056eeda2SDerek Robson */ 581874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &tx_cfg, sizeof(tx_cfg)); 58269af5d92SSrishti Sharma if (retval != sizeof(tx_cfg)) { 5835de3b090SSimon Sandström dev_dbg(device->dev, 5845de3b090SSimon Sandström "reading tx_cfg from fifo failed: got %d byte(s), expected %d", 5855de3b090SSimon Sandström retval, (unsigned int)sizeof(tx_cfg)); 586874bcba6SMarcus Wolf continue; 587874bcba6SMarcus Wolf } 588874bcba6SMarcus Wolf 589874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &size, sizeof(size_t)); 59069af5d92SSrishti Sharma if (retval != sizeof(size_t)) { 5915de3b090SSimon Sandström dev_dbg(device->dev, 5925de3b090SSimon Sandström "reading msg size from fifo failed: got %d, expected %d", 5935de3b090SSimon Sandström retval, (unsigned int)sizeof(size_t)); 594874bcba6SMarcus Wolf continue; 595874bcba6SMarcus Wolf } 596874bcba6SMarcus Wolf 597874bcba6SMarcus Wolf /* use fixed message length, if requested */ 598874bcba6SMarcus Wolf if (tx_cfg.fixed_message_length != 0) 599874bcba6SMarcus Wolf size = tx_cfg.fixed_message_length; 600874bcba6SMarcus Wolf 601874bcba6SMarcus Wolf /* increase size, if len byte is requested */ 602d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 603874bcba6SMarcus Wolf size++; 604874bcba6SMarcus Wolf 605874bcba6SMarcus Wolf /* increase size, if adr byte is requested */ 606d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 607874bcba6SMarcus Wolf size++; 608874bcba6SMarcus Wolf 609874bcba6SMarcus Wolf /* prime buffer */ 610da3761feSMarcus Wolf memset(device->buffer, 0, size); 611874bcba6SMarcus Wolf position = 0; 612874bcba6SMarcus Wolf 613874bcba6SMarcus Wolf /* add length byte, if requested */ 614d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 6155de3b090SSimon Sandström /* 6165de3b090SSimon Sandström * according to spec, length byte itself must be 6175de3b090SSimon Sandström * excluded from the length calculation 6185de3b090SSimon Sandström */ 6195de3b090SSimon Sandström device->buffer[position++] = size - 1; 620874bcba6SMarcus Wolf 621874bcba6SMarcus Wolf /* add adr byte, if requested */ 622d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 623da3761feSMarcus Wolf device->buffer[position++] = tx_cfg.address_byte; 624874bcba6SMarcus Wolf 625874bcba6SMarcus Wolf /* finally get message data from fifo */ 62620e5f042SEisha Chen-yen-su retval = kfifo_out(&device->tx_fifo, &device->buffer[position], 62720e5f042SEisha Chen-yen-su sizeof(device->buffer) - position); 62820e5f042SEisha Chen-yen-su dev_dbg(device->dev, 62920e5f042SEisha Chen-yen-su "read %d message byte(s) from fifo queue.", retval); 630874bcba6SMarcus Wolf 63136892816SSophie Matter /* 63236892816SSophie Matter * if rx is active, we need to interrupt the waiting for 633056eeda2SDerek Robson * incoming telegrams, to be able to send something. 634056eeda2SDerek Robson * We are only allowed, if currently no reception takes 635056eeda2SDerek Robson * place otherwise we need to wait for the incoming telegram 636056eeda2SDerek Robson * to finish 637056eeda2SDerek Robson */ 638874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 639874bcba6SMarcus Wolf !device->rx_active || 6402b3943b3SValentin Vidic device->interrupt_rx_allowed); 641874bcba6SMarcus Wolf 64236892816SSophie Matter /* 64336892816SSophie Matter * prevent race conditions 644056eeda2SDerek Robson * irq will be reenabled after tx config is set 645056eeda2SDerek Robson */ 646874bcba6SMarcus Wolf disable_irq(device->irq_num[DIO0]); 647874bcba6SMarcus Wolf device->tx_active = true; 648874bcba6SMarcus Wolf 649e5adddf5SSidong Yang /* clear fifo, set fifo threshold, set payload length */ 650e5adddf5SSidong Yang retval = rf69_set_mode(spi, standby); /* this clears the fifo */ 651e5adddf5SSidong Yang if (retval < 0) 652f3696bdfSSidong Yang goto abort; 653e5adddf5SSidong Yang 654de71b5bdSValentin Vidic if (device->rx_active && !rx_interrupted) { 65536892816SSophie Matter /* 65636892816SSophie Matter * rx is currently waiting for a telegram; 657056eeda2SDerek Robson * we need to set the radio module to standby 658056eeda2SDerek Robson */ 659874bcba6SMarcus Wolf rx_interrupted = true; 660874bcba6SMarcus Wolf } 661874bcba6SMarcus Wolf 662038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(spi, FIFO_THRESHOLD); 663038a5b4eSNguyen Phan Quang Minh if (retval < 0) 664f3696bdfSSidong Yang goto abort; 66591086b82SValentin Vidic if (tx_cfg.enable_length_byte == OPTION_ON) { 666038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, size * tx_cfg.repetitions); 667038a5b4eSNguyen Phan Quang Minh if (retval < 0) 668f3696bdfSSidong Yang goto abort; 66991086b82SValentin Vidic } else { 670038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, 0); 671038a5b4eSNguyen Phan Quang Minh if (retval < 0) 672f3696bdfSSidong Yang goto abort; 673874bcba6SMarcus Wolf } 674874bcba6SMarcus Wolf 675874bcba6SMarcus Wolf /* configure the rf chip */ 676038a5b4eSNguyen Phan Quang Minh retval = rf69_set_tx_cfg(device, &tx_cfg); 677038a5b4eSNguyen Phan Quang Minh if (retval < 0) 678f3696bdfSSidong Yang goto abort; 679874bcba6SMarcus Wolf 680874bcba6SMarcus Wolf /* enable fifo level interrupt */ 681038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO1, DIO_FIFO_LEVEL); 682038a5b4eSNguyen Phan Quang Minh if (retval < 0) 683f3696bdfSSidong Yang goto abort; 6843d7f3bf2SSimon Sandström device->irq_state[DIO1] = DIO_FIFO_LEVEL; 685874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING); 686874bcba6SMarcus Wolf 687874bcba6SMarcus Wolf /* enable packet sent interrupt */ 688038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PACKET_SENT); 689038a5b4eSNguyen Phan Quang Minh if (retval < 0) 690f3696bdfSSidong Yang goto abort; 6913d7f3bf2SSimon Sandström device->irq_state[DIO0] = DIO_PACKET_SENT; 692874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 693874bcba6SMarcus Wolf enable_irq(device->irq_num[DIO0]); /* was disabled by rx active check */ 694874bcba6SMarcus Wolf 695874bcba6SMarcus Wolf /* enable transmission */ 696038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, transmit); 697038a5b4eSNguyen Phan Quang Minh if (retval < 0) 698f3696bdfSSidong Yang goto abort; 699874bcba6SMarcus Wolf 700874bcba6SMarcus Wolf /* transfer this msg (and repetitions) to chip fifo */ 701874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 702874bcba6SMarcus Wolf position = 0; 703874bcba6SMarcus Wolf repetitions = tx_cfg.repetitions; 70491086b82SValentin Vidic while ((repetitions > 0) && (size > position)) { 70591086b82SValentin Vidic if ((size - position) > device->free_in_fifo) { 70691086b82SValentin Vidic /* msg to big for fifo - take a part */ 707dba18d61SValentin Vidic int write_size = device->free_in_fifo; 708dba18d61SValentin Vidic 709874bcba6SMarcus Wolf device->free_in_fifo = 0; 710874bcba6SMarcus Wolf rf69_write_fifo(spi, 711da3761feSMarcus Wolf &device->buffer[position], 712dba18d61SValentin Vidic write_size); 713dba18d61SValentin Vidic position += write_size; 71491086b82SValentin Vidic } else { 71591086b82SValentin Vidic /* msg fits into fifo - take all */ 716874bcba6SMarcus Wolf device->free_in_fifo -= size; 717874bcba6SMarcus Wolf repetitions--; 718874bcba6SMarcus Wolf rf69_write_fifo(spi, 719da3761feSMarcus Wolf &device->buffer[position], 720874bcba6SMarcus Wolf (size - position)); 721874bcba6SMarcus Wolf position = 0; /* reset for next repetition */ 722874bcba6SMarcus Wolf } 723874bcba6SMarcus Wolf 724874bcba6SMarcus Wolf retval = wait_event_interruptible(device->fifo_wait_queue, 725874bcba6SMarcus Wolf device->free_in_fifo > 0); 7266b167a67SValentin Vidic if (retval) { 727ebc1dea2SValentin Vidic dev_dbg(device->dev, "ABORT\n"); 7286b167a67SValentin Vidic goto abort; 7296b167a67SValentin Vidic } 730874bcba6SMarcus Wolf } 731874bcba6SMarcus Wolf 732874bcba6SMarcus Wolf /* we are done. Wait for packet to get sent */ 73320e5f042SEisha Chen-yen-su dev_dbg(device->dev, 73420e5f042SEisha Chen-yen-su "thread: wait for packet to get sent/fifo to be empty"); 735874bcba6SMarcus Wolf wait_event_interruptible(device->fifo_wait_queue, 736874bcba6SMarcus Wolf device->free_in_fifo == FIFO_SIZE || 737874bcba6SMarcus Wolf kthread_should_stop()); 7386b167a67SValentin Vidic if (kthread_should_stop()) 739a35342d0SSidong Yang return 0; 740874bcba6SMarcus Wolf 741874bcba6SMarcus Wolf /* STOP_TRANSMISSION */ 742874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: Packet sent. Set mode to stby."); 743038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, standby); 744038a5b4eSNguyen Phan Quang Minh if (retval < 0) 745f3696bdfSSidong Yang goto abort; 746874bcba6SMarcus Wolf 747874bcba6SMarcus Wolf /* everything sent? */ 74869af5d92SSrishti Sharma if (kfifo_is_empty(&device->tx_fifo)) { 749874bcba6SMarcus Wolf abort: 75091086b82SValentin Vidic if (rx_interrupted) { 751874bcba6SMarcus Wolf rx_interrupted = false; 752874bcba6SMarcus Wolf pi433_start_rx(device); 753874bcba6SMarcus Wolf } 754874bcba6SMarcus Wolf device->tx_active = false; 755874bcba6SMarcus Wolf wake_up_interruptible(&device->rx_wait_queue); 756874bcba6SMarcus Wolf } 757874bcba6SMarcus Wolf } 758874bcba6SMarcus Wolf } 759874bcba6SMarcus Wolf 760874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 761874bcba6SMarcus Wolf 762874bcba6SMarcus Wolf static ssize_t 763874bcba6SMarcus Wolf pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) 764874bcba6SMarcus Wolf { 765874bcba6SMarcus Wolf struct pi433_instance *instance; 766874bcba6SMarcus Wolf struct pi433_device *device; 767874bcba6SMarcus Wolf int bytes_received; 768874bcba6SMarcus Wolf ssize_t retval; 769874bcba6SMarcus Wolf 770874bcba6SMarcus Wolf /* check, whether internal buffer is big enough for requested size */ 771874bcba6SMarcus Wolf if (size > MAX_MSG_SIZE) 772874bcba6SMarcus Wolf return -EMSGSIZE; 773874bcba6SMarcus Wolf 774874bcba6SMarcus Wolf instance = filp->private_data; 775874bcba6SMarcus Wolf device = instance->device; 776874bcba6SMarcus Wolf 777874bcba6SMarcus Wolf /* just one read request at a time */ 778874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 77991086b82SValentin Vidic if (device->rx_active) { 780874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 781874bcba6SMarcus Wolf return -EAGAIN; 78283e3e2efSValentin Vidic } 78383e3e2efSValentin Vidic 784874bcba6SMarcus Wolf device->rx_active = true; 785874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 786874bcba6SMarcus Wolf 787874bcba6SMarcus Wolf /* start receiving */ 788874bcba6SMarcus Wolf /* will block until something was received*/ 789874bcba6SMarcus Wolf device->rx_buffer_size = size; 790874bcba6SMarcus Wolf bytes_received = pi433_receive(device); 791874bcba6SMarcus Wolf 792874bcba6SMarcus Wolf /* release rx */ 793874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 794874bcba6SMarcus Wolf device->rx_active = false; 795874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 796874bcba6SMarcus Wolf 797874bcba6SMarcus Wolf /* if read was successful copy to user space*/ 79869af5d92SSrishti Sharma if (bytes_received > 0) { 799874bcba6SMarcus Wolf retval = copy_to_user(buf, device->rx_buffer, bytes_received); 800874bcba6SMarcus Wolf if (retval) 80139ae5f1eSdan.carpenter@oracle.com return -EFAULT; 802874bcba6SMarcus Wolf } 803874bcba6SMarcus Wolf 804874bcba6SMarcus Wolf return bytes_received; 805874bcba6SMarcus Wolf } 806874bcba6SMarcus Wolf 807874bcba6SMarcus Wolf static ssize_t 808874bcba6SMarcus Wolf pi433_write(struct file *filp, const char __user *buf, 809874bcba6SMarcus Wolf size_t count, loff_t *f_pos) 810874bcba6SMarcus Wolf { 811874bcba6SMarcus Wolf struct pi433_instance *instance; 812874bcba6SMarcus Wolf struct pi433_device *device; 81357f8965aSStefano Manni int retval; 8145451dab9SValentin Vidic unsigned int required, available, copied; 815874bcba6SMarcus Wolf 816874bcba6SMarcus Wolf instance = filp->private_data; 817874bcba6SMarcus Wolf device = instance->device; 818874bcba6SMarcus Wolf 81963688e61SSophie Matter /* 82063688e61SSophie Matter * check, whether internal buffer (tx thread) is big enough 82163688e61SSophie Matter * for requested size 82263688e61SSophie Matter */ 823874bcba6SMarcus Wolf if (count > MAX_MSG_SIZE) 824874bcba6SMarcus Wolf return -EMSGSIZE; 825874bcba6SMarcus Wolf 82663688e61SSophie Matter /* 82763688e61SSophie Matter * write the following sequence into fifo: 828056eeda2SDerek Robson * - tx_cfg 829056eeda2SDerek Robson * - size of message 830056eeda2SDerek Robson * - message 831056eeda2SDerek Robson */ 832874bcba6SMarcus Wolf mutex_lock(&device->tx_fifo_lock); 8335451dab9SValentin Vidic 8345451dab9SValentin Vidic required = sizeof(instance->tx_cfg) + sizeof(size_t) + count; 8355451dab9SValentin Vidic available = kfifo_avail(&device->tx_fifo); 8365451dab9SValentin Vidic if (required > available) { 8375451dab9SValentin Vidic dev_dbg(device->dev, "write to fifo failed: %d bytes required but %d available", 8385451dab9SValentin Vidic required, available); 8395451dab9SValentin Vidic mutex_unlock(&device->tx_fifo_lock); 8405451dab9SValentin Vidic return -EAGAIN; 8415451dab9SValentin Vidic } 8425451dab9SValentin Vidic 84320e5f042SEisha Chen-yen-su retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg, 84420e5f042SEisha Chen-yen-su sizeof(instance->tx_cfg)); 845874bcba6SMarcus Wolf if (retval != sizeof(instance->tx_cfg)) 846874bcba6SMarcus Wolf goto abort; 847874bcba6SMarcus Wolf 848874bcba6SMarcus Wolf retval = kfifo_in(&device->tx_fifo, &count, sizeof(size_t)); 849874bcba6SMarcus Wolf if (retval != sizeof(size_t)) 850874bcba6SMarcus Wolf goto abort; 851874bcba6SMarcus Wolf 852874bcba6SMarcus Wolf retval = kfifo_from_user(&device->tx_fifo, buf, count, &copied); 853874bcba6SMarcus Wolf if (retval || copied != count) 854874bcba6SMarcus Wolf goto abort; 855874bcba6SMarcus Wolf 856874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 857874bcba6SMarcus Wolf 858874bcba6SMarcus Wolf /* start transfer */ 859874bcba6SMarcus Wolf wake_up_interruptible(&device->tx_wait_queue); 860874bcba6SMarcus Wolf dev_dbg(device->dev, "write: generated new msg with %d bytes.", copied); 861874bcba6SMarcus Wolf 862dd111469SOliver Graute return copied; 863874bcba6SMarcus Wolf 864874bcba6SMarcus Wolf abort: 8655451dab9SValentin Vidic dev_warn(device->dev, 8665451dab9SValentin Vidic "write to fifo failed, non recoverable: 0x%x", retval); 867874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 868874bcba6SMarcus Wolf return -EAGAIN; 869874bcba6SMarcus Wolf } 870874bcba6SMarcus Wolf 871874bcba6SMarcus Wolf static long 872874bcba6SMarcus Wolf pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 873874bcba6SMarcus Wolf { 874874bcba6SMarcus Wolf struct pi433_instance *instance; 875874bcba6SMarcus Wolf struct pi433_device *device; 8766de4ef65SHugo Lefeuvre struct pi433_tx_cfg tx_cfg; 87750271d38SAl Viro void __user *argp = (void __user *)arg; 878874bcba6SMarcus Wolf 879874bcba6SMarcus Wolf /* Check type and command number */ 880874bcba6SMarcus Wolf if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC) 881874bcba6SMarcus Wolf return -ENOTTY; 882874bcba6SMarcus Wolf 883874bcba6SMarcus Wolf instance = filp->private_data; 884874bcba6SMarcus Wolf device = instance->device; 885874bcba6SMarcus Wolf 88616fdcc7bSValentin Vidic if (!device) 887874bcba6SMarcus Wolf return -ESHUTDOWN; 888874bcba6SMarcus Wolf 889874bcba6SMarcus Wolf switch (cmd) { 890874bcba6SMarcus Wolf case PI433_IOC_RD_TX_CFG: 89150271d38SAl Viro if (copy_to_user(argp, &instance->tx_cfg, 89250271d38SAl Viro sizeof(struct pi433_tx_cfg))) 89350271d38SAl Viro return -EFAULT; 894874bcba6SMarcus Wolf break; 895874bcba6SMarcus Wolf case PI433_IOC_WR_TX_CFG: 8966de4ef65SHugo Lefeuvre if (copy_from_user(&tx_cfg, argp, sizeof(struct pi433_tx_cfg))) 89750271d38SAl Viro return -EFAULT; 8986de4ef65SHugo Lefeuvre mutex_lock(&device->tx_fifo_lock); 8996de4ef65SHugo Lefeuvre memcpy(&instance->tx_cfg, &tx_cfg, sizeof(struct pi433_tx_cfg)); 9006de4ef65SHugo Lefeuvre mutex_unlock(&device->tx_fifo_lock); 901874bcba6SMarcus Wolf break; 902874bcba6SMarcus Wolf case PI433_IOC_RD_RX_CFG: 90350271d38SAl Viro if (copy_to_user(argp, &device->rx_cfg, 90450271d38SAl Viro sizeof(struct pi433_rx_cfg))) 90550271d38SAl Viro return -EFAULT; 906874bcba6SMarcus Wolf break; 907874bcba6SMarcus Wolf case PI433_IOC_WR_RX_CFG: 908874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 909874bcba6SMarcus Wolf 910874bcba6SMarcus Wolf /* during pendig read request, change of config not allowed */ 911874bcba6SMarcus Wolf if (device->rx_active) { 912874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 91350271d38SAl Viro return -EAGAIN; 914874bcba6SMarcus Wolf } 915874bcba6SMarcus Wolf 91650271d38SAl Viro if (copy_from_user(&device->rx_cfg, argp, 91750271d38SAl Viro sizeof(struct pi433_rx_cfg))) { 918874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 91950271d38SAl Viro return -EFAULT; 920874bcba6SMarcus Wolf } 921874bcba6SMarcus Wolf 922874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 923874bcba6SMarcus Wolf break; 924874bcba6SMarcus Wolf default: 92548c80cccSNishka Dasgupta return -EINVAL; 926874bcba6SMarcus Wolf } 927874bcba6SMarcus Wolf 92848c80cccSNishka Dasgupta return 0; 929874bcba6SMarcus Wolf } 930874bcba6SMarcus Wolf 931874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 932874bcba6SMarcus Wolf 933874bcba6SMarcus Wolf static int pi433_open(struct inode *inode, struct file *filp) 934874bcba6SMarcus Wolf { 935874bcba6SMarcus Wolf struct pi433_device *device; 936874bcba6SMarcus Wolf struct pi433_instance *instance; 937874bcba6SMarcus Wolf 938874bcba6SMarcus Wolf mutex_lock(&minor_lock); 939874bcba6SMarcus Wolf device = idr_find(&pi433_idr, iminor(inode)); 940874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 941874bcba6SMarcus Wolf if (!device) { 942874bcba6SMarcus Wolf pr_debug("device: minor %d unknown.\n", iminor(inode)); 943874bcba6SMarcus Wolf return -ENODEV; 944874bcba6SMarcus Wolf } 945874bcba6SMarcus Wolf 946874bcba6SMarcus Wolf instance = kzalloc(sizeof(*instance), GFP_KERNEL); 947983000d7SHugo Lefeuvre if (!instance) 948874bcba6SMarcus Wolf return -ENOMEM; 949874bcba6SMarcus Wolf 950874bcba6SMarcus Wolf /* setup instance data*/ 951874bcba6SMarcus Wolf instance->device = device; 952874bcba6SMarcus Wolf instance->tx_cfg.bit_rate = 4711; 953874bcba6SMarcus Wolf // TODO: fill instance->tx_cfg; 954874bcba6SMarcus Wolf 955874bcba6SMarcus Wolf /* instance data as context */ 956874bcba6SMarcus Wolf filp->private_data = instance; 957c5bf68feSKirill Smelkov stream_open(inode, filp); 958874bcba6SMarcus Wolf 959874bcba6SMarcus Wolf return 0; 960874bcba6SMarcus Wolf } 961874bcba6SMarcus Wolf 962874bcba6SMarcus Wolf static int pi433_release(struct inode *inode, struct file *filp) 963874bcba6SMarcus Wolf { 964874bcba6SMarcus Wolf struct pi433_instance *instance; 965874bcba6SMarcus Wolf 966874bcba6SMarcus Wolf instance = filp->private_data; 967874bcba6SMarcus Wolf kfree(instance); 968874bcba6SMarcus Wolf filp->private_data = NULL; 969874bcba6SMarcus Wolf 970874bcba6SMarcus Wolf return 0; 971874bcba6SMarcus Wolf } 972874bcba6SMarcus Wolf 973874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 974874bcba6SMarcus Wolf 975469b84f3SValentin Vidic static int setup_gpio(struct pi433_device *device) 976874bcba6SMarcus Wolf { 977874bcba6SMarcus Wolf char name[5]; 978874bcba6SMarcus Wolf int retval; 979874bcba6SMarcus Wolf int i; 980ee26e236SCihangir Akturk const irq_handler_t DIO_irq_handler[NUM_DIO] = { 981ee26e236SCihangir Akturk DIO0_irq_handler, 982ee26e236SCihangir Akturk DIO1_irq_handler 983ee26e236SCihangir Akturk }; 984874bcba6SMarcus Wolf 985e27de55dSValentin Vidic for (i = 0; i < NUM_DIO; i++) { 986874bcba6SMarcus Wolf /* "construct" name and get the gpio descriptor */ 987874bcba6SMarcus Wolf snprintf(name, sizeof(name), "DIO%d", i); 98820e5f042SEisha Chen-yen-su device->gpiod[i] = gpiod_get(&device->spi->dev, name, 98920e5f042SEisha Chen-yen-su 0 /*GPIOD_IN*/); 990874bcba6SMarcus Wolf 99169af5d92SSrishti Sharma if (device->gpiod[i] == ERR_PTR(-ENOENT)) { 99220e5f042SEisha Chen-yen-su dev_dbg(&device->spi->dev, 99320e5f042SEisha Chen-yen-su "Could not find entry for %s. Ignoring.", name); 994874bcba6SMarcus Wolf continue; 995874bcba6SMarcus Wolf } 996874bcba6SMarcus Wolf 997874bcba6SMarcus Wolf if (device->gpiod[i] == ERR_PTR(-EBUSY)) 998874bcba6SMarcus Wolf dev_dbg(&device->spi->dev, "%s is busy.", name); 999874bcba6SMarcus Wolf 100091086b82SValentin Vidic if (IS_ERR(device->gpiod[i])) { 1001874bcba6SMarcus Wolf retval = PTR_ERR(device->gpiod[i]); 1002874bcba6SMarcus Wolf /* release already allocated gpios */ 1003c976752eSOliver Graute for (i--; i >= 0; i--) { 1004874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1005874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1006874bcba6SMarcus Wolf } 1007874bcba6SMarcus Wolf return retval; 1008874bcba6SMarcus Wolf } 1009874bcba6SMarcus Wolf 1010874bcba6SMarcus Wolf /* configure the pin */ 1011874bcba6SMarcus Wolf gpiod_unexport(device->gpiod[i]); 1012874bcba6SMarcus Wolf retval = gpiod_direction_input(device->gpiod[i]); 10136b167a67SValentin Vidic if (retval) 10146b167a67SValentin Vidic return retval; 1015874bcba6SMarcus Wolf 1016874bcba6SMarcus Wolf /* configure irq */ 1017874bcba6SMarcus Wolf device->irq_num[i] = gpiod_to_irq(device->gpiod[i]); 101869af5d92SSrishti Sharma if (device->irq_num[i] < 0) { 10195de3b090SSimon Sandström device->gpiod[i] = ERR_PTR(-EINVAL); 1020874bcba6SMarcus Wolf return device->irq_num[i]; 1021874bcba6SMarcus Wolf } 1022874bcba6SMarcus Wolf retval = request_irq(device->irq_num[i], 1023874bcba6SMarcus Wolf DIO_irq_handler[i], 1024874bcba6SMarcus Wolf 0, /* flags */ 1025874bcba6SMarcus Wolf name, 1026874bcba6SMarcus Wolf device); 1027874bcba6SMarcus Wolf 1028874bcba6SMarcus Wolf if (retval) 1029874bcba6SMarcus Wolf return retval; 1030874bcba6SMarcus Wolf 10312ebd34caSHarsha Sharma dev_dbg(&device->spi->dev, "%s successfully configured", name); 1032874bcba6SMarcus Wolf } 1033874bcba6SMarcus Wolf 1034874bcba6SMarcus Wolf return 0; 1035874bcba6SMarcus Wolf } 1036874bcba6SMarcus Wolf 1037469b84f3SValentin Vidic static void free_gpio(struct pi433_device *device) 1038874bcba6SMarcus Wolf { 1039874bcba6SMarcus Wolf int i; 1040874bcba6SMarcus Wolf 104191086b82SValentin Vidic for (i = 0; i < NUM_DIO; i++) { 1042874bcba6SMarcus Wolf /* check if gpiod is valid */ 1043874bcba6SMarcus Wolf if (IS_ERR(device->gpiod[i])) 1044874bcba6SMarcus Wolf continue; 1045874bcba6SMarcus Wolf 1046874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1047874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1048874bcba6SMarcus Wolf } 1049874bcba6SMarcus Wolf } 1050874bcba6SMarcus Wolf 1051874bcba6SMarcus Wolf static int pi433_get_minor(struct pi433_device *device) 1052874bcba6SMarcus Wolf { 1053874bcba6SMarcus Wolf int retval = -ENOMEM; 1054874bcba6SMarcus Wolf 1055874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1056874bcba6SMarcus Wolf retval = idr_alloc(&pi433_idr, device, 0, N_PI433_MINORS, GFP_KERNEL); 1057874bcba6SMarcus Wolf if (retval >= 0) { 1058874bcba6SMarcus Wolf device->minor = retval; 1059874bcba6SMarcus Wolf retval = 0; 1060874bcba6SMarcus Wolf } else if (retval == -ENOSPC) { 1061d2cb4845SMarcin Ciupak dev_err(&device->spi->dev, "too many pi433 devices\n"); 1062874bcba6SMarcus Wolf retval = -EINVAL; 1063874bcba6SMarcus Wolf } 1064874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1065874bcba6SMarcus Wolf return retval; 1066874bcba6SMarcus Wolf } 1067874bcba6SMarcus Wolf 1068874bcba6SMarcus Wolf static void pi433_free_minor(struct pi433_device *dev) 1069874bcba6SMarcus Wolf { 1070874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1071874bcba6SMarcus Wolf idr_remove(&pi433_idr, dev->minor); 1072874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1073874bcba6SMarcus Wolf } 1074c144df8dSValentin Vidic 1075874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1076874bcba6SMarcus Wolf 1077874bcba6SMarcus Wolf static const struct file_operations pi433_fops = { 1078874bcba6SMarcus Wolf .owner = THIS_MODULE, 107936892816SSophie Matter /* 108036892816SSophie Matter * REVISIT switch to aio primitives, so that userspace 1081874bcba6SMarcus Wolf * gets more complete API coverage. It'll simplify things 1082874bcba6SMarcus Wolf * too, except for the locking. 1083874bcba6SMarcus Wolf */ 1084874bcba6SMarcus Wolf .write = pi433_write, 1085874bcba6SMarcus Wolf .read = pi433_read, 1086874bcba6SMarcus Wolf .unlocked_ioctl = pi433_ioctl, 1087407e9ef7SArnd Bergmann .compat_ioctl = compat_ptr_ioctl, 1088874bcba6SMarcus Wolf .open = pi433_open, 1089874bcba6SMarcus Wolf .release = pi433_release, 1090874bcba6SMarcus Wolf .llseek = no_llseek, 1091874bcba6SMarcus Wolf }; 1092874bcba6SMarcus Wolf 1093874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1094874bcba6SMarcus Wolf 1095874bcba6SMarcus Wolf static int pi433_probe(struct spi_device *spi) 1096874bcba6SMarcus Wolf { 1097874bcba6SMarcus Wolf struct pi433_device *device; 1098874bcba6SMarcus Wolf int retval; 1099874bcba6SMarcus Wolf 1100874bcba6SMarcus Wolf /* setup spi parameters */ 1101874bcba6SMarcus Wolf spi->mode = 0x00; 1102874bcba6SMarcus Wolf spi->bits_per_word = 8; 110363688e61SSophie Matter /* 110463688e61SSophie Matter * spi->max_speed_hz = 10000000; 110563688e61SSophie Matter * 1MHz already set by device tree overlay 110663688e61SSophie Matter */ 1107874bcba6SMarcus Wolf 1108874bcba6SMarcus Wolf retval = spi_setup(spi); 110991086b82SValentin Vidic if (retval) { 1110874bcba6SMarcus Wolf dev_dbg(&spi->dev, "configuration of SPI interface failed!\n"); 1111874bcba6SMarcus Wolf return retval; 111283e3e2efSValentin Vidic } 111383e3e2efSValentin Vidic 1114874bcba6SMarcus Wolf dev_dbg(&spi->dev, 1115874bcba6SMarcus Wolf "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed", 1116874bcba6SMarcus Wolf spi->mode, spi->bits_per_word, spi->max_speed_hz); 1117874bcba6SMarcus Wolf 1118*6cc2d1a6SPaulo Miguel Almeida /* read chip version */ 1119*6cc2d1a6SPaulo Miguel Almeida retval = rf69_get_version(spi); 1120874bcba6SMarcus Wolf if (retval < 0) 1121874bcba6SMarcus Wolf return retval; 1122874bcba6SMarcus Wolf 11236feb5c82SXiangyang Zhang switch (retval) { 1124874bcba6SMarcus Wolf case 0x24: 112528eb8555SColin Ian King dev_dbg(&spi->dev, "found pi433 (ver. 0x%x)", retval); 1126874bcba6SMarcus Wolf break; 1127874bcba6SMarcus Wolf default: 1128874bcba6SMarcus Wolf dev_dbg(&spi->dev, "unknown chip version: 0x%x", retval); 1129874bcba6SMarcus Wolf return -ENODEV; 1130874bcba6SMarcus Wolf } 1131874bcba6SMarcus Wolf 1132874bcba6SMarcus Wolf /* Allocate driver data */ 1133874bcba6SMarcus Wolf device = kzalloc(sizeof(*device), GFP_KERNEL); 1134874bcba6SMarcus Wolf if (!device) 1135874bcba6SMarcus Wolf return -ENOMEM; 1136874bcba6SMarcus Wolf 1137874bcba6SMarcus Wolf /* Initialize the driver data */ 1138874bcba6SMarcus Wolf device->spi = spi; 1139874bcba6SMarcus Wolf device->rx_active = false; 1140874bcba6SMarcus Wolf device->tx_active = false; 1141874bcba6SMarcus Wolf device->interrupt_rx_allowed = false; 1142874bcba6SMarcus Wolf 1143983000d7SHugo Lefeuvre /* init rx buffer */ 1144983000d7SHugo Lefeuvre device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL); 1145983000d7SHugo Lefeuvre if (!device->rx_buffer) { 1146983000d7SHugo Lefeuvre retval = -ENOMEM; 1147983000d7SHugo Lefeuvre goto RX_failed; 1148983000d7SHugo Lefeuvre } 1149983000d7SHugo Lefeuvre 1150874bcba6SMarcus Wolf /* init wait queues */ 1151874bcba6SMarcus Wolf init_waitqueue_head(&device->tx_wait_queue); 1152874bcba6SMarcus Wolf init_waitqueue_head(&device->rx_wait_queue); 1153874bcba6SMarcus Wolf init_waitqueue_head(&device->fifo_wait_queue); 1154874bcba6SMarcus Wolf 1155874bcba6SMarcus Wolf /* init fifo */ 1156874bcba6SMarcus Wolf INIT_KFIFO(device->tx_fifo); 1157874bcba6SMarcus Wolf 1158874bcba6SMarcus Wolf /* init mutexes and locks */ 1159874bcba6SMarcus Wolf mutex_init(&device->tx_fifo_lock); 1160874bcba6SMarcus Wolf mutex_init(&device->rx_lock); 1161874bcba6SMarcus Wolf 1162874bcba6SMarcus Wolf /* setup GPIO (including irq_handler) for the different DIOs */ 1163469b84f3SValentin Vidic retval = setup_gpio(device); 116469af5d92SSrishti Sharma if (retval) { 1165874bcba6SMarcus Wolf dev_dbg(&spi->dev, "setup of GPIOs failed"); 1166874bcba6SMarcus Wolf goto GPIO_failed; 1167874bcba6SMarcus Wolf } 1168874bcba6SMarcus Wolf 1169874bcba6SMarcus Wolf /* setup the radio module */ 117054fc95a7SSimon Sandström retval = rf69_set_mode(spi, standby); 117154fc95a7SSimon Sandström if (retval < 0) 117254fc95a7SSimon Sandström goto minor_failed; 117354fc95a7SSimon Sandström retval = rf69_set_data_mode(spi, DATAMODUL_MODE_PACKET); 117454fc95a7SSimon Sandström if (retval < 0) 117554fc95a7SSimon Sandström goto minor_failed; 117654fc95a7SSimon Sandström retval = rf69_enable_amplifier(spi, MASK_PALEVEL_PA0); 117754fc95a7SSimon Sandström if (retval < 0) 117854fc95a7SSimon Sandström goto minor_failed; 117954fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA1); 118054fc95a7SSimon Sandström if (retval < 0) 118154fc95a7SSimon Sandström goto minor_failed; 118254fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA2); 118354fc95a7SSimon Sandström if (retval < 0) 118454fc95a7SSimon Sandström goto minor_failed; 118554fc95a7SSimon Sandström retval = rf69_set_output_power_level(spi, 13); 118654fc95a7SSimon Sandström if (retval < 0) 118754fc95a7SSimon Sandström goto minor_failed; 11880b897065SValentin Vidic retval = rf69_set_antenna_impedance(spi, fifty_ohm); 118954fc95a7SSimon Sandström if (retval < 0) 119054fc95a7SSimon Sandström goto minor_failed; 1191874bcba6SMarcus Wolf 1192874bcba6SMarcus Wolf /* determ minor number */ 1193874bcba6SMarcus Wolf retval = pi433_get_minor(device); 119469af5d92SSrishti Sharma if (retval) { 1195d2cb4845SMarcin Ciupak dev_dbg(&spi->dev, "get of minor number failed"); 1196874bcba6SMarcus Wolf goto minor_failed; 1197874bcba6SMarcus Wolf } 1198874bcba6SMarcus Wolf 1199874bcba6SMarcus Wolf /* create device */ 1200874bcba6SMarcus Wolf device->devt = MKDEV(MAJOR(pi433_dev), device->minor); 1201874bcba6SMarcus Wolf device->dev = device_create(pi433_class, 1202874bcba6SMarcus Wolf &spi->dev, 1203874bcba6SMarcus Wolf device->devt, 1204874bcba6SMarcus Wolf device, 120599ee4774SMarcin Ciupak "pi433.%d", 120699ee4774SMarcin Ciupak device->minor); 1207874bcba6SMarcus Wolf if (IS_ERR(device->dev)) { 1208874bcba6SMarcus Wolf pr_err("pi433: device register failed\n"); 1209874bcba6SMarcus Wolf retval = PTR_ERR(device->dev); 1210874bcba6SMarcus Wolf goto device_create_failed; 121191086b82SValentin Vidic } else { 1212874bcba6SMarcus Wolf dev_dbg(device->dev, 1213874bcba6SMarcus Wolf "created device for major %d, minor %d\n", 1214874bcba6SMarcus Wolf MAJOR(pi433_dev), 1215874bcba6SMarcus Wolf device->minor); 1216874bcba6SMarcus Wolf } 1217874bcba6SMarcus Wolf 1218d2cb4845SMarcin Ciupak /* start tx thread */ 1219d2cb4845SMarcin Ciupak device->tx_task_struct = kthread_run(pi433_tx_thread, 1220d2cb4845SMarcin Ciupak device, 122199ee4774SMarcin Ciupak "pi433.%d_tx_task", 122299ee4774SMarcin Ciupak device->minor); 1223d2cb4845SMarcin Ciupak if (IS_ERR(device->tx_task_struct)) { 1224d2cb4845SMarcin Ciupak dev_dbg(device->dev, "start of send thread failed"); 12251ba60ad5SWei Yongjun retval = PTR_ERR(device->tx_task_struct); 1226d2cb4845SMarcin Ciupak goto send_thread_failed; 1227d2cb4845SMarcin Ciupak } 1228d2cb4845SMarcin Ciupak 1229874bcba6SMarcus Wolf /* create cdev */ 1230874bcba6SMarcus Wolf device->cdev = cdev_alloc(); 123164c4c4caSMichael Straube if (!device->cdev) { 123264c4c4caSMichael Straube dev_dbg(device->dev, "allocation of cdev failed"); 123370458c20SWei Yongjun retval = -ENOMEM; 123464c4c4caSMichael Straube goto cdev_failed; 123564c4c4caSMichael Straube } 1236874bcba6SMarcus Wolf device->cdev->owner = THIS_MODULE; 1237874bcba6SMarcus Wolf cdev_init(device->cdev, &pi433_fops); 1238874bcba6SMarcus Wolf retval = cdev_add(device->cdev, device->devt, 1); 123969af5d92SSrishti Sharma if (retval) { 1240874bcba6SMarcus Wolf dev_dbg(device->dev, "register of cdev failed"); 1241e086f614SMichael Straube goto del_cdev; 1242874bcba6SMarcus Wolf } 1243874bcba6SMarcus Wolf 1244874bcba6SMarcus Wolf /* spi setup */ 1245874bcba6SMarcus Wolf spi_set_drvdata(spi, device); 1246874bcba6SMarcus Wolf 1247874bcba6SMarcus Wolf return 0; 1248874bcba6SMarcus Wolf 1249e086f614SMichael Straube del_cdev: 1250e086f614SMichael Straube cdev_del(device->cdev); 1251874bcba6SMarcus Wolf cdev_failed: 1252d2cb4845SMarcin Ciupak kthread_stop(device->tx_task_struct); 1253d2cb4845SMarcin Ciupak send_thread_failed: 1254874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1255874bcba6SMarcus Wolf device_create_failed: 1256874bcba6SMarcus Wolf pi433_free_minor(device); 1257874bcba6SMarcus Wolf minor_failed: 1258469b84f3SValentin Vidic free_gpio(device); 1259874bcba6SMarcus Wolf GPIO_failed: 1260983000d7SHugo Lefeuvre kfree(device->rx_buffer); 1261983000d7SHugo Lefeuvre RX_failed: 1262874bcba6SMarcus Wolf kfree(device); 1263874bcba6SMarcus Wolf 1264874bcba6SMarcus Wolf return retval; 1265874bcba6SMarcus Wolf } 1266874bcba6SMarcus Wolf 1267874bcba6SMarcus Wolf static int pi433_remove(struct spi_device *spi) 1268874bcba6SMarcus Wolf { 1269874bcba6SMarcus Wolf struct pi433_device *device = spi_get_drvdata(spi); 1270874bcba6SMarcus Wolf 1271874bcba6SMarcus Wolf /* free GPIOs */ 1272469b84f3SValentin Vidic free_gpio(device); 1273874bcba6SMarcus Wolf 1274874bcba6SMarcus Wolf /* make sure ops on existing fds can abort cleanly */ 1275874bcba6SMarcus Wolf device->spi = NULL; 1276874bcba6SMarcus Wolf 1277874bcba6SMarcus Wolf kthread_stop(device->tx_task_struct); 1278874bcba6SMarcus Wolf 1279874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1280874bcba6SMarcus Wolf 1281874bcba6SMarcus Wolf cdev_del(device->cdev); 1282874bcba6SMarcus Wolf 1283874bcba6SMarcus Wolf pi433_free_minor(device); 1284874bcba6SMarcus Wolf 1285983000d7SHugo Lefeuvre kfree(device->rx_buffer); 1286874bcba6SMarcus Wolf kfree(device); 1287874bcba6SMarcus Wolf 1288874bcba6SMarcus Wolf return 0; 1289874bcba6SMarcus Wolf } 1290874bcba6SMarcus Wolf 1291874bcba6SMarcus Wolf static const struct of_device_id pi433_dt_ids[] = { 1292874bcba6SMarcus Wolf { .compatible = "Smarthome-Wolf,pi433" }, 1293874bcba6SMarcus Wolf {}, 1294874bcba6SMarcus Wolf }; 1295874bcba6SMarcus Wolf 1296874bcba6SMarcus Wolf MODULE_DEVICE_TABLE(of, pi433_dt_ids); 1297874bcba6SMarcus Wolf 1298874bcba6SMarcus Wolf static struct spi_driver pi433_spi_driver = { 1299874bcba6SMarcus Wolf .driver = { 1300874bcba6SMarcus Wolf .name = "pi433", 1301874bcba6SMarcus Wolf .owner = THIS_MODULE, 1302874bcba6SMarcus Wolf .of_match_table = of_match_ptr(pi433_dt_ids), 1303874bcba6SMarcus Wolf }, 1304874bcba6SMarcus Wolf .probe = pi433_probe, 1305874bcba6SMarcus Wolf .remove = pi433_remove, 1306874bcba6SMarcus Wolf 130736892816SSophie Matter /* 130836892816SSophie Matter * NOTE: suspend/resume methods are not necessary here. 1309874bcba6SMarcus Wolf * We don't do anything except pass the requests to/from 1310874bcba6SMarcus Wolf * the underlying controller. The refrigerator handles 1311874bcba6SMarcus Wolf * most issues; the controller driver handles the rest. 1312874bcba6SMarcus Wolf */ 1313874bcba6SMarcus Wolf }; 1314874bcba6SMarcus Wolf 1315874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1316874bcba6SMarcus Wolf 1317874bcba6SMarcus Wolf static int __init pi433_init(void) 1318874bcba6SMarcus Wolf { 1319874bcba6SMarcus Wolf int status; 1320874bcba6SMarcus Wolf 132136892816SSophie Matter /* 132236892816SSophie Matter * If MAX_MSG_SIZE is smaller then FIFO_SIZE, the driver won't 1323056eeda2SDerek Robson * work stable - risk of buffer overflow 1324056eeda2SDerek Robson */ 1325874bcba6SMarcus Wolf if (MAX_MSG_SIZE < FIFO_SIZE) 1326874bcba6SMarcus Wolf return -EINVAL; 1327874bcba6SMarcus Wolf 132836892816SSophie Matter /* 132936892816SSophie Matter * Claim device numbers. Then register a class 1330874bcba6SMarcus Wolf * that will key udev/mdev to add/remove /dev nodes. Last, register 1331874bcba6SMarcus Wolf * Last, register the driver which manages those device numbers. 1332874bcba6SMarcus Wolf */ 1333c7265435SHariPrasath Elango status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433"); 1334874bcba6SMarcus Wolf if (status < 0) 1335874bcba6SMarcus Wolf return status; 1336874bcba6SMarcus Wolf 1337874bcba6SMarcus Wolf pi433_class = class_create(THIS_MODULE, "pi433"); 133869af5d92SSrishti Sharma if (IS_ERR(pi433_class)) { 13399be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13409be5755cSOliver Graute pi433_spi_driver.driver.name); 1341874bcba6SMarcus Wolf return PTR_ERR(pi433_class); 1342874bcba6SMarcus Wolf } 1343874bcba6SMarcus Wolf 1344874bcba6SMarcus Wolf status = spi_register_driver(&pi433_spi_driver); 134569af5d92SSrishti Sharma if (status < 0) { 1346874bcba6SMarcus Wolf class_destroy(pi433_class); 13479be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13489be5755cSOliver Graute pi433_spi_driver.driver.name); 1349874bcba6SMarcus Wolf } 1350874bcba6SMarcus Wolf 1351874bcba6SMarcus Wolf return status; 1352874bcba6SMarcus Wolf } 1353874bcba6SMarcus Wolf 1354874bcba6SMarcus Wolf module_init(pi433_init); 1355874bcba6SMarcus Wolf 1356874bcba6SMarcus Wolf static void __exit pi433_exit(void) 1357874bcba6SMarcus Wolf { 1358874bcba6SMarcus Wolf spi_unregister_driver(&pi433_spi_driver); 1359874bcba6SMarcus Wolf class_destroy(pi433_class); 1360874bcba6SMarcus Wolf unregister_chrdev(MAJOR(pi433_dev), pi433_spi_driver.driver.name); 1361874bcba6SMarcus Wolf } 1362874bcba6SMarcus Wolf module_exit(pi433_exit); 1363874bcba6SMarcus Wolf 1364874bcba6SMarcus Wolf MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>"); 1365874bcba6SMarcus Wolf MODULE_DESCRIPTION("Driver for Pi433"); 1366874bcba6SMarcus Wolf MODULE_LICENSE("GPL"); 1367874bcba6SMarcus Wolf MODULE_ALIAS("spi:pi433"); 1368