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; 95874bcba6SMarcus Wolf struct mutex rx_lock; 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) { 322966debe0SSimon Sandström ret = rf69_enable_sync(dev->spi); 323966debe0SSimon Sandström if (ret < 0) 324966debe0SSimon Sandström return ret; 325966debe0SSimon Sandström } else { 326966debe0SSimon Sandström ret = rf69_disable_sync(dev->spi); 327966debe0SSimon Sandström if (ret < 0) 328966debe0SSimon Sandström return ret; 329966debe0SSimon Sandström } 330966debe0SSimon Sandström 331d423c809SSimon Sandström if (tx_cfg->enable_length_byte == OPTION_ON) { 332c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_var); 333125a452cSElia Geretto if (ret < 0) 334125a452cSElia Geretto return ret; 335125a452cSElia Geretto } else { 336c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_fix); 337125a452cSElia Geretto if (ret < 0) 338125a452cSElia Geretto return ret; 339125a452cSElia Geretto } 34039252a4bSSimon Sandström 34139252a4bSSimon Sandström if (tx_cfg->enable_crc == OPTION_ON) { 34239252a4bSSimon Sandström ret = rf69_enable_crc(dev->spi); 34339252a4bSSimon Sandström if (ret < 0) 34439252a4bSSimon Sandström return ret; 34539252a4bSSimon Sandström } else { 34639252a4bSSimon Sandström ret = rf69_disable_crc(dev->spi); 34739252a4bSSimon Sandström if (ret < 0) 34839252a4bSSimon Sandström return ret; 34939252a4bSSimon Sandström } 350874bcba6SMarcus Wolf 351874bcba6SMarcus Wolf /* configure sync, if enabled */ 352d423c809SSimon Sandström if (tx_cfg->enable_sync == OPTION_ON) { 353038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_size(dev->spi, tx_cfg->sync_length); 354038a5b4eSNguyen Phan Quang Minh if (ret < 0) 355038a5b4eSNguyen Phan Quang Minh return ret; 356038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern); 357038a5b4eSNguyen Phan Quang Minh if (ret < 0) 358038a5b4eSNguyen Phan Quang Minh return ret; 359874bcba6SMarcus Wolf } 360874bcba6SMarcus Wolf 361874bcba6SMarcus Wolf return 0; 362874bcba6SMarcus Wolf } 363874bcba6SMarcus Wolf 364874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 365874bcba6SMarcus Wolf 366874bcba6SMarcus Wolf static int 367874bcba6SMarcus Wolf pi433_start_rx(struct pi433_device *dev) 368874bcba6SMarcus Wolf { 369874bcba6SMarcus Wolf int retval; 370874bcba6SMarcus Wolf 371874bcba6SMarcus Wolf /* return without action, if no pending read request */ 372874bcba6SMarcus Wolf if (!dev->rx_active) 373874bcba6SMarcus Wolf return 0; 374874bcba6SMarcus Wolf 375874bcba6SMarcus Wolf /* setup for receiving */ 376874bcba6SMarcus Wolf retval = rf69_set_rx_cfg(dev, &dev->rx_cfg); 3776b167a67SValentin Vidic if (retval) 3786b167a67SValentin Vidic return retval; 379874bcba6SMarcus Wolf 380874bcba6SMarcus Wolf /* setup rssi irq */ 381038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO0, DIO_RSSI_DIO0); 382038a5b4eSNguyen Phan Quang Minh if (retval < 0) 383038a5b4eSNguyen Phan Quang Minh return retval; 3843d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_RSSI_DIO0; 385874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 386874bcba6SMarcus Wolf 387874bcba6SMarcus Wolf /* setup fifo level interrupt */ 388038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD); 389038a5b4eSNguyen Phan Quang Minh if (retval < 0) 390038a5b4eSNguyen Phan Quang Minh return retval; 391038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO1, DIO_FIFO_LEVEL); 392038a5b4eSNguyen Phan Quang Minh if (retval < 0) 393038a5b4eSNguyen Phan Quang Minh return retval; 3943d7f3bf2SSimon Sandström dev->irq_state[DIO1] = DIO_FIFO_LEVEL; 395874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING); 396874bcba6SMarcus Wolf 397874bcba6SMarcus Wolf /* set module to receiving mode */ 398038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(dev->spi, receive); 399038a5b4eSNguyen Phan Quang Minh if (retval < 0) 400038a5b4eSNguyen Phan Quang Minh return retval; 401874bcba6SMarcus Wolf 402874bcba6SMarcus Wolf return 0; 403874bcba6SMarcus Wolf } 404874bcba6SMarcus Wolf 405874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 406874bcba6SMarcus Wolf 4077de77a39SJoseph Wright static int 408874bcba6SMarcus Wolf pi433_receive(void *data) 409874bcba6SMarcus Wolf { 410874bcba6SMarcus Wolf struct pi433_device *dev = data; 411038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = dev->spi; 412874bcba6SMarcus Wolf int bytes_to_read, bytes_total; 413874bcba6SMarcus Wolf int retval; 414874bcba6SMarcus Wolf 415874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 416874bcba6SMarcus Wolf 417874bcba6SMarcus Wolf /* wait for any tx to finish */ 418874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for any tx to finish"); 419874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active); 42091086b82SValentin Vidic if (retval) { 42191086b82SValentin Vidic /* wait was interrupted */ 422874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 423874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 424874bcba6SMarcus Wolf return retval; 425874bcba6SMarcus Wolf } 426874bcba6SMarcus Wolf 427874bcba6SMarcus Wolf /* prepare status vars */ 428874bcba6SMarcus Wolf dev->free_in_fifo = FIFO_SIZE; 429874bcba6SMarcus Wolf dev->rx_position = 0; 430874bcba6SMarcus Wolf dev->rx_bytes_dropped = 0; 431874bcba6SMarcus Wolf 432874bcba6SMarcus Wolf /* setup radio module to listen for something "in the air" */ 433874bcba6SMarcus Wolf retval = pi433_start_rx(dev); 434874bcba6SMarcus Wolf if (retval) 435874bcba6SMarcus Wolf return retval; 436874bcba6SMarcus Wolf 437874bcba6SMarcus Wolf /* now check RSSI, if low wait for getting high (RSSI interrupt) */ 4381cd41fc3SValentin Vidic while (!rf69_get_flag(dev->spi, rssi_exceeded_threshold)) { 439874bcba6SMarcus Wolf /* allow tx to interrupt us while waiting for high RSSI */ 440874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 441874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 442874bcba6SMarcus Wolf 443874bcba6SMarcus Wolf /* wait for RSSI level to become high */ 444874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for high RSSI level"); 445874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, 446874bcba6SMarcus Wolf rf69_get_flag(dev->spi, 4471cd41fc3SValentin Vidic rssi_exceeded_threshold)); 4486b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4496b167a67SValentin Vidic goto abort; 450874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 451874bcba6SMarcus Wolf 452874bcba6SMarcus Wolf /* cross check for ongoing tx */ 4536b167a67SValentin Vidic if (!dev->tx_active) 4546b167a67SValentin Vidic break; 455874bcba6SMarcus Wolf } 456874bcba6SMarcus Wolf 457874bcba6SMarcus Wolf /* configure payload ready irq */ 458038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PAYLOAD_READY); 459038a5b4eSNguyen Phan Quang Minh if (retval < 0) 460038a5b4eSNguyen Phan Quang Minh goto abort; 4613d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_PAYLOAD_READY; 462874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 463874bcba6SMarcus Wolf 464874bcba6SMarcus Wolf /* fixed or unlimited length? */ 46591086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length != 0) { 46691086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length > dev->rx_buffer_size) { 467874bcba6SMarcus Wolf retval = -1; 468874bcba6SMarcus Wolf goto abort; 469874bcba6SMarcus Wolf } 470874bcba6SMarcus Wolf bytes_total = dev->rx_cfg.fixed_message_length; 47120e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d by fixed length", 47220e5f042SEisha Chen-yen-su bytes_total); 47391086b82SValentin Vidic } else { 474874bcba6SMarcus Wolf bytes_total = dev->rx_buffer_size; 47520e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d as requested by read", 47620e5f042SEisha Chen-yen-su bytes_total); 477874bcba6SMarcus Wolf } 478874bcba6SMarcus Wolf 479874bcba6SMarcus Wolf /* length byte enabled? */ 48091086b82SValentin Vidic if (dev->rx_cfg.enable_length_byte == OPTION_ON) { 481874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 482874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 4836b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4846b167a67SValentin Vidic goto abort; 485874bcba6SMarcus Wolf 486874bcba6SMarcus Wolf rf69_read_fifo(spi, (u8 *)&bytes_total, 1); 48769af5d92SSrishti Sharma if (bytes_total > dev->rx_buffer_size) { 488874bcba6SMarcus Wolf retval = -1; 489874bcba6SMarcus Wolf goto abort; 490874bcba6SMarcus Wolf } 491874bcba6SMarcus Wolf dev->free_in_fifo++; 49220e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte", 49320e5f042SEisha Chen-yen-su bytes_total); 494874bcba6SMarcus Wolf } 495874bcba6SMarcus Wolf 496874bcba6SMarcus Wolf /* address byte enabled? */ 497cd9d5291SValentin Vidic if (dev->rx_cfg.enable_address_filtering != filtering_off) { 498874bcba6SMarcus Wolf u8 dummy; 499874bcba6SMarcus Wolf 500874bcba6SMarcus Wolf bytes_total--; 501874bcba6SMarcus Wolf 502874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 503874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5046b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5056b167a67SValentin Vidic goto abort; 506874bcba6SMarcus Wolf 507874bcba6SMarcus Wolf rf69_read_fifo(spi, &dummy, 1); 508874bcba6SMarcus Wolf dev->free_in_fifo++; 509874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: address byte stripped off"); 510874bcba6SMarcus Wolf } 511874bcba6SMarcus Wolf 512874bcba6SMarcus Wolf /* get payload */ 51391086b82SValentin Vidic while (dev->rx_position < bytes_total) { 5140b9acf7dSValentin Vidic if (!rf69_get_flag(dev->spi, payload_ready)) { 515874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 516874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5176b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5186b167a67SValentin Vidic goto abort; 519874bcba6SMarcus Wolf } 520874bcba6SMarcus Wolf 521874bcba6SMarcus Wolf /* need to drop bytes or acquire? */ 522874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 523f1345b2fSEisha Chen-yen-su bytes_to_read = dev->rx_bytes_to_drop - 524f1345b2fSEisha Chen-yen-su dev->rx_bytes_dropped; 525874bcba6SMarcus Wolf else 526874bcba6SMarcus Wolf bytes_to_read = bytes_total - dev->rx_position; 527874bcba6SMarcus Wolf 528874bcba6SMarcus Wolf /* access the fifo */ 529874bcba6SMarcus Wolf if (bytes_to_read > FIFO_SIZE - dev->free_in_fifo) 530874bcba6SMarcus Wolf bytes_to_read = FIFO_SIZE - dev->free_in_fifo; 531874bcba6SMarcus Wolf retval = rf69_read_fifo(spi, 532874bcba6SMarcus Wolf &dev->rx_buffer[dev->rx_position], 533874bcba6SMarcus Wolf bytes_to_read); 5346b167a67SValentin Vidic if (retval) /* read failed */ 5356b167a67SValentin Vidic goto abort; 5366b167a67SValentin Vidic 537874bcba6SMarcus Wolf dev->free_in_fifo += bytes_to_read; 538874bcba6SMarcus Wolf 539874bcba6SMarcus Wolf /* adjust status vars */ 540874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 541874bcba6SMarcus Wolf dev->rx_bytes_dropped += bytes_to_read; 542874bcba6SMarcus Wolf else 543874bcba6SMarcus Wolf dev->rx_position += bytes_to_read; 544874bcba6SMarcus Wolf } 545874bcba6SMarcus Wolf 5462ebd34caSHarsha Sharma /* rx done, wait was interrupted or error occurred */ 547874bcba6SMarcus Wolf abort: 548874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 549038a5b4eSNguyen Phan Quang Minh if (rf69_set_mode(dev->spi, standby)) 550038a5b4eSNguyen Phan Quang Minh pr_err("rf69_set_mode(): radio module failed to go standby\n"); 551874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 552874bcba6SMarcus Wolf 553874bcba6SMarcus Wolf if (retval) 554874bcba6SMarcus Wolf return retval; 555874bcba6SMarcus Wolf else 556874bcba6SMarcus Wolf return bytes_total; 557874bcba6SMarcus Wolf } 558874bcba6SMarcus Wolf 5597de77a39SJoseph Wright static int 560874bcba6SMarcus Wolf pi433_tx_thread(void *data) 561874bcba6SMarcus Wolf { 562874bcba6SMarcus Wolf struct pi433_device *device = data; 563038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = device->spi; 564874bcba6SMarcus Wolf struct pi433_tx_cfg tx_cfg; 565874bcba6SMarcus Wolf size_t size; 566874bcba6SMarcus Wolf bool rx_interrupted = false; 567874bcba6SMarcus Wolf int position, repetitions; 568874bcba6SMarcus Wolf int retval; 569874bcba6SMarcus Wolf 57091086b82SValentin Vidic while (1) { 571874bcba6SMarcus Wolf /* wait for fifo to be populated or for request to terminate*/ 572874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: going to wait for new messages"); 573874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 574874bcba6SMarcus Wolf (!kfifo_is_empty(&device->tx_fifo) || 575874bcba6SMarcus Wolf kthread_should_stop())); 576874bcba6SMarcus Wolf if (kthread_should_stop()) 577874bcba6SMarcus Wolf return 0; 578874bcba6SMarcus Wolf 57936892816SSophie Matter /* 58036892816SSophie Matter * get data from fifo in the following order: 581056eeda2SDerek Robson * - tx_cfg 582056eeda2SDerek Robson * - size of message 583056eeda2SDerek Robson * - message 584056eeda2SDerek Robson */ 585874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &tx_cfg, sizeof(tx_cfg)); 58669af5d92SSrishti Sharma if (retval != sizeof(tx_cfg)) { 5875de3b090SSimon Sandström dev_dbg(device->dev, 5885de3b090SSimon Sandström "reading tx_cfg from fifo failed: got %d byte(s), expected %d", 5895de3b090SSimon Sandström retval, (unsigned int)sizeof(tx_cfg)); 590874bcba6SMarcus Wolf continue; 591874bcba6SMarcus Wolf } 592874bcba6SMarcus Wolf 593874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &size, sizeof(size_t)); 59469af5d92SSrishti Sharma if (retval != sizeof(size_t)) { 5955de3b090SSimon Sandström dev_dbg(device->dev, 5965de3b090SSimon Sandström "reading msg size from fifo failed: got %d, expected %d", 5975de3b090SSimon Sandström retval, (unsigned int)sizeof(size_t)); 598874bcba6SMarcus Wolf continue; 599874bcba6SMarcus Wolf } 600874bcba6SMarcus Wolf 601874bcba6SMarcus Wolf /* use fixed message length, if requested */ 602874bcba6SMarcus Wolf if (tx_cfg.fixed_message_length != 0) 603874bcba6SMarcus Wolf size = tx_cfg.fixed_message_length; 604874bcba6SMarcus Wolf 605874bcba6SMarcus Wolf /* increase size, if len byte is requested */ 606d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 607874bcba6SMarcus Wolf size++; 608874bcba6SMarcus Wolf 609874bcba6SMarcus Wolf /* increase size, if adr byte is requested */ 610d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 611874bcba6SMarcus Wolf size++; 612874bcba6SMarcus Wolf 613874bcba6SMarcus Wolf /* prime buffer */ 614da3761feSMarcus Wolf memset(device->buffer, 0, size); 615874bcba6SMarcus Wolf position = 0; 616874bcba6SMarcus Wolf 617874bcba6SMarcus Wolf /* add length byte, if requested */ 618d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 6195de3b090SSimon Sandström /* 6205de3b090SSimon Sandström * according to spec, length byte itself must be 6215de3b090SSimon Sandström * excluded from the length calculation 6225de3b090SSimon Sandström */ 6235de3b090SSimon Sandström device->buffer[position++] = size - 1; 624874bcba6SMarcus Wolf 625874bcba6SMarcus Wolf /* add adr byte, if requested */ 626d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 627da3761feSMarcus Wolf device->buffer[position++] = tx_cfg.address_byte; 628874bcba6SMarcus Wolf 629874bcba6SMarcus Wolf /* finally get message data from fifo */ 63020e5f042SEisha Chen-yen-su retval = kfifo_out(&device->tx_fifo, &device->buffer[position], 63120e5f042SEisha Chen-yen-su sizeof(device->buffer) - position); 63220e5f042SEisha Chen-yen-su dev_dbg(device->dev, 63320e5f042SEisha Chen-yen-su "read %d message byte(s) from fifo queue.", retval); 634874bcba6SMarcus Wolf 63536892816SSophie Matter /* 63636892816SSophie Matter * if rx is active, we need to interrupt the waiting for 637056eeda2SDerek Robson * incoming telegrams, to be able to send something. 638056eeda2SDerek Robson * We are only allowed, if currently no reception takes 639056eeda2SDerek Robson * place otherwise we need to wait for the incoming telegram 640056eeda2SDerek Robson * to finish 641056eeda2SDerek Robson */ 642874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 643874bcba6SMarcus Wolf !device->rx_active || 6442b3943b3SValentin Vidic device->interrupt_rx_allowed); 645874bcba6SMarcus Wolf 64636892816SSophie Matter /* 64736892816SSophie Matter * prevent race conditions 648056eeda2SDerek Robson * irq will be reenabled after tx config is set 649056eeda2SDerek Robson */ 650874bcba6SMarcus Wolf disable_irq(device->irq_num[DIO0]); 651874bcba6SMarcus Wolf device->tx_active = true; 652874bcba6SMarcus Wolf 653de71b5bdSValentin Vidic if (device->rx_active && !rx_interrupted) { 65436892816SSophie Matter /* 65536892816SSophie Matter * rx is currently waiting for a telegram; 656056eeda2SDerek Robson * we need to set the radio module to standby 657056eeda2SDerek Robson */ 658038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(device->spi, standby); 659038a5b4eSNguyen Phan Quang Minh if (retval < 0) 660038a5b4eSNguyen Phan Quang Minh return retval; 661874bcba6SMarcus Wolf rx_interrupted = true; 662874bcba6SMarcus Wolf } 663874bcba6SMarcus Wolf 664874bcba6SMarcus Wolf /* clear fifo, set fifo threshold, set payload length */ 665038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, standby); /* this clears the fifo */ 666038a5b4eSNguyen Phan Quang Minh if (retval < 0) 667038a5b4eSNguyen Phan Quang Minh return retval; 668038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(spi, FIFO_THRESHOLD); 669038a5b4eSNguyen Phan Quang Minh if (retval < 0) 670038a5b4eSNguyen Phan Quang Minh return retval; 67191086b82SValentin Vidic if (tx_cfg.enable_length_byte == OPTION_ON) { 672038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, size * tx_cfg.repetitions); 673038a5b4eSNguyen Phan Quang Minh if (retval < 0) 674038a5b4eSNguyen Phan Quang Minh return retval; 67591086b82SValentin Vidic } else { 676038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, 0); 677038a5b4eSNguyen Phan Quang Minh if (retval < 0) 678038a5b4eSNguyen Phan Quang Minh return retval; 679874bcba6SMarcus Wolf } 680874bcba6SMarcus Wolf 681874bcba6SMarcus Wolf /* configure the rf chip */ 682038a5b4eSNguyen Phan Quang Minh retval = rf69_set_tx_cfg(device, &tx_cfg); 683038a5b4eSNguyen Phan Quang Minh if (retval < 0) 684038a5b4eSNguyen Phan Quang Minh return retval; 685874bcba6SMarcus Wolf 686874bcba6SMarcus Wolf /* enable fifo level interrupt */ 687038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO1, DIO_FIFO_LEVEL); 688038a5b4eSNguyen Phan Quang Minh if (retval < 0) 689038a5b4eSNguyen Phan Quang Minh return retval; 6903d7f3bf2SSimon Sandström device->irq_state[DIO1] = DIO_FIFO_LEVEL; 691874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING); 692874bcba6SMarcus Wolf 693874bcba6SMarcus Wolf /* enable packet sent interrupt */ 694038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PACKET_SENT); 695038a5b4eSNguyen Phan Quang Minh if (retval < 0) 696038a5b4eSNguyen Phan Quang Minh return retval; 6973d7f3bf2SSimon Sandström device->irq_state[DIO0] = DIO_PACKET_SENT; 698874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 699874bcba6SMarcus Wolf enable_irq(device->irq_num[DIO0]); /* was disabled by rx active check */ 700874bcba6SMarcus Wolf 701874bcba6SMarcus Wolf /* enable transmission */ 702038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, transmit); 703038a5b4eSNguyen Phan Quang Minh if (retval < 0) 704038a5b4eSNguyen Phan Quang Minh return retval; 705874bcba6SMarcus Wolf 706874bcba6SMarcus Wolf /* transfer this msg (and repetitions) to chip fifo */ 707874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 708874bcba6SMarcus Wolf position = 0; 709874bcba6SMarcus Wolf repetitions = tx_cfg.repetitions; 71091086b82SValentin Vidic while ((repetitions > 0) && (size > position)) { 71191086b82SValentin Vidic if ((size - position) > device->free_in_fifo) { 71291086b82SValentin Vidic /* msg to big for fifo - take a part */ 713dba18d61SValentin Vidic int write_size = device->free_in_fifo; 714dba18d61SValentin Vidic 715874bcba6SMarcus Wolf device->free_in_fifo = 0; 716874bcba6SMarcus Wolf rf69_write_fifo(spi, 717da3761feSMarcus Wolf &device->buffer[position], 718dba18d61SValentin Vidic write_size); 719dba18d61SValentin Vidic position += write_size; 72091086b82SValentin Vidic } else { 72191086b82SValentin Vidic /* msg fits into fifo - take all */ 722874bcba6SMarcus Wolf device->free_in_fifo -= size; 723874bcba6SMarcus Wolf repetitions--; 724874bcba6SMarcus Wolf rf69_write_fifo(spi, 725da3761feSMarcus Wolf &device->buffer[position], 726874bcba6SMarcus Wolf (size - position)); 727874bcba6SMarcus Wolf position = 0; /* reset for next repetition */ 728874bcba6SMarcus Wolf } 729874bcba6SMarcus Wolf 730874bcba6SMarcus Wolf retval = wait_event_interruptible(device->fifo_wait_queue, 731874bcba6SMarcus Wolf device->free_in_fifo > 0); 7326b167a67SValentin Vidic if (retval) { 733ebc1dea2SValentin Vidic dev_dbg(device->dev, "ABORT\n"); 7346b167a67SValentin Vidic goto abort; 7356b167a67SValentin Vidic } 736874bcba6SMarcus Wolf } 737874bcba6SMarcus Wolf 738874bcba6SMarcus Wolf /* we are done. Wait for packet to get sent */ 73920e5f042SEisha Chen-yen-su dev_dbg(device->dev, 74020e5f042SEisha Chen-yen-su "thread: wait for packet to get sent/fifo to be empty"); 741874bcba6SMarcus Wolf wait_event_interruptible(device->fifo_wait_queue, 742874bcba6SMarcus Wolf device->free_in_fifo == FIFO_SIZE || 743874bcba6SMarcus Wolf kthread_should_stop()); 7446b167a67SValentin Vidic if (kthread_should_stop()) 745ebc1dea2SValentin Vidic dev_dbg(device->dev, "ABORT\n"); 746874bcba6SMarcus Wolf 747874bcba6SMarcus Wolf /* STOP_TRANSMISSION */ 748874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: Packet sent. Set mode to stby."); 749038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, standby); 750038a5b4eSNguyen Phan Quang Minh if (retval < 0) 751038a5b4eSNguyen Phan Quang Minh return retval; 752874bcba6SMarcus Wolf 753874bcba6SMarcus Wolf /* everything sent? */ 75469af5d92SSrishti Sharma if (kfifo_is_empty(&device->tx_fifo)) { 755874bcba6SMarcus Wolf abort: 75691086b82SValentin Vidic if (rx_interrupted) { 757874bcba6SMarcus Wolf rx_interrupted = false; 758874bcba6SMarcus Wolf pi433_start_rx(device); 759874bcba6SMarcus Wolf } 760874bcba6SMarcus Wolf device->tx_active = false; 761874bcba6SMarcus Wolf wake_up_interruptible(&device->rx_wait_queue); 762874bcba6SMarcus Wolf } 763874bcba6SMarcus Wolf } 764874bcba6SMarcus Wolf } 765874bcba6SMarcus Wolf 766874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 767874bcba6SMarcus Wolf 768874bcba6SMarcus Wolf static ssize_t 769874bcba6SMarcus Wolf pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) 770874bcba6SMarcus Wolf { 771874bcba6SMarcus Wolf struct pi433_instance *instance; 772874bcba6SMarcus Wolf struct pi433_device *device; 773874bcba6SMarcus Wolf int bytes_received; 774874bcba6SMarcus Wolf ssize_t retval; 775874bcba6SMarcus Wolf 776874bcba6SMarcus Wolf /* check, whether internal buffer is big enough for requested size */ 777874bcba6SMarcus Wolf if (size > MAX_MSG_SIZE) 778874bcba6SMarcus Wolf return -EMSGSIZE; 779874bcba6SMarcus Wolf 780874bcba6SMarcus Wolf instance = filp->private_data; 781874bcba6SMarcus Wolf device = instance->device; 782874bcba6SMarcus Wolf 783874bcba6SMarcus Wolf /* just one read request at a time */ 784874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 78591086b82SValentin Vidic if (device->rx_active) { 786874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 787874bcba6SMarcus Wolf return -EAGAIN; 78883e3e2efSValentin Vidic } 78983e3e2efSValentin Vidic 790874bcba6SMarcus Wolf device->rx_active = true; 791874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 792874bcba6SMarcus Wolf 793874bcba6SMarcus Wolf /* start receiving */ 794874bcba6SMarcus Wolf /* will block until something was received*/ 795874bcba6SMarcus Wolf device->rx_buffer_size = size; 796874bcba6SMarcus Wolf bytes_received = pi433_receive(device); 797874bcba6SMarcus Wolf 798874bcba6SMarcus Wolf /* release rx */ 799874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 800874bcba6SMarcus Wolf device->rx_active = false; 801874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 802874bcba6SMarcus Wolf 803874bcba6SMarcus Wolf /* if read was successful copy to user space*/ 80469af5d92SSrishti Sharma if (bytes_received > 0) { 805874bcba6SMarcus Wolf retval = copy_to_user(buf, device->rx_buffer, bytes_received); 806874bcba6SMarcus Wolf if (retval) 80739ae5f1eSdan.carpenter@oracle.com return -EFAULT; 808874bcba6SMarcus Wolf } 809874bcba6SMarcus Wolf 810874bcba6SMarcus Wolf return bytes_received; 811874bcba6SMarcus Wolf } 812874bcba6SMarcus Wolf 813874bcba6SMarcus Wolf static ssize_t 814874bcba6SMarcus Wolf pi433_write(struct file *filp, const char __user *buf, 815874bcba6SMarcus Wolf size_t count, loff_t *f_pos) 816874bcba6SMarcus Wolf { 817874bcba6SMarcus Wolf struct pi433_instance *instance; 818874bcba6SMarcus Wolf struct pi433_device *device; 81957f8965aSStefano Manni int retval; 8205451dab9SValentin Vidic unsigned int required, available, copied; 821874bcba6SMarcus Wolf 822874bcba6SMarcus Wolf instance = filp->private_data; 823874bcba6SMarcus Wolf device = instance->device; 824874bcba6SMarcus Wolf 82563688e61SSophie Matter /* 82663688e61SSophie Matter * check, whether internal buffer (tx thread) is big enough 82763688e61SSophie Matter * for requested size 82863688e61SSophie Matter */ 829874bcba6SMarcus Wolf if (count > MAX_MSG_SIZE) 830874bcba6SMarcus Wolf return -EMSGSIZE; 831874bcba6SMarcus Wolf 83263688e61SSophie Matter /* 83363688e61SSophie Matter * write the following sequence into fifo: 834056eeda2SDerek Robson * - tx_cfg 835056eeda2SDerek Robson * - size of message 836056eeda2SDerek Robson * - message 837056eeda2SDerek Robson */ 838874bcba6SMarcus Wolf mutex_lock(&device->tx_fifo_lock); 8395451dab9SValentin Vidic 8405451dab9SValentin Vidic required = sizeof(instance->tx_cfg) + sizeof(size_t) + count; 8415451dab9SValentin Vidic available = kfifo_avail(&device->tx_fifo); 8425451dab9SValentin Vidic if (required > available) { 8435451dab9SValentin Vidic dev_dbg(device->dev, "write to fifo failed: %d bytes required but %d available", 8445451dab9SValentin Vidic required, available); 8455451dab9SValentin Vidic mutex_unlock(&device->tx_fifo_lock); 8465451dab9SValentin Vidic return -EAGAIN; 8475451dab9SValentin Vidic } 8485451dab9SValentin Vidic 84920e5f042SEisha Chen-yen-su retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg, 85020e5f042SEisha Chen-yen-su sizeof(instance->tx_cfg)); 851874bcba6SMarcus Wolf if (retval != sizeof(instance->tx_cfg)) 852874bcba6SMarcus Wolf goto abort; 853874bcba6SMarcus Wolf 854874bcba6SMarcus Wolf retval = kfifo_in(&device->tx_fifo, &count, sizeof(size_t)); 855874bcba6SMarcus Wolf if (retval != sizeof(size_t)) 856874bcba6SMarcus Wolf goto abort; 857874bcba6SMarcus Wolf 858874bcba6SMarcus Wolf retval = kfifo_from_user(&device->tx_fifo, buf, count, &copied); 859874bcba6SMarcus Wolf if (retval || copied != count) 860874bcba6SMarcus Wolf goto abort; 861874bcba6SMarcus Wolf 862874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 863874bcba6SMarcus Wolf 864874bcba6SMarcus Wolf /* start transfer */ 865874bcba6SMarcus Wolf wake_up_interruptible(&device->tx_wait_queue); 866874bcba6SMarcus Wolf dev_dbg(device->dev, "write: generated new msg with %d bytes.", copied); 867874bcba6SMarcus Wolf 868dd111469SOliver Graute return copied; 869874bcba6SMarcus Wolf 870874bcba6SMarcus Wolf abort: 8715451dab9SValentin Vidic dev_warn(device->dev, 8725451dab9SValentin Vidic "write to fifo failed, non recoverable: 0x%x", retval); 873874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 874874bcba6SMarcus Wolf return -EAGAIN; 875874bcba6SMarcus Wolf } 876874bcba6SMarcus Wolf 877874bcba6SMarcus Wolf static long 878874bcba6SMarcus Wolf pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 879874bcba6SMarcus Wolf { 880874bcba6SMarcus Wolf int retval = 0; 881874bcba6SMarcus Wolf struct pi433_instance *instance; 882874bcba6SMarcus Wolf struct pi433_device *device; 8836de4ef65SHugo Lefeuvre struct pi433_tx_cfg tx_cfg; 88450271d38SAl Viro void __user *argp = (void __user *)arg; 885874bcba6SMarcus Wolf 886874bcba6SMarcus Wolf /* Check type and command number */ 887874bcba6SMarcus Wolf if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC) 888874bcba6SMarcus Wolf return -ENOTTY; 889874bcba6SMarcus Wolf 890874bcba6SMarcus Wolf instance = filp->private_data; 891874bcba6SMarcus Wolf device = instance->device; 892874bcba6SMarcus Wolf 89316fdcc7bSValentin Vidic if (!device) 894874bcba6SMarcus Wolf return -ESHUTDOWN; 895874bcba6SMarcus Wolf 896874bcba6SMarcus Wolf switch (cmd) { 897874bcba6SMarcus Wolf case PI433_IOC_RD_TX_CFG: 89850271d38SAl Viro if (copy_to_user(argp, &instance->tx_cfg, 89950271d38SAl Viro sizeof(struct pi433_tx_cfg))) 90050271d38SAl Viro return -EFAULT; 901874bcba6SMarcus Wolf break; 902874bcba6SMarcus Wolf case PI433_IOC_WR_TX_CFG: 9036de4ef65SHugo Lefeuvre if (copy_from_user(&tx_cfg, argp, sizeof(struct pi433_tx_cfg))) 90450271d38SAl Viro return -EFAULT; 9056de4ef65SHugo Lefeuvre mutex_lock(&device->tx_fifo_lock); 9066de4ef65SHugo Lefeuvre memcpy(&instance->tx_cfg, &tx_cfg, sizeof(struct pi433_tx_cfg)); 9076de4ef65SHugo Lefeuvre mutex_unlock(&device->tx_fifo_lock); 908874bcba6SMarcus Wolf break; 909874bcba6SMarcus Wolf case PI433_IOC_RD_RX_CFG: 91050271d38SAl Viro if (copy_to_user(argp, &device->rx_cfg, 91150271d38SAl Viro sizeof(struct pi433_rx_cfg))) 91250271d38SAl Viro return -EFAULT; 913874bcba6SMarcus Wolf break; 914874bcba6SMarcus Wolf case PI433_IOC_WR_RX_CFG: 915874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 916874bcba6SMarcus Wolf 917874bcba6SMarcus Wolf /* during pendig read request, change of config not allowed */ 918874bcba6SMarcus Wolf if (device->rx_active) { 919874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 92050271d38SAl Viro return -EAGAIN; 921874bcba6SMarcus Wolf } 922874bcba6SMarcus Wolf 92350271d38SAl Viro if (copy_from_user(&device->rx_cfg, argp, 92450271d38SAl Viro sizeof(struct pi433_rx_cfg))) { 925874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 92650271d38SAl Viro return -EFAULT; 927874bcba6SMarcus Wolf } 928874bcba6SMarcus Wolf 929874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 930874bcba6SMarcus Wolf break; 931874bcba6SMarcus Wolf default: 932874bcba6SMarcus Wolf retval = -EINVAL; 933874bcba6SMarcus Wolf } 934874bcba6SMarcus Wolf 935874bcba6SMarcus Wolf return retval; 936874bcba6SMarcus Wolf } 937874bcba6SMarcus Wolf 938874bcba6SMarcus Wolf #ifdef CONFIG_COMPAT 939874bcba6SMarcus Wolf static long 940874bcba6SMarcus Wolf pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 941874bcba6SMarcus Wolf { 942874bcba6SMarcus Wolf return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); 943874bcba6SMarcus Wolf } 944874bcba6SMarcus Wolf #else 945874bcba6SMarcus Wolf #define pi433_compat_ioctl NULL 946874bcba6SMarcus Wolf #endif /* CONFIG_COMPAT */ 947874bcba6SMarcus Wolf 948874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 949874bcba6SMarcus Wolf 950874bcba6SMarcus Wolf static int pi433_open(struct inode *inode, struct file *filp) 951874bcba6SMarcus Wolf { 952874bcba6SMarcus Wolf struct pi433_device *device; 953874bcba6SMarcus Wolf struct pi433_instance *instance; 954874bcba6SMarcus Wolf 955874bcba6SMarcus Wolf mutex_lock(&minor_lock); 956874bcba6SMarcus Wolf device = idr_find(&pi433_idr, iminor(inode)); 957874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 958874bcba6SMarcus Wolf if (!device) { 959874bcba6SMarcus Wolf pr_debug("device: minor %d unknown.\n", iminor(inode)); 960874bcba6SMarcus Wolf return -ENODEV; 961874bcba6SMarcus Wolf } 962874bcba6SMarcus Wolf 963874bcba6SMarcus Wolf instance = kzalloc(sizeof(*instance), GFP_KERNEL); 964983000d7SHugo Lefeuvre if (!instance) 965874bcba6SMarcus Wolf return -ENOMEM; 966874bcba6SMarcus Wolf 967874bcba6SMarcus Wolf /* setup instance data*/ 968874bcba6SMarcus Wolf instance->device = device; 969874bcba6SMarcus Wolf instance->tx_cfg.bit_rate = 4711; 970874bcba6SMarcus Wolf // TODO: fill instance->tx_cfg; 971874bcba6SMarcus Wolf 972874bcba6SMarcus Wolf /* instance data as context */ 973874bcba6SMarcus Wolf filp->private_data = instance; 974c5bf68feSKirill Smelkov stream_open(inode, filp); 975874bcba6SMarcus Wolf 976874bcba6SMarcus Wolf return 0; 977874bcba6SMarcus Wolf } 978874bcba6SMarcus Wolf 979874bcba6SMarcus Wolf static int pi433_release(struct inode *inode, struct file *filp) 980874bcba6SMarcus Wolf { 981874bcba6SMarcus Wolf struct pi433_instance *instance; 982874bcba6SMarcus Wolf 983874bcba6SMarcus Wolf instance = filp->private_data; 984874bcba6SMarcus Wolf kfree(instance); 985874bcba6SMarcus Wolf filp->private_data = NULL; 986874bcba6SMarcus Wolf 987874bcba6SMarcus Wolf return 0; 988874bcba6SMarcus Wolf } 989874bcba6SMarcus Wolf 990874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 991874bcba6SMarcus Wolf 992469b84f3SValentin Vidic static int setup_gpio(struct pi433_device *device) 993874bcba6SMarcus Wolf { 994874bcba6SMarcus Wolf char name[5]; 995874bcba6SMarcus Wolf int retval; 996874bcba6SMarcus Wolf int i; 997ee26e236SCihangir Akturk const irq_handler_t DIO_irq_handler[NUM_DIO] = { 998ee26e236SCihangir Akturk DIO0_irq_handler, 999ee26e236SCihangir Akturk DIO1_irq_handler 1000ee26e236SCihangir Akturk }; 1001874bcba6SMarcus Wolf 1002e27de55dSValentin Vidic for (i = 0; i < NUM_DIO; i++) { 1003874bcba6SMarcus Wolf /* "construct" name and get the gpio descriptor */ 1004874bcba6SMarcus Wolf snprintf(name, sizeof(name), "DIO%d", i); 100520e5f042SEisha Chen-yen-su device->gpiod[i] = gpiod_get(&device->spi->dev, name, 100620e5f042SEisha Chen-yen-su 0 /*GPIOD_IN*/); 1007874bcba6SMarcus Wolf 100869af5d92SSrishti Sharma if (device->gpiod[i] == ERR_PTR(-ENOENT)) { 100920e5f042SEisha Chen-yen-su dev_dbg(&device->spi->dev, 101020e5f042SEisha Chen-yen-su "Could not find entry for %s. Ignoring.", name); 1011874bcba6SMarcus Wolf continue; 1012874bcba6SMarcus Wolf } 1013874bcba6SMarcus Wolf 1014874bcba6SMarcus Wolf if (device->gpiod[i] == ERR_PTR(-EBUSY)) 1015874bcba6SMarcus Wolf dev_dbg(&device->spi->dev, "%s is busy.", name); 1016874bcba6SMarcus Wolf 101791086b82SValentin Vidic if (IS_ERR(device->gpiod[i])) { 1018874bcba6SMarcus Wolf retval = PTR_ERR(device->gpiod[i]); 1019874bcba6SMarcus Wolf /* release already allocated gpios */ 1020c976752eSOliver Graute for (i--; i >= 0; i--) { 1021874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1022874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1023874bcba6SMarcus Wolf } 1024874bcba6SMarcus Wolf return retval; 1025874bcba6SMarcus Wolf } 1026874bcba6SMarcus Wolf 1027874bcba6SMarcus Wolf /* configure the pin */ 1028874bcba6SMarcus Wolf gpiod_unexport(device->gpiod[i]); 1029874bcba6SMarcus Wolf retval = gpiod_direction_input(device->gpiod[i]); 10306b167a67SValentin Vidic if (retval) 10316b167a67SValentin Vidic return retval; 1032874bcba6SMarcus Wolf 1033874bcba6SMarcus Wolf /* configure irq */ 1034874bcba6SMarcus Wolf device->irq_num[i] = gpiod_to_irq(device->gpiod[i]); 103569af5d92SSrishti Sharma if (device->irq_num[i] < 0) { 10365de3b090SSimon Sandström device->gpiod[i] = ERR_PTR(-EINVAL); 1037874bcba6SMarcus Wolf return device->irq_num[i]; 1038874bcba6SMarcus Wolf } 1039874bcba6SMarcus Wolf retval = request_irq(device->irq_num[i], 1040874bcba6SMarcus Wolf DIO_irq_handler[i], 1041874bcba6SMarcus Wolf 0, /* flags */ 1042874bcba6SMarcus Wolf name, 1043874bcba6SMarcus Wolf device); 1044874bcba6SMarcus Wolf 1045874bcba6SMarcus Wolf if (retval) 1046874bcba6SMarcus Wolf return retval; 1047874bcba6SMarcus Wolf 10482ebd34caSHarsha Sharma dev_dbg(&device->spi->dev, "%s successfully configured", name); 1049874bcba6SMarcus Wolf } 1050874bcba6SMarcus Wolf 1051874bcba6SMarcus Wolf return 0; 1052874bcba6SMarcus Wolf } 1053874bcba6SMarcus Wolf 1054469b84f3SValentin Vidic static void free_gpio(struct pi433_device *device) 1055874bcba6SMarcus Wolf { 1056874bcba6SMarcus Wolf int i; 1057874bcba6SMarcus Wolf 105891086b82SValentin Vidic for (i = 0; i < NUM_DIO; i++) { 1059874bcba6SMarcus Wolf /* check if gpiod is valid */ 1060874bcba6SMarcus Wolf if (IS_ERR(device->gpiod[i])) 1061874bcba6SMarcus Wolf continue; 1062874bcba6SMarcus Wolf 1063874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1064874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1065874bcba6SMarcus Wolf } 1066874bcba6SMarcus Wolf } 1067874bcba6SMarcus Wolf 1068874bcba6SMarcus Wolf static int pi433_get_minor(struct pi433_device *device) 1069874bcba6SMarcus Wolf { 1070874bcba6SMarcus Wolf int retval = -ENOMEM; 1071874bcba6SMarcus Wolf 1072874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1073874bcba6SMarcus Wolf retval = idr_alloc(&pi433_idr, device, 0, N_PI433_MINORS, GFP_KERNEL); 1074874bcba6SMarcus Wolf if (retval >= 0) { 1075874bcba6SMarcus Wolf device->minor = retval; 1076874bcba6SMarcus Wolf retval = 0; 1077874bcba6SMarcus Wolf } else if (retval == -ENOSPC) { 1078d2cb4845SMarcin Ciupak dev_err(&device->spi->dev, "too many pi433 devices\n"); 1079874bcba6SMarcus Wolf retval = -EINVAL; 1080874bcba6SMarcus Wolf } 1081874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1082874bcba6SMarcus Wolf return retval; 1083874bcba6SMarcus Wolf } 1084874bcba6SMarcus Wolf 1085874bcba6SMarcus Wolf static void pi433_free_minor(struct pi433_device *dev) 1086874bcba6SMarcus Wolf { 1087874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1088874bcba6SMarcus Wolf idr_remove(&pi433_idr, dev->minor); 1089874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1090874bcba6SMarcus Wolf } 1091c144df8dSValentin Vidic 1092874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1093874bcba6SMarcus Wolf 1094874bcba6SMarcus Wolf static const struct file_operations pi433_fops = { 1095874bcba6SMarcus Wolf .owner = THIS_MODULE, 109636892816SSophie Matter /* 109736892816SSophie Matter * REVISIT switch to aio primitives, so that userspace 1098874bcba6SMarcus Wolf * gets more complete API coverage. It'll simplify things 1099874bcba6SMarcus Wolf * too, except for the locking. 1100874bcba6SMarcus Wolf */ 1101874bcba6SMarcus Wolf .write = pi433_write, 1102874bcba6SMarcus Wolf .read = pi433_read, 1103874bcba6SMarcus Wolf .unlocked_ioctl = pi433_ioctl, 1104874bcba6SMarcus Wolf .compat_ioctl = pi433_compat_ioctl, 1105874bcba6SMarcus Wolf .open = pi433_open, 1106874bcba6SMarcus Wolf .release = pi433_release, 1107874bcba6SMarcus Wolf .llseek = no_llseek, 1108874bcba6SMarcus Wolf }; 1109874bcba6SMarcus Wolf 1110874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1111874bcba6SMarcus Wolf 1112874bcba6SMarcus Wolf static int pi433_probe(struct spi_device *spi) 1113874bcba6SMarcus Wolf { 1114874bcba6SMarcus Wolf struct pi433_device *device; 1115874bcba6SMarcus Wolf int retval; 1116874bcba6SMarcus Wolf 1117874bcba6SMarcus Wolf /* setup spi parameters */ 1118874bcba6SMarcus Wolf spi->mode = 0x00; 1119874bcba6SMarcus Wolf spi->bits_per_word = 8; 112063688e61SSophie Matter /* 112163688e61SSophie Matter * spi->max_speed_hz = 10000000; 112263688e61SSophie Matter * 1MHz already set by device tree overlay 112363688e61SSophie Matter */ 1124874bcba6SMarcus Wolf 1125874bcba6SMarcus Wolf retval = spi_setup(spi); 112691086b82SValentin Vidic if (retval) { 1127874bcba6SMarcus Wolf dev_dbg(&spi->dev, "configuration of SPI interface failed!\n"); 1128874bcba6SMarcus Wolf return retval; 112983e3e2efSValentin Vidic } 113083e3e2efSValentin Vidic 1131874bcba6SMarcus Wolf dev_dbg(&spi->dev, 1132874bcba6SMarcus Wolf "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed", 1133874bcba6SMarcus Wolf spi->mode, spi->bits_per_word, spi->max_speed_hz); 1134874bcba6SMarcus Wolf 1135874bcba6SMarcus Wolf /* Ping the chip by reading the version register */ 1136874bcba6SMarcus Wolf retval = spi_w8r8(spi, 0x10); 1137874bcba6SMarcus Wolf if (retval < 0) 1138874bcba6SMarcus Wolf return retval; 1139874bcba6SMarcus Wolf 11406feb5c82SXiangyang Zhang switch (retval) { 1141874bcba6SMarcus Wolf case 0x24: 114228eb8555SColin Ian King dev_dbg(&spi->dev, "found pi433 (ver. 0x%x)", retval); 1143874bcba6SMarcus Wolf break; 1144874bcba6SMarcus Wolf default: 1145874bcba6SMarcus Wolf dev_dbg(&spi->dev, "unknown chip version: 0x%x", retval); 1146874bcba6SMarcus Wolf return -ENODEV; 1147874bcba6SMarcus Wolf } 1148874bcba6SMarcus Wolf 1149874bcba6SMarcus Wolf /* Allocate driver data */ 1150874bcba6SMarcus Wolf device = kzalloc(sizeof(*device), GFP_KERNEL); 1151874bcba6SMarcus Wolf if (!device) 1152874bcba6SMarcus Wolf return -ENOMEM; 1153874bcba6SMarcus Wolf 1154874bcba6SMarcus Wolf /* Initialize the driver data */ 1155874bcba6SMarcus Wolf device->spi = spi; 1156874bcba6SMarcus Wolf device->rx_active = false; 1157874bcba6SMarcus Wolf device->tx_active = false; 1158874bcba6SMarcus Wolf device->interrupt_rx_allowed = false; 1159874bcba6SMarcus Wolf 1160983000d7SHugo Lefeuvre /* init rx buffer */ 1161983000d7SHugo Lefeuvre device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL); 1162983000d7SHugo Lefeuvre if (!device->rx_buffer) { 1163983000d7SHugo Lefeuvre retval = -ENOMEM; 1164983000d7SHugo Lefeuvre goto RX_failed; 1165983000d7SHugo Lefeuvre } 1166983000d7SHugo Lefeuvre 1167874bcba6SMarcus Wolf /* init wait queues */ 1168874bcba6SMarcus Wolf init_waitqueue_head(&device->tx_wait_queue); 1169874bcba6SMarcus Wolf init_waitqueue_head(&device->rx_wait_queue); 1170874bcba6SMarcus Wolf init_waitqueue_head(&device->fifo_wait_queue); 1171874bcba6SMarcus Wolf 1172874bcba6SMarcus Wolf /* init fifo */ 1173874bcba6SMarcus Wolf INIT_KFIFO(device->tx_fifo); 1174874bcba6SMarcus Wolf 1175874bcba6SMarcus Wolf /* init mutexes and locks */ 1176874bcba6SMarcus Wolf mutex_init(&device->tx_fifo_lock); 1177874bcba6SMarcus Wolf mutex_init(&device->rx_lock); 1178874bcba6SMarcus Wolf 1179874bcba6SMarcus Wolf /* setup GPIO (including irq_handler) for the different DIOs */ 1180469b84f3SValentin Vidic retval = setup_gpio(device); 118169af5d92SSrishti Sharma if (retval) { 1182874bcba6SMarcus Wolf dev_dbg(&spi->dev, "setup of GPIOs failed"); 1183874bcba6SMarcus Wolf goto GPIO_failed; 1184874bcba6SMarcus Wolf } 1185874bcba6SMarcus Wolf 1186874bcba6SMarcus Wolf /* setup the radio module */ 118754fc95a7SSimon Sandström retval = rf69_set_mode(spi, standby); 118854fc95a7SSimon Sandström if (retval < 0) 118954fc95a7SSimon Sandström goto minor_failed; 119054fc95a7SSimon Sandström retval = rf69_set_data_mode(spi, DATAMODUL_MODE_PACKET); 119154fc95a7SSimon Sandström if (retval < 0) 119254fc95a7SSimon Sandström goto minor_failed; 119354fc95a7SSimon Sandström retval = rf69_enable_amplifier(spi, MASK_PALEVEL_PA0); 119454fc95a7SSimon Sandström if (retval < 0) 119554fc95a7SSimon Sandström goto minor_failed; 119654fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA1); 119754fc95a7SSimon Sandström if (retval < 0) 119854fc95a7SSimon Sandström goto minor_failed; 119954fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA2); 120054fc95a7SSimon Sandström if (retval < 0) 120154fc95a7SSimon Sandström goto minor_failed; 120254fc95a7SSimon Sandström retval = rf69_set_output_power_level(spi, 13); 120354fc95a7SSimon Sandström if (retval < 0) 120454fc95a7SSimon Sandström goto minor_failed; 12050b897065SValentin Vidic retval = rf69_set_antenna_impedance(spi, fifty_ohm); 120654fc95a7SSimon Sandström if (retval < 0) 120754fc95a7SSimon Sandström goto minor_failed; 1208874bcba6SMarcus Wolf 1209874bcba6SMarcus Wolf /* determ minor number */ 1210874bcba6SMarcus Wolf retval = pi433_get_minor(device); 121169af5d92SSrishti Sharma if (retval) { 1212d2cb4845SMarcin Ciupak dev_dbg(&spi->dev, "get of minor number failed"); 1213874bcba6SMarcus Wolf goto minor_failed; 1214874bcba6SMarcus Wolf } 1215874bcba6SMarcus Wolf 1216874bcba6SMarcus Wolf /* create device */ 1217874bcba6SMarcus Wolf device->devt = MKDEV(MAJOR(pi433_dev), device->minor); 1218874bcba6SMarcus Wolf device->dev = device_create(pi433_class, 1219874bcba6SMarcus Wolf &spi->dev, 1220874bcba6SMarcus Wolf device->devt, 1221874bcba6SMarcus Wolf device, 122299ee4774SMarcin Ciupak "pi433.%d", 122399ee4774SMarcin Ciupak device->minor); 1224874bcba6SMarcus Wolf if (IS_ERR(device->dev)) { 1225874bcba6SMarcus Wolf pr_err("pi433: device register failed\n"); 1226874bcba6SMarcus Wolf retval = PTR_ERR(device->dev); 1227874bcba6SMarcus Wolf goto device_create_failed; 122891086b82SValentin Vidic } else { 1229874bcba6SMarcus Wolf dev_dbg(device->dev, 1230874bcba6SMarcus Wolf "created device for major %d, minor %d\n", 1231874bcba6SMarcus Wolf MAJOR(pi433_dev), 1232874bcba6SMarcus Wolf device->minor); 1233874bcba6SMarcus Wolf } 1234874bcba6SMarcus Wolf 1235d2cb4845SMarcin Ciupak /* start tx thread */ 1236d2cb4845SMarcin Ciupak device->tx_task_struct = kthread_run(pi433_tx_thread, 1237d2cb4845SMarcin Ciupak device, 123899ee4774SMarcin Ciupak "pi433.%d_tx_task", 123999ee4774SMarcin Ciupak device->minor); 1240d2cb4845SMarcin Ciupak if (IS_ERR(device->tx_task_struct)) { 1241d2cb4845SMarcin Ciupak dev_dbg(device->dev, "start of send thread failed"); 12421ba60ad5SWei Yongjun retval = PTR_ERR(device->tx_task_struct); 1243d2cb4845SMarcin Ciupak goto send_thread_failed; 1244d2cb4845SMarcin Ciupak } 1245d2cb4845SMarcin Ciupak 1246874bcba6SMarcus Wolf /* create cdev */ 1247874bcba6SMarcus Wolf device->cdev = cdev_alloc(); 124864c4c4caSMichael Straube if (!device->cdev) { 124964c4c4caSMichael Straube dev_dbg(device->dev, "allocation of cdev failed"); 125064c4c4caSMichael Straube goto cdev_failed; 125164c4c4caSMichael Straube } 1252874bcba6SMarcus Wolf device->cdev->owner = THIS_MODULE; 1253874bcba6SMarcus Wolf cdev_init(device->cdev, &pi433_fops); 1254874bcba6SMarcus Wolf retval = cdev_add(device->cdev, device->devt, 1); 125569af5d92SSrishti Sharma if (retval) { 1256874bcba6SMarcus Wolf dev_dbg(device->dev, "register of cdev failed"); 1257e086f614SMichael Straube goto del_cdev; 1258874bcba6SMarcus Wolf } 1259874bcba6SMarcus Wolf 1260874bcba6SMarcus Wolf /* spi setup */ 1261874bcba6SMarcus Wolf spi_set_drvdata(spi, device); 1262874bcba6SMarcus Wolf 1263874bcba6SMarcus Wolf return 0; 1264874bcba6SMarcus Wolf 1265e086f614SMichael Straube del_cdev: 1266e086f614SMichael Straube cdev_del(device->cdev); 1267874bcba6SMarcus Wolf cdev_failed: 1268d2cb4845SMarcin Ciupak kthread_stop(device->tx_task_struct); 1269d2cb4845SMarcin Ciupak send_thread_failed: 1270874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1271874bcba6SMarcus Wolf device_create_failed: 1272874bcba6SMarcus Wolf pi433_free_minor(device); 1273874bcba6SMarcus Wolf minor_failed: 1274469b84f3SValentin Vidic free_gpio(device); 1275874bcba6SMarcus Wolf GPIO_failed: 1276983000d7SHugo Lefeuvre kfree(device->rx_buffer); 1277983000d7SHugo Lefeuvre RX_failed: 1278874bcba6SMarcus Wolf kfree(device); 1279874bcba6SMarcus Wolf 1280874bcba6SMarcus Wolf return retval; 1281874bcba6SMarcus Wolf } 1282874bcba6SMarcus Wolf 1283874bcba6SMarcus Wolf static int pi433_remove(struct spi_device *spi) 1284874bcba6SMarcus Wolf { 1285874bcba6SMarcus Wolf struct pi433_device *device = spi_get_drvdata(spi); 1286874bcba6SMarcus Wolf 1287874bcba6SMarcus Wolf /* free GPIOs */ 1288469b84f3SValentin Vidic free_gpio(device); 1289874bcba6SMarcus Wolf 1290874bcba6SMarcus Wolf /* make sure ops on existing fds can abort cleanly */ 1291874bcba6SMarcus Wolf device->spi = NULL; 1292874bcba6SMarcus Wolf 1293874bcba6SMarcus Wolf kthread_stop(device->tx_task_struct); 1294874bcba6SMarcus Wolf 1295874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1296874bcba6SMarcus Wolf 1297874bcba6SMarcus Wolf cdev_del(device->cdev); 1298874bcba6SMarcus Wolf 1299874bcba6SMarcus Wolf pi433_free_minor(device); 1300874bcba6SMarcus Wolf 1301983000d7SHugo Lefeuvre kfree(device->rx_buffer); 1302874bcba6SMarcus Wolf kfree(device); 1303874bcba6SMarcus Wolf 1304874bcba6SMarcus Wolf return 0; 1305874bcba6SMarcus Wolf } 1306874bcba6SMarcus Wolf 1307874bcba6SMarcus Wolf static const struct of_device_id pi433_dt_ids[] = { 1308874bcba6SMarcus Wolf { .compatible = "Smarthome-Wolf,pi433" }, 1309874bcba6SMarcus Wolf {}, 1310874bcba6SMarcus Wolf }; 1311874bcba6SMarcus Wolf 1312874bcba6SMarcus Wolf MODULE_DEVICE_TABLE(of, pi433_dt_ids); 1313874bcba6SMarcus Wolf 1314874bcba6SMarcus Wolf static struct spi_driver pi433_spi_driver = { 1315874bcba6SMarcus Wolf .driver = { 1316874bcba6SMarcus Wolf .name = "pi433", 1317874bcba6SMarcus Wolf .owner = THIS_MODULE, 1318874bcba6SMarcus Wolf .of_match_table = of_match_ptr(pi433_dt_ids), 1319874bcba6SMarcus Wolf }, 1320874bcba6SMarcus Wolf .probe = pi433_probe, 1321874bcba6SMarcus Wolf .remove = pi433_remove, 1322874bcba6SMarcus Wolf 132336892816SSophie Matter /* 132436892816SSophie Matter * NOTE: suspend/resume methods are not necessary here. 1325874bcba6SMarcus Wolf * We don't do anything except pass the requests to/from 1326874bcba6SMarcus Wolf * the underlying controller. The refrigerator handles 1327874bcba6SMarcus Wolf * most issues; the controller driver handles the rest. 1328874bcba6SMarcus Wolf */ 1329874bcba6SMarcus Wolf }; 1330874bcba6SMarcus Wolf 1331874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1332874bcba6SMarcus Wolf 1333874bcba6SMarcus Wolf static int __init pi433_init(void) 1334874bcba6SMarcus Wolf { 1335874bcba6SMarcus Wolf int status; 1336874bcba6SMarcus Wolf 133736892816SSophie Matter /* 133836892816SSophie Matter * If MAX_MSG_SIZE is smaller then FIFO_SIZE, the driver won't 1339056eeda2SDerek Robson * work stable - risk of buffer overflow 1340056eeda2SDerek Robson */ 1341874bcba6SMarcus Wolf if (MAX_MSG_SIZE < FIFO_SIZE) 1342874bcba6SMarcus Wolf return -EINVAL; 1343874bcba6SMarcus Wolf 134436892816SSophie Matter /* 134536892816SSophie Matter * Claim device numbers. Then register a class 1346874bcba6SMarcus Wolf * that will key udev/mdev to add/remove /dev nodes. Last, register 1347874bcba6SMarcus Wolf * Last, register the driver which manages those device numbers. 1348874bcba6SMarcus Wolf */ 1349c7265435SHariPrasath Elango status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433"); 1350874bcba6SMarcus Wolf if (status < 0) 1351874bcba6SMarcus Wolf return status; 1352874bcba6SMarcus Wolf 1353874bcba6SMarcus Wolf pi433_class = class_create(THIS_MODULE, "pi433"); 135469af5d92SSrishti Sharma if (IS_ERR(pi433_class)) { 13559be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13569be5755cSOliver Graute pi433_spi_driver.driver.name); 1357874bcba6SMarcus Wolf return PTR_ERR(pi433_class); 1358874bcba6SMarcus Wolf } 1359874bcba6SMarcus Wolf 1360874bcba6SMarcus Wolf status = spi_register_driver(&pi433_spi_driver); 136169af5d92SSrishti Sharma if (status < 0) { 1362874bcba6SMarcus Wolf class_destroy(pi433_class); 13639be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13649be5755cSOliver Graute pi433_spi_driver.driver.name); 1365874bcba6SMarcus Wolf } 1366874bcba6SMarcus Wolf 1367874bcba6SMarcus Wolf return status; 1368874bcba6SMarcus Wolf } 1369874bcba6SMarcus Wolf 1370874bcba6SMarcus Wolf module_init(pi433_init); 1371874bcba6SMarcus Wolf 1372874bcba6SMarcus Wolf static void __exit pi433_exit(void) 1373874bcba6SMarcus Wolf { 1374874bcba6SMarcus Wolf spi_unregister_driver(&pi433_spi_driver); 1375874bcba6SMarcus Wolf class_destroy(pi433_class); 1376874bcba6SMarcus Wolf unregister_chrdev(MAJOR(pi433_dev), pi433_spi_driver.driver.name); 1377874bcba6SMarcus Wolf } 1378874bcba6SMarcus Wolf module_exit(pi433_exit); 1379874bcba6SMarcus Wolf 1380874bcba6SMarcus Wolf MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>"); 1381874bcba6SMarcus Wolf MODULE_DESCRIPTION("Driver for Pi433"); 1382874bcba6SMarcus Wolf MODULE_LICENSE("GPL"); 1383874bcba6SMarcus Wolf MODULE_ALIAS("spi:pi433"); 1384