1874bcba6SMarcus Wolf /* 2874bcba6SMarcus Wolf * userspace interface for pi433 radio module 3874bcba6SMarcus Wolf * 4874bcba6SMarcus Wolf * Pi433 is a 433MHz radio module for the Raspberry Pi. 5874bcba6SMarcus Wolf * It is based on the HopeRf Module RFM69CW. Therefore inside of this 6874bcba6SMarcus Wolf * driver, you'll find an abstraction of the rf69 chip. 7874bcba6SMarcus Wolf * 8874bcba6SMarcus Wolf * If needed, this driver could be extended, to also support other 9874bcba6SMarcus Wolf * devices, basing on HopeRfs rf69. 10874bcba6SMarcus Wolf * 11874bcba6SMarcus Wolf * The driver can also be extended, to support other modules of 12874bcba6SMarcus Wolf * HopeRf with a similar interace - e. g. RFM69HCW, RFM12, RFM95, ... 13874bcba6SMarcus Wolf * 14874bcba6SMarcus Wolf * Copyright (C) 2016 Wolf-Entwicklungen 15874bcba6SMarcus Wolf * Marcus Wolf <linux@wolf-entwicklungen.de> 16874bcba6SMarcus Wolf * 17874bcba6SMarcus Wolf * This program is free software; you can redistribute it and/or modify 18874bcba6SMarcus Wolf * it under the terms of the GNU General Public License as published by 19874bcba6SMarcus Wolf * the Free Software Foundation; either version 2 of the License, or 20874bcba6SMarcus Wolf * (at your option) any later version. 21874bcba6SMarcus Wolf * 22874bcba6SMarcus Wolf * This program is distributed in the hope that it will be useful, 23874bcba6SMarcus Wolf * but WITHOUT ANY WARRANTY; without even the implied warranty of 24874bcba6SMarcus Wolf * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25874bcba6SMarcus Wolf * GNU General Public License for more details. 26874bcba6SMarcus Wolf */ 27874bcba6SMarcus Wolf 28874bcba6SMarcus Wolf #undef DEBUG 29874bcba6SMarcus Wolf 30874bcba6SMarcus Wolf #include <linux/init.h> 31874bcba6SMarcus Wolf #include <linux/module.h> 32874bcba6SMarcus Wolf #include <linux/idr.h> 33874bcba6SMarcus Wolf #include <linux/ioctl.h> 34874bcba6SMarcus Wolf #include <linux/uaccess.h> 35874bcba6SMarcus Wolf #include <linux/fs.h> 36874bcba6SMarcus Wolf #include <linux/device.h> 37874bcba6SMarcus Wolf #include <linux/cdev.h> 38874bcba6SMarcus Wolf #include <linux/err.h> 39874bcba6SMarcus Wolf #include <linux/kfifo.h> 40874bcba6SMarcus Wolf #include <linux/errno.h> 41874bcba6SMarcus Wolf #include <linux/mutex.h> 42874bcba6SMarcus Wolf #include <linux/of.h> 43874bcba6SMarcus Wolf #include <linux/of_device.h> 44874bcba6SMarcus Wolf #include <linux/interrupt.h> 45874bcba6SMarcus Wolf #include <linux/irq.h> 46874bcba6SMarcus Wolf #include <linux/gpio/consumer.h> 47874bcba6SMarcus Wolf #include <linux/kthread.h> 48874bcba6SMarcus Wolf #include <linux/wait.h> 49874bcba6SMarcus Wolf #include <linux/spi/spi.h> 50874bcba6SMarcus Wolf #ifdef CONFIG_COMPAT 517b4c570eSDerek Robson #include <linux/compat.h> 52874bcba6SMarcus Wolf #endif 53874bcba6SMarcus Wolf 54874bcba6SMarcus Wolf #include "pi433_if.h" 55874bcba6SMarcus Wolf #include "rf69.h" 56874bcba6SMarcus Wolf 579ab7bc50SValentin Vidic #define N_PI433_MINORS BIT(MINORBITS) /*32*/ /* ... up to 256 */ 58874bcba6SMarcus Wolf #define MAX_MSG_SIZE 900 /* min: FIFO_SIZE! */ 59874bcba6SMarcus Wolf #define MSG_FIFO_SIZE 65536 /* 65536 = 2^16 */ 60874bcba6SMarcus Wolf #define NUM_DIO 2 61874bcba6SMarcus Wolf 62874bcba6SMarcus Wolf static dev_t pi433_dev; 63874bcba6SMarcus Wolf static DEFINE_IDR(pi433_idr); 64874bcba6SMarcus Wolf static DEFINE_MUTEX(minor_lock); /* Protect idr accesses */ 65874bcba6SMarcus Wolf 66874bcba6SMarcus Wolf static struct class *pi433_class; /* mainly for udev to create /dev/pi433 */ 67874bcba6SMarcus Wolf 68874bcba6SMarcus Wolf /* tx config is instance specific 69056eeda2SDerek Robson * so with each open a new tx config struct is needed 70056eeda2SDerek Robson */ 71874bcba6SMarcus Wolf /* rx config is device specific 72056eeda2SDerek Robson * so we have just one rx config, ebedded in device struct 73056eeda2SDerek Robson */ 74874bcba6SMarcus Wolf struct pi433_device { 75874bcba6SMarcus Wolf /* device handling related values */ 76874bcba6SMarcus Wolf dev_t devt; 77874bcba6SMarcus Wolf int minor; 78874bcba6SMarcus Wolf struct device *dev; 79874bcba6SMarcus Wolf struct cdev *cdev; 80874bcba6SMarcus Wolf struct spi_device *spi; 81211c2820SValentin Vidic unsigned int users; 82874bcba6SMarcus Wolf 83874bcba6SMarcus Wolf /* irq related values */ 84874bcba6SMarcus Wolf struct gpio_desc *gpiod[NUM_DIO]; 85874bcba6SMarcus Wolf int irq_num[NUM_DIO]; 86874bcba6SMarcus Wolf u8 irq_state[NUM_DIO]; 87874bcba6SMarcus Wolf 88874bcba6SMarcus Wolf /* tx related values */ 89874bcba6SMarcus Wolf STRUCT_KFIFO_REC_1(MSG_FIFO_SIZE) tx_fifo; 905451dab9SValentin Vidic struct mutex tx_fifo_lock; /* serialize userspace writers */ 91874bcba6SMarcus Wolf struct task_struct *tx_task_struct; 92874bcba6SMarcus Wolf wait_queue_head_t tx_wait_queue; 93874bcba6SMarcus Wolf u8 free_in_fifo; 9462f39d49SArnd Bergmann char buffer[MAX_MSG_SIZE]; 95874bcba6SMarcus Wolf 96874bcba6SMarcus Wolf /* rx related values */ 97874bcba6SMarcus Wolf struct pi433_rx_cfg rx_cfg; 98874bcba6SMarcus Wolf u8 *rx_buffer; 99874bcba6SMarcus Wolf unsigned int rx_buffer_size; 100874bcba6SMarcus Wolf u32 rx_bytes_to_drop; 101874bcba6SMarcus Wolf u32 rx_bytes_dropped; 102874bcba6SMarcus Wolf unsigned int rx_position; 103874bcba6SMarcus Wolf struct mutex rx_lock; 104874bcba6SMarcus Wolf wait_queue_head_t rx_wait_queue; 105874bcba6SMarcus Wolf 106874bcba6SMarcus Wolf /* fifo wait queue */ 107874bcba6SMarcus Wolf struct task_struct *fifo_task_struct; 108874bcba6SMarcus Wolf wait_queue_head_t fifo_wait_queue; 109874bcba6SMarcus Wolf 110874bcba6SMarcus Wolf /* flags */ 111874bcba6SMarcus Wolf bool rx_active; 112874bcba6SMarcus Wolf bool tx_active; 113874bcba6SMarcus Wolf bool interrupt_rx_allowed; 114874bcba6SMarcus Wolf }; 115874bcba6SMarcus Wolf 116874bcba6SMarcus Wolf struct pi433_instance { 117874bcba6SMarcus Wolf struct pi433_device *device; 118874bcba6SMarcus Wolf struct pi433_tx_cfg tx_cfg; 119874bcba6SMarcus Wolf }; 120874bcba6SMarcus Wolf 121874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 122874bcba6SMarcus Wolf 123874bcba6SMarcus Wolf /* GPIO interrupt handlers */ 124ee26e236SCihangir Akturk static irqreturn_t DIO0_irq_handler(int irq, void *dev_id) 125874bcba6SMarcus Wolf { 126874bcba6SMarcus Wolf struct pi433_device *device = dev_id; 127874bcba6SMarcus Wolf 12891086b82SValentin Vidic if (device->irq_state[DIO0] == DIO_PACKET_SENT) { 129874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 1305a60d7baSHaneen Mohammed dev_dbg(device->dev, "DIO0 irq: Packet sent\n"); 131874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 13291086b82SValentin Vidic } else if (device->irq_state[DIO0] == DIO_RSSI_DIO0) { 1335a60d7baSHaneen Mohammed dev_dbg(device->dev, "DIO0 irq: RSSI level over threshold\n"); 134874bcba6SMarcus Wolf wake_up_interruptible(&device->rx_wait_queue); 13591086b82SValentin Vidic } else if (device->irq_state[DIO0] == DIO_PAYLOAD_READY) { 1363d7f3bf2SSimon Sandström dev_dbg(device->dev, "DIO0 irq: Payload ready\n"); 137874bcba6SMarcus Wolf device->free_in_fifo = 0; 138874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 139874bcba6SMarcus Wolf } 140874bcba6SMarcus Wolf 141ee26e236SCihangir Akturk return IRQ_HANDLED; 142874bcba6SMarcus Wolf } 143874bcba6SMarcus Wolf 144ee26e236SCihangir Akturk static irqreturn_t DIO1_irq_handler(int irq, void *dev_id) 145874bcba6SMarcus Wolf { 146874bcba6SMarcus Wolf struct pi433_device *device = dev_id; 147874bcba6SMarcus Wolf 14891086b82SValentin Vidic if (device->irq_state[DIO1] == DIO_FIFO_NOT_EMPTY_DIO1) { 149874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 15091086b82SValentin Vidic } else if (device->irq_state[DIO1] == DIO_FIFO_LEVEL) { 1516b167a67SValentin Vidic if (device->rx_active) 1526b167a67SValentin Vidic device->free_in_fifo = FIFO_THRESHOLD - 1; 1536b167a67SValentin Vidic else 1546b167a67SValentin Vidic device->free_in_fifo = FIFO_SIZE - FIFO_THRESHOLD - 1; 155874bcba6SMarcus Wolf } 1565a60d7baSHaneen Mohammed dev_dbg(device->dev, 1575a60d7baSHaneen Mohammed "DIO1 irq: %d bytes free in fifo\n", device->free_in_fifo); 158874bcba6SMarcus Wolf wake_up_interruptible(&device->fifo_wait_queue); 159874bcba6SMarcus Wolf 160ee26e236SCihangir Akturk return IRQ_HANDLED; 161874bcba6SMarcus Wolf } 162874bcba6SMarcus Wolf 163874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 164874bcba6SMarcus Wolf 165874bcba6SMarcus Wolf static int 166874bcba6SMarcus Wolf rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg) 167874bcba6SMarcus Wolf { 168125a452cSElia Geretto int ret; 169874bcba6SMarcus Wolf int payload_length; 170874bcba6SMarcus Wolf 171874bcba6SMarcus Wolf /* receiver config */ 172038a5b4eSNguyen Phan Quang Minh ret = rf69_set_frequency(dev->spi, rx_cfg->frequency); 173038a5b4eSNguyen Phan Quang Minh if (ret < 0) 174038a5b4eSNguyen Phan Quang Minh return ret; 175038a5b4eSNguyen Phan Quang Minh ret = rf69_set_bit_rate(dev->spi, rx_cfg->bit_rate); 176038a5b4eSNguyen Phan Quang Minh if (ret < 0) 177038a5b4eSNguyen Phan Quang Minh return ret; 178038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation(dev->spi, rx_cfg->modulation); 179038a5b4eSNguyen Phan Quang Minh if (ret < 0) 180038a5b4eSNguyen Phan Quang Minh return ret; 181038a5b4eSNguyen Phan Quang Minh ret = rf69_set_antenna_impedance(dev->spi, rx_cfg->antenna_impedance); 182038a5b4eSNguyen Phan Quang Minh if (ret < 0) 183038a5b4eSNguyen Phan Quang Minh return ret; 184038a5b4eSNguyen Phan Quang Minh ret = rf69_set_rssi_threshold(dev->spi, rx_cfg->rssi_threshold); 185038a5b4eSNguyen Phan Quang Minh if (ret < 0) 186038a5b4eSNguyen Phan Quang Minh return ret; 187038a5b4eSNguyen Phan Quang Minh ret = rf69_set_ook_threshold_dec(dev->spi, rx_cfg->threshold_decrement); 188038a5b4eSNguyen Phan Quang Minh if (ret < 0) 189038a5b4eSNguyen Phan Quang Minh return ret; 19020e5f042SEisha Chen-yen-su ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse, 19120e5f042SEisha Chen-yen-su rx_cfg->bw_exponent); 192038a5b4eSNguyen Phan Quang Minh if (ret < 0) 193038a5b4eSNguyen Phan Quang Minh return ret; 19420e5f042SEisha Chen-yen-su ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse, 19520e5f042SEisha Chen-yen-su rx_cfg->bw_exponent); 196038a5b4eSNguyen Phan Quang Minh if (ret < 0) 197038a5b4eSNguyen Phan Quang Minh return ret; 198038a5b4eSNguyen Phan Quang Minh ret = rf69_set_dagc(dev->spi, rx_cfg->dagc); 199038a5b4eSNguyen Phan Quang Minh if (ret < 0) 200038a5b4eSNguyen Phan Quang Minh return ret; 201874bcba6SMarcus Wolf 202874bcba6SMarcus Wolf dev->rx_bytes_to_drop = rx_cfg->bytes_to_drop; 203874bcba6SMarcus Wolf 204874bcba6SMarcus Wolf /* packet config */ 205874bcba6SMarcus Wolf /* enable */ 20691086b82SValentin Vidic if (rx_cfg->enable_sync == OPTION_ON) { 207966debe0SSimon Sandström ret = rf69_enable_sync(dev->spi); 208966debe0SSimon Sandström if (ret < 0) 209966debe0SSimon Sandström return ret; 210966debe0SSimon Sandström 21120e5f042SEisha Chen-yen-su ret = rf69_set_fifo_fill_condition(dev->spi, 21253e0b83dSValentin Vidic after_sync_interrupt); 213038a5b4eSNguyen Phan Quang Minh if (ret < 0) 214038a5b4eSNguyen Phan Quang Minh return ret; 21591086b82SValentin Vidic } else { 216966debe0SSimon Sandström ret = rf69_disable_sync(dev->spi); 217966debe0SSimon Sandström if (ret < 0) 218966debe0SSimon Sandström return ret; 219966debe0SSimon Sandström 220038a5b4eSNguyen Phan Quang Minh ret = rf69_set_fifo_fill_condition(dev->spi, always); 221038a5b4eSNguyen Phan Quang Minh if (ret < 0) 222038a5b4eSNguyen Phan Quang Minh return ret; 223874bcba6SMarcus Wolf } 224d423c809SSimon Sandström if (rx_cfg->enable_length_byte == OPTION_ON) { 225c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_var); 226125a452cSElia Geretto if (ret < 0) 227125a452cSElia Geretto return ret; 228125a452cSElia Geretto } else { 229c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_fix); 230125a452cSElia Geretto if (ret < 0) 231125a452cSElia Geretto return ret; 232125a452cSElia Geretto } 233e69a0500SValentin Vidic ret = rf69_set_address_filtering(dev->spi, 23420e5f042SEisha Chen-yen-su rx_cfg->enable_address_filtering); 235038a5b4eSNguyen Phan Quang Minh if (ret < 0) 236038a5b4eSNguyen Phan Quang Minh return ret; 23739252a4bSSimon Sandström 23839252a4bSSimon Sandström if (rx_cfg->enable_crc == OPTION_ON) { 23939252a4bSSimon Sandström ret = rf69_enable_crc(dev->spi); 24039252a4bSSimon Sandström if (ret < 0) 24139252a4bSSimon Sandström return ret; 24239252a4bSSimon Sandström } else { 24339252a4bSSimon Sandström ret = rf69_disable_crc(dev->spi); 24439252a4bSSimon Sandström if (ret < 0) 24539252a4bSSimon Sandström return ret; 24639252a4bSSimon Sandström } 247874bcba6SMarcus Wolf 248874bcba6SMarcus Wolf /* lengths */ 249038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_size(dev->spi, rx_cfg->sync_length); 250038a5b4eSNguyen Phan Quang Minh if (ret < 0) 251038a5b4eSNguyen Phan Quang Minh return ret; 25291086b82SValentin Vidic if (rx_cfg->enable_length_byte == OPTION_ON) { 253038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, 0xff); 254038a5b4eSNguyen Phan Quang Minh if (ret < 0) 255038a5b4eSNguyen Phan Quang Minh return ret; 25691086b82SValentin Vidic } else if (rx_cfg->fixed_message_length != 0) { 257874bcba6SMarcus Wolf payload_length = rx_cfg->fixed_message_length; 2586b167a67SValentin Vidic if (rx_cfg->enable_length_byte == OPTION_ON) 2596b167a67SValentin Vidic payload_length++; 260cd9d5291SValentin Vidic if (rx_cfg->enable_address_filtering != filtering_off) 2616b167a67SValentin Vidic payload_length++; 262038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, payload_length); 263038a5b4eSNguyen Phan Quang Minh if (ret < 0) 264038a5b4eSNguyen Phan Quang Minh return ret; 26591086b82SValentin Vidic } else { 266038a5b4eSNguyen Phan Quang Minh ret = rf69_set_payload_length(dev->spi, 0); 267038a5b4eSNguyen Phan Quang Minh if (ret < 0) 268038a5b4eSNguyen Phan Quang Minh return ret; 269874bcba6SMarcus Wolf } 270874bcba6SMarcus Wolf 271874bcba6SMarcus Wolf /* values */ 27291086b82SValentin Vidic if (rx_cfg->enable_sync == OPTION_ON) { 273038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_values(dev->spi, rx_cfg->sync_pattern); 274038a5b4eSNguyen Phan Quang Minh if (ret < 0) 275038a5b4eSNguyen Phan Quang Minh return ret; 276874bcba6SMarcus Wolf } 277cd9d5291SValentin Vidic if (rx_cfg->enable_address_filtering != filtering_off) { 278038a5b4eSNguyen Phan Quang Minh ret = rf69_set_node_address(dev->spi, rx_cfg->node_address); 279038a5b4eSNguyen Phan Quang Minh if (ret < 0) 280038a5b4eSNguyen Phan Quang Minh return ret; 28120e5f042SEisha Chen-yen-su ret = rf69_set_broadcast_address(dev->spi, 28220e5f042SEisha Chen-yen-su rx_cfg->broadcast_address); 283038a5b4eSNguyen Phan Quang Minh if (ret < 0) 284038a5b4eSNguyen Phan Quang Minh return ret; 285874bcba6SMarcus Wolf } 286874bcba6SMarcus Wolf 287874bcba6SMarcus Wolf return 0; 288874bcba6SMarcus Wolf } 289874bcba6SMarcus Wolf 290874bcba6SMarcus Wolf static int 291874bcba6SMarcus Wolf rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg) 292874bcba6SMarcus Wolf { 293125a452cSElia Geretto int ret; 294125a452cSElia Geretto 295038a5b4eSNguyen Phan Quang Minh ret = rf69_set_frequency(dev->spi, tx_cfg->frequency); 296038a5b4eSNguyen Phan Quang Minh if (ret < 0) 297038a5b4eSNguyen Phan Quang Minh return ret; 298038a5b4eSNguyen Phan Quang Minh ret = rf69_set_bit_rate(dev->spi, tx_cfg->bit_rate); 299038a5b4eSNguyen Phan Quang Minh if (ret < 0) 300038a5b4eSNguyen Phan Quang Minh return ret; 301038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation(dev->spi, tx_cfg->modulation); 302038a5b4eSNguyen Phan Quang Minh if (ret < 0) 303038a5b4eSNguyen Phan Quang Minh return ret; 304038a5b4eSNguyen Phan Quang Minh ret = rf69_set_deviation(dev->spi, tx_cfg->dev_frequency); 305038a5b4eSNguyen Phan Quang Minh if (ret < 0) 306038a5b4eSNguyen Phan Quang Minh return ret; 307038a5b4eSNguyen Phan Quang Minh ret = rf69_set_pa_ramp(dev->spi, tx_cfg->pa_ramp); 308038a5b4eSNguyen Phan Quang Minh if (ret < 0) 309038a5b4eSNguyen Phan Quang Minh return ret; 310038a5b4eSNguyen Phan Quang Minh ret = rf69_set_modulation_shaping(dev->spi, tx_cfg->mod_shaping); 311038a5b4eSNguyen Phan Quang Minh if (ret < 0) 312038a5b4eSNguyen Phan Quang Minh return ret; 313038a5b4eSNguyen Phan Quang Minh ret = rf69_set_tx_start_condition(dev->spi, tx_cfg->tx_start_condition); 314038a5b4eSNguyen Phan Quang Minh if (ret < 0) 315038a5b4eSNguyen Phan Quang Minh return ret; 316874bcba6SMarcus Wolf 317874bcba6SMarcus Wolf /* packet format enable */ 31891086b82SValentin Vidic if (tx_cfg->enable_preamble == OPTION_ON) { 31920e5f042SEisha Chen-yen-su ret = rf69_set_preamble_length(dev->spi, 32020e5f042SEisha Chen-yen-su tx_cfg->preamble_length); 321038a5b4eSNguyen Phan Quang Minh if (ret < 0) 322038a5b4eSNguyen Phan Quang Minh return ret; 32391086b82SValentin Vidic } else { 324038a5b4eSNguyen Phan Quang Minh ret = rf69_set_preamble_length(dev->spi, 0); 325038a5b4eSNguyen Phan Quang Minh if (ret < 0) 326038a5b4eSNguyen Phan Quang Minh return ret; 327874bcba6SMarcus Wolf } 328966debe0SSimon Sandström 329966debe0SSimon Sandström if (tx_cfg->enable_sync == OPTION_ON) { 330966debe0SSimon Sandström ret = rf69_enable_sync(dev->spi); 331966debe0SSimon Sandström if (ret < 0) 332966debe0SSimon Sandström return ret; 333966debe0SSimon Sandström } else { 334966debe0SSimon Sandström ret = rf69_disable_sync(dev->spi); 335966debe0SSimon Sandström if (ret < 0) 336966debe0SSimon Sandström return ret; 337966debe0SSimon Sandström } 338966debe0SSimon Sandström 339d423c809SSimon Sandström if (tx_cfg->enable_length_byte == OPTION_ON) { 340c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_var); 341125a452cSElia Geretto if (ret < 0) 342125a452cSElia Geretto return ret; 343125a452cSElia Geretto } else { 344c436ef3bSValentin Vidic ret = rf69_set_packet_format(dev->spi, packet_length_fix); 345125a452cSElia Geretto if (ret < 0) 346125a452cSElia Geretto return ret; 347125a452cSElia Geretto } 34839252a4bSSimon Sandström 34939252a4bSSimon Sandström if (tx_cfg->enable_crc == OPTION_ON) { 35039252a4bSSimon Sandström ret = rf69_enable_crc(dev->spi); 35139252a4bSSimon Sandström if (ret < 0) 35239252a4bSSimon Sandström return ret; 35339252a4bSSimon Sandström } else { 35439252a4bSSimon Sandström ret = rf69_disable_crc(dev->spi); 35539252a4bSSimon Sandström if (ret < 0) 35639252a4bSSimon Sandström return ret; 35739252a4bSSimon Sandström } 358874bcba6SMarcus Wolf 359874bcba6SMarcus Wolf /* configure sync, if enabled */ 360d423c809SSimon Sandström if (tx_cfg->enable_sync == OPTION_ON) { 361038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_size(dev->spi, tx_cfg->sync_length); 362038a5b4eSNguyen Phan Quang Minh if (ret < 0) 363038a5b4eSNguyen Phan Quang Minh return ret; 364038a5b4eSNguyen Phan Quang Minh ret = rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern); 365038a5b4eSNguyen Phan Quang Minh if (ret < 0) 366038a5b4eSNguyen Phan Quang Minh return ret; 367874bcba6SMarcus Wolf } 368874bcba6SMarcus Wolf 369874bcba6SMarcus Wolf return 0; 370874bcba6SMarcus Wolf } 371874bcba6SMarcus Wolf 372874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 373874bcba6SMarcus Wolf 374874bcba6SMarcus Wolf static int 375874bcba6SMarcus Wolf pi433_start_rx(struct pi433_device *dev) 376874bcba6SMarcus Wolf { 377874bcba6SMarcus Wolf int retval; 378874bcba6SMarcus Wolf 379874bcba6SMarcus Wolf /* return without action, if no pending read request */ 380874bcba6SMarcus Wolf if (!dev->rx_active) 381874bcba6SMarcus Wolf return 0; 382874bcba6SMarcus Wolf 383874bcba6SMarcus Wolf /* setup for receiving */ 384874bcba6SMarcus Wolf retval = rf69_set_rx_cfg(dev, &dev->rx_cfg); 3856b167a67SValentin Vidic if (retval) 3866b167a67SValentin Vidic return retval; 387874bcba6SMarcus Wolf 388874bcba6SMarcus Wolf /* setup rssi irq */ 389038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO0, DIO_RSSI_DIO0); 390038a5b4eSNguyen Phan Quang Minh if (retval < 0) 391038a5b4eSNguyen Phan Quang Minh return retval; 3923d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_RSSI_DIO0; 393874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 394874bcba6SMarcus Wolf 395874bcba6SMarcus Wolf /* setup fifo level interrupt */ 396038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD); 397038a5b4eSNguyen Phan Quang Minh if (retval < 0) 398038a5b4eSNguyen Phan Quang Minh return retval; 399038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(dev->spi, DIO1, DIO_FIFO_LEVEL); 400038a5b4eSNguyen Phan Quang Minh if (retval < 0) 401038a5b4eSNguyen Phan Quang Minh return retval; 4023d7f3bf2SSimon Sandström dev->irq_state[DIO1] = DIO_FIFO_LEVEL; 403874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING); 404874bcba6SMarcus Wolf 405874bcba6SMarcus Wolf /* set module to receiving mode */ 406038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(dev->spi, receive); 407038a5b4eSNguyen Phan Quang Minh if (retval < 0) 408038a5b4eSNguyen Phan Quang Minh return retval; 409874bcba6SMarcus Wolf 410874bcba6SMarcus Wolf return 0; 411874bcba6SMarcus Wolf } 412874bcba6SMarcus Wolf 413874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 414874bcba6SMarcus Wolf 4157de77a39SJoseph Wright static int 416874bcba6SMarcus Wolf pi433_receive(void *data) 417874bcba6SMarcus Wolf { 418874bcba6SMarcus Wolf struct pi433_device *dev = data; 419038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = dev->spi; 420874bcba6SMarcus Wolf int bytes_to_read, bytes_total; 421874bcba6SMarcus Wolf int retval; 422874bcba6SMarcus Wolf 423874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 424874bcba6SMarcus Wolf 425874bcba6SMarcus Wolf /* wait for any tx to finish */ 426874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for any tx to finish"); 427874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active); 42891086b82SValentin Vidic if (retval) { 42991086b82SValentin Vidic /* wait was interrupted */ 430874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 431874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 432874bcba6SMarcus Wolf return retval; 433874bcba6SMarcus Wolf } 434874bcba6SMarcus Wolf 435874bcba6SMarcus Wolf /* prepare status vars */ 436874bcba6SMarcus Wolf dev->free_in_fifo = FIFO_SIZE; 437874bcba6SMarcus Wolf dev->rx_position = 0; 438874bcba6SMarcus Wolf dev->rx_bytes_dropped = 0; 439874bcba6SMarcus Wolf 440874bcba6SMarcus Wolf /* setup radio module to listen for something "in the air" */ 441874bcba6SMarcus Wolf retval = pi433_start_rx(dev); 442874bcba6SMarcus Wolf if (retval) 443874bcba6SMarcus Wolf return retval; 444874bcba6SMarcus Wolf 445874bcba6SMarcus Wolf /* now check RSSI, if low wait for getting high (RSSI interrupt) */ 4461cd41fc3SValentin Vidic while (!rf69_get_flag(dev->spi, rssi_exceeded_threshold)) { 447874bcba6SMarcus Wolf /* allow tx to interrupt us while waiting for high RSSI */ 448874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 449874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 450874bcba6SMarcus Wolf 451874bcba6SMarcus Wolf /* wait for RSSI level to become high */ 452874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: going to wait for high RSSI level"); 453874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->rx_wait_queue, 454874bcba6SMarcus Wolf rf69_get_flag(dev->spi, 4551cd41fc3SValentin Vidic rssi_exceeded_threshold)); 4566b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4576b167a67SValentin Vidic goto abort; 458874bcba6SMarcus Wolf dev->interrupt_rx_allowed = false; 459874bcba6SMarcus Wolf 460874bcba6SMarcus Wolf /* cross check for ongoing tx */ 4616b167a67SValentin Vidic if (!dev->tx_active) 4626b167a67SValentin Vidic break; 463874bcba6SMarcus Wolf } 464874bcba6SMarcus Wolf 465874bcba6SMarcus Wolf /* configure payload ready irq */ 466038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PAYLOAD_READY); 467038a5b4eSNguyen Phan Quang Minh if (retval < 0) 468038a5b4eSNguyen Phan Quang Minh goto abort; 4693d7f3bf2SSimon Sandström dev->irq_state[DIO0] = DIO_PAYLOAD_READY; 470874bcba6SMarcus Wolf irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 471874bcba6SMarcus Wolf 472874bcba6SMarcus Wolf /* fixed or unlimited length? */ 47391086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length != 0) { 47491086b82SValentin Vidic if (dev->rx_cfg.fixed_message_length > dev->rx_buffer_size) { 475874bcba6SMarcus Wolf retval = -1; 476874bcba6SMarcus Wolf goto abort; 477874bcba6SMarcus Wolf } 478874bcba6SMarcus Wolf bytes_total = dev->rx_cfg.fixed_message_length; 47920e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d by fixed length", 48020e5f042SEisha Chen-yen-su bytes_total); 48191086b82SValentin Vidic } else { 482874bcba6SMarcus Wolf bytes_total = dev->rx_buffer_size; 48320e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len set to %d as requested by read", 48420e5f042SEisha Chen-yen-su bytes_total); 485874bcba6SMarcus Wolf } 486874bcba6SMarcus Wolf 487874bcba6SMarcus Wolf /* length byte enabled? */ 48891086b82SValentin Vidic if (dev->rx_cfg.enable_length_byte == OPTION_ON) { 489874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 490874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 4916b167a67SValentin Vidic if (retval) /* wait was interrupted */ 4926b167a67SValentin Vidic goto abort; 493874bcba6SMarcus Wolf 494874bcba6SMarcus Wolf rf69_read_fifo(spi, (u8 *)&bytes_total, 1); 49569af5d92SSrishti Sharma if (bytes_total > dev->rx_buffer_size) { 496874bcba6SMarcus Wolf retval = -1; 497874bcba6SMarcus Wolf goto abort; 498874bcba6SMarcus Wolf } 499874bcba6SMarcus Wolf dev->free_in_fifo++; 50020e5f042SEisha Chen-yen-su dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte", 50120e5f042SEisha Chen-yen-su bytes_total); 502874bcba6SMarcus Wolf } 503874bcba6SMarcus Wolf 504874bcba6SMarcus Wolf /* address byte enabled? */ 505cd9d5291SValentin Vidic if (dev->rx_cfg.enable_address_filtering != filtering_off) { 506874bcba6SMarcus Wolf u8 dummy; 507874bcba6SMarcus Wolf 508874bcba6SMarcus Wolf bytes_total--; 509874bcba6SMarcus Wolf 510874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 511874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5126b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5136b167a67SValentin Vidic goto abort; 514874bcba6SMarcus Wolf 515874bcba6SMarcus Wolf rf69_read_fifo(spi, &dummy, 1); 516874bcba6SMarcus Wolf dev->free_in_fifo++; 517874bcba6SMarcus Wolf dev_dbg(dev->dev, "rx: address byte stripped off"); 518874bcba6SMarcus Wolf } 519874bcba6SMarcus Wolf 520874bcba6SMarcus Wolf /* get payload */ 52191086b82SValentin Vidic while (dev->rx_position < bytes_total) { 5220b9acf7dSValentin Vidic if (!rf69_get_flag(dev->spi, payload_ready)) { 523874bcba6SMarcus Wolf retval = wait_event_interruptible(dev->fifo_wait_queue, 524874bcba6SMarcus Wolf dev->free_in_fifo < FIFO_SIZE); 5256b167a67SValentin Vidic if (retval) /* wait was interrupted */ 5266b167a67SValentin Vidic goto abort; 527874bcba6SMarcus Wolf } 528874bcba6SMarcus Wolf 529874bcba6SMarcus Wolf /* need to drop bytes or acquire? */ 530874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 531f1345b2fSEisha Chen-yen-su bytes_to_read = dev->rx_bytes_to_drop - 532f1345b2fSEisha Chen-yen-su dev->rx_bytes_dropped; 533874bcba6SMarcus Wolf else 534874bcba6SMarcus Wolf bytes_to_read = bytes_total - dev->rx_position; 535874bcba6SMarcus Wolf 536874bcba6SMarcus Wolf /* access the fifo */ 537874bcba6SMarcus Wolf if (bytes_to_read > FIFO_SIZE - dev->free_in_fifo) 538874bcba6SMarcus Wolf bytes_to_read = FIFO_SIZE - dev->free_in_fifo; 539874bcba6SMarcus Wolf retval = rf69_read_fifo(spi, 540874bcba6SMarcus Wolf &dev->rx_buffer[dev->rx_position], 541874bcba6SMarcus Wolf bytes_to_read); 5426b167a67SValentin Vidic if (retval) /* read failed */ 5436b167a67SValentin Vidic goto abort; 5446b167a67SValentin Vidic 545874bcba6SMarcus Wolf dev->free_in_fifo += bytes_to_read; 546874bcba6SMarcus Wolf 547874bcba6SMarcus Wolf /* adjust status vars */ 548874bcba6SMarcus Wolf if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) 549874bcba6SMarcus Wolf dev->rx_bytes_dropped += bytes_to_read; 550874bcba6SMarcus Wolf else 551874bcba6SMarcus Wolf dev->rx_position += bytes_to_read; 552874bcba6SMarcus Wolf } 553874bcba6SMarcus Wolf 5542ebd34caSHarsha Sharma /* rx done, wait was interrupted or error occurred */ 555874bcba6SMarcus Wolf abort: 556874bcba6SMarcus Wolf dev->interrupt_rx_allowed = true; 557038a5b4eSNguyen Phan Quang Minh if (rf69_set_mode(dev->spi, standby)) 558038a5b4eSNguyen Phan Quang Minh pr_err("rf69_set_mode(): radio module failed to go standby\n"); 559874bcba6SMarcus Wolf wake_up_interruptible(&dev->tx_wait_queue); 560874bcba6SMarcus Wolf 561874bcba6SMarcus Wolf if (retval) 562874bcba6SMarcus Wolf return retval; 563874bcba6SMarcus Wolf else 564874bcba6SMarcus Wolf return bytes_total; 565874bcba6SMarcus Wolf } 566874bcba6SMarcus Wolf 5677de77a39SJoseph Wright static int 568874bcba6SMarcus Wolf pi433_tx_thread(void *data) 569874bcba6SMarcus Wolf { 570874bcba6SMarcus Wolf struct pi433_device *device = data; 571038a5b4eSNguyen Phan Quang Minh struct spi_device *spi = device->spi; 572874bcba6SMarcus Wolf struct pi433_tx_cfg tx_cfg; 573874bcba6SMarcus Wolf size_t size; 574874bcba6SMarcus Wolf bool rx_interrupted = false; 575874bcba6SMarcus Wolf int position, repetitions; 576874bcba6SMarcus Wolf int retval; 577874bcba6SMarcus Wolf 57891086b82SValentin Vidic while (1) { 579874bcba6SMarcus Wolf /* wait for fifo to be populated or for request to terminate*/ 580874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: going to wait for new messages"); 581874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 582874bcba6SMarcus Wolf (!kfifo_is_empty(&device->tx_fifo) || 583874bcba6SMarcus Wolf kthread_should_stop())); 584874bcba6SMarcus Wolf if (kthread_should_stop()) 585874bcba6SMarcus Wolf return 0; 586874bcba6SMarcus Wolf 587874bcba6SMarcus Wolf /* get data from fifo in the following order: 588056eeda2SDerek Robson * - tx_cfg 589056eeda2SDerek Robson * - size of message 590056eeda2SDerek Robson * - message 591056eeda2SDerek Robson */ 592874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &tx_cfg, sizeof(tx_cfg)); 59369af5d92SSrishti Sharma if (retval != sizeof(tx_cfg)) { 594874bcba6SMarcus Wolf dev_dbg(device->dev, "reading tx_cfg from fifo failed: got %d byte(s), expected %d", retval, (unsigned int)sizeof(tx_cfg)); 595874bcba6SMarcus Wolf continue; 596874bcba6SMarcus Wolf } 597874bcba6SMarcus Wolf 598874bcba6SMarcus Wolf retval = kfifo_out(&device->tx_fifo, &size, sizeof(size_t)); 59969af5d92SSrishti Sharma if (retval != sizeof(size_t)) { 600874bcba6SMarcus Wolf dev_dbg(device->dev, "reading msg size from fifo failed: got %d, expected %d", retval, (unsigned int)sizeof(size_t)); 601874bcba6SMarcus Wolf continue; 602874bcba6SMarcus Wolf } 603874bcba6SMarcus Wolf 604874bcba6SMarcus Wolf /* use fixed message length, if requested */ 605874bcba6SMarcus Wolf if (tx_cfg.fixed_message_length != 0) 606874bcba6SMarcus Wolf size = tx_cfg.fixed_message_length; 607874bcba6SMarcus Wolf 608874bcba6SMarcus Wolf /* increase size, if len byte is requested */ 609d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 610874bcba6SMarcus Wolf size++; 611874bcba6SMarcus Wolf 612874bcba6SMarcus Wolf /* increase size, if adr byte is requested */ 613d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 614874bcba6SMarcus Wolf size++; 615874bcba6SMarcus Wolf 616874bcba6SMarcus Wolf /* prime buffer */ 617da3761feSMarcus Wolf memset(device->buffer, 0, size); 618874bcba6SMarcus Wolf position = 0; 619874bcba6SMarcus Wolf 620874bcba6SMarcus Wolf /* add length byte, if requested */ 621d423c809SSimon Sandström if (tx_cfg.enable_length_byte == OPTION_ON) 622da3761feSMarcus Wolf device->buffer[position++] = size - 1; /* according to spec length byte itself must be excluded from the length calculation */ 623874bcba6SMarcus Wolf 624874bcba6SMarcus Wolf /* add adr byte, if requested */ 625d423c809SSimon Sandström if (tx_cfg.enable_address_byte == OPTION_ON) 626da3761feSMarcus Wolf device->buffer[position++] = tx_cfg.address_byte; 627874bcba6SMarcus Wolf 628874bcba6SMarcus Wolf /* finally get message data from fifo */ 62920e5f042SEisha Chen-yen-su retval = kfifo_out(&device->tx_fifo, &device->buffer[position], 63020e5f042SEisha Chen-yen-su sizeof(device->buffer) - position); 63120e5f042SEisha Chen-yen-su dev_dbg(device->dev, 63220e5f042SEisha Chen-yen-su "read %d message byte(s) from fifo queue.", retval); 633874bcba6SMarcus Wolf 634874bcba6SMarcus Wolf /* if rx is active, we need to interrupt the waiting for 635056eeda2SDerek Robson * incoming telegrams, to be able to send something. 636056eeda2SDerek Robson * We are only allowed, if currently no reception takes 637056eeda2SDerek Robson * place otherwise we need to wait for the incoming telegram 638056eeda2SDerek Robson * to finish 639056eeda2SDerek Robson */ 640874bcba6SMarcus Wolf wait_event_interruptible(device->tx_wait_queue, 641874bcba6SMarcus Wolf !device->rx_active || 6422b3943b3SValentin Vidic device->interrupt_rx_allowed); 643874bcba6SMarcus Wolf 644874bcba6SMarcus Wolf /* prevent race conditions 645056eeda2SDerek Robson * irq will be reenabled after tx config is set 646056eeda2SDerek Robson */ 647874bcba6SMarcus Wolf disable_irq(device->irq_num[DIO0]); 648874bcba6SMarcus Wolf device->tx_active = true; 649874bcba6SMarcus Wolf 650de71b5bdSValentin Vidic if (device->rx_active && !rx_interrupted) { 651874bcba6SMarcus Wolf /* rx is currently waiting for a telegram; 652056eeda2SDerek Robson * we need to set the radio module to standby 653056eeda2SDerek Robson */ 654038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(device->spi, standby); 655038a5b4eSNguyen Phan Quang Minh if (retval < 0) 656038a5b4eSNguyen Phan Quang Minh return retval; 657874bcba6SMarcus Wolf rx_interrupted = true; 658874bcba6SMarcus Wolf } 659874bcba6SMarcus Wolf 660874bcba6SMarcus Wolf /* clear fifo, set fifo threshold, set payload length */ 661038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, standby); /* this clears the fifo */ 662038a5b4eSNguyen Phan Quang Minh if (retval < 0) 663038a5b4eSNguyen Phan Quang Minh return retval; 664038a5b4eSNguyen Phan Quang Minh retval = rf69_set_fifo_threshold(spi, FIFO_THRESHOLD); 665038a5b4eSNguyen Phan Quang Minh if (retval < 0) 666038a5b4eSNguyen Phan Quang Minh return retval; 66791086b82SValentin Vidic if (tx_cfg.enable_length_byte == OPTION_ON) { 668038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, size * tx_cfg.repetitions); 669038a5b4eSNguyen Phan Quang Minh if (retval < 0) 670038a5b4eSNguyen Phan Quang Minh return retval; 67191086b82SValentin Vidic } else { 672038a5b4eSNguyen Phan Quang Minh retval = rf69_set_payload_length(spi, 0); 673038a5b4eSNguyen Phan Quang Minh if (retval < 0) 674038a5b4eSNguyen Phan Quang Minh return retval; 675874bcba6SMarcus Wolf } 676874bcba6SMarcus Wolf 677874bcba6SMarcus Wolf /* configure the rf chip */ 678038a5b4eSNguyen Phan Quang Minh retval = rf69_set_tx_cfg(device, &tx_cfg); 679038a5b4eSNguyen Phan Quang Minh if (retval < 0) 680038a5b4eSNguyen Phan Quang Minh return retval; 681874bcba6SMarcus Wolf 682874bcba6SMarcus Wolf /* enable fifo level interrupt */ 683038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO1, DIO_FIFO_LEVEL); 684038a5b4eSNguyen Phan Quang Minh if (retval < 0) 685038a5b4eSNguyen Phan Quang Minh return retval; 6863d7f3bf2SSimon Sandström device->irq_state[DIO1] = DIO_FIFO_LEVEL; 687874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING); 688874bcba6SMarcus Wolf 689874bcba6SMarcus Wolf /* enable packet sent interrupt */ 690038a5b4eSNguyen Phan Quang Minh retval = rf69_set_dio_mapping(spi, DIO0, DIO_PACKET_SENT); 691038a5b4eSNguyen Phan Quang Minh if (retval < 0) 692038a5b4eSNguyen Phan Quang Minh return retval; 6933d7f3bf2SSimon Sandström device->irq_state[DIO0] = DIO_PACKET_SENT; 694874bcba6SMarcus Wolf irq_set_irq_type(device->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); 695874bcba6SMarcus Wolf enable_irq(device->irq_num[DIO0]); /* was disabled by rx active check */ 696874bcba6SMarcus Wolf 697874bcba6SMarcus Wolf /* enable transmission */ 698038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, transmit); 699038a5b4eSNguyen Phan Quang Minh if (retval < 0) 700038a5b4eSNguyen Phan Quang Minh return retval; 701874bcba6SMarcus Wolf 702874bcba6SMarcus Wolf /* transfer this msg (and repetitions) to chip fifo */ 703874bcba6SMarcus Wolf device->free_in_fifo = FIFO_SIZE; 704874bcba6SMarcus Wolf position = 0; 705874bcba6SMarcus Wolf repetitions = tx_cfg.repetitions; 70691086b82SValentin Vidic while ((repetitions > 0) && (size > position)) { 70791086b82SValentin Vidic if ((size - position) > device->free_in_fifo) { 70891086b82SValentin Vidic /* msg to big for fifo - take a part */ 709dba18d61SValentin Vidic int write_size = device->free_in_fifo; 710dba18d61SValentin Vidic 711874bcba6SMarcus Wolf device->free_in_fifo = 0; 712874bcba6SMarcus Wolf rf69_write_fifo(spi, 713da3761feSMarcus Wolf &device->buffer[position], 714dba18d61SValentin Vidic write_size); 715dba18d61SValentin Vidic position += write_size; 71691086b82SValentin Vidic } else { 71791086b82SValentin Vidic /* msg fits into fifo - take all */ 718874bcba6SMarcus Wolf device->free_in_fifo -= size; 719874bcba6SMarcus Wolf repetitions--; 720874bcba6SMarcus Wolf rf69_write_fifo(spi, 721da3761feSMarcus Wolf &device->buffer[position], 722874bcba6SMarcus Wolf (size - position)); 723874bcba6SMarcus Wolf position = 0; /* reset for next repetition */ 724874bcba6SMarcus Wolf } 725874bcba6SMarcus Wolf 726874bcba6SMarcus Wolf retval = wait_event_interruptible(device->fifo_wait_queue, 727874bcba6SMarcus Wolf device->free_in_fifo > 0); 7286b167a67SValentin Vidic if (retval) { 729ebc1dea2SValentin Vidic dev_dbg(device->dev, "ABORT\n"); 7306b167a67SValentin Vidic goto abort; 7316b167a67SValentin Vidic } 732874bcba6SMarcus Wolf } 733874bcba6SMarcus Wolf 734874bcba6SMarcus Wolf /* we are done. Wait for packet to get sent */ 73520e5f042SEisha Chen-yen-su dev_dbg(device->dev, 73620e5f042SEisha Chen-yen-su "thread: wait for packet to get sent/fifo to be empty"); 737874bcba6SMarcus Wolf wait_event_interruptible(device->fifo_wait_queue, 738874bcba6SMarcus Wolf device->free_in_fifo == FIFO_SIZE || 739874bcba6SMarcus Wolf kthread_should_stop()); 7406b167a67SValentin Vidic if (kthread_should_stop()) 741ebc1dea2SValentin Vidic dev_dbg(device->dev, "ABORT\n"); 742874bcba6SMarcus Wolf 743874bcba6SMarcus Wolf /* STOP_TRANSMISSION */ 744874bcba6SMarcus Wolf dev_dbg(device->dev, "thread: Packet sent. Set mode to stby."); 745038a5b4eSNguyen Phan Quang Minh retval = rf69_set_mode(spi, standby); 746038a5b4eSNguyen Phan Quang Minh if (retval < 0) 747038a5b4eSNguyen Phan Quang Minh return retval; 748874bcba6SMarcus Wolf 749874bcba6SMarcus Wolf /* everything sent? */ 75069af5d92SSrishti Sharma if (kfifo_is_empty(&device->tx_fifo)) { 751874bcba6SMarcus Wolf abort: 75291086b82SValentin Vidic if (rx_interrupted) { 753874bcba6SMarcus Wolf rx_interrupted = false; 754874bcba6SMarcus Wolf pi433_start_rx(device); 755874bcba6SMarcus Wolf } 756874bcba6SMarcus Wolf device->tx_active = false; 757874bcba6SMarcus Wolf wake_up_interruptible(&device->rx_wait_queue); 758874bcba6SMarcus Wolf } 759874bcba6SMarcus Wolf } 760874bcba6SMarcus Wolf } 761874bcba6SMarcus Wolf 762874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 763874bcba6SMarcus Wolf 764874bcba6SMarcus Wolf static ssize_t 765874bcba6SMarcus Wolf pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) 766874bcba6SMarcus Wolf { 767874bcba6SMarcus Wolf struct pi433_instance *instance; 768874bcba6SMarcus Wolf struct pi433_device *device; 769874bcba6SMarcus Wolf int bytes_received; 770874bcba6SMarcus Wolf ssize_t retval; 771874bcba6SMarcus Wolf 772874bcba6SMarcus Wolf /* check, whether internal buffer is big enough for requested size */ 773874bcba6SMarcus Wolf if (size > MAX_MSG_SIZE) 774874bcba6SMarcus Wolf return -EMSGSIZE; 775874bcba6SMarcus Wolf 776874bcba6SMarcus Wolf instance = filp->private_data; 777874bcba6SMarcus Wolf device = instance->device; 778874bcba6SMarcus Wolf 779874bcba6SMarcus Wolf /* just one read request at a time */ 780874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 78191086b82SValentin Vidic if (device->rx_active) { 782874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 783874bcba6SMarcus Wolf return -EAGAIN; 78483e3e2efSValentin Vidic } 78583e3e2efSValentin Vidic 786874bcba6SMarcus Wolf device->rx_active = true; 787874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 788874bcba6SMarcus Wolf 789874bcba6SMarcus Wolf /* start receiving */ 790874bcba6SMarcus Wolf /* will block until something was received*/ 791874bcba6SMarcus Wolf device->rx_buffer_size = size; 792874bcba6SMarcus Wolf bytes_received = pi433_receive(device); 793874bcba6SMarcus Wolf 794874bcba6SMarcus Wolf /* release rx */ 795874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 796874bcba6SMarcus Wolf device->rx_active = false; 797874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 798874bcba6SMarcus Wolf 799874bcba6SMarcus Wolf /* if read was successful copy to user space*/ 80069af5d92SSrishti Sharma if (bytes_received > 0) { 801874bcba6SMarcus Wolf retval = copy_to_user(buf, device->rx_buffer, bytes_received); 802874bcba6SMarcus Wolf if (retval) 80339ae5f1eSdan.carpenter@oracle.com return -EFAULT; 804874bcba6SMarcus Wolf } 805874bcba6SMarcus Wolf 806874bcba6SMarcus Wolf return bytes_received; 807874bcba6SMarcus Wolf } 808874bcba6SMarcus Wolf 809874bcba6SMarcus Wolf static ssize_t 810874bcba6SMarcus Wolf pi433_write(struct file *filp, const char __user *buf, 811874bcba6SMarcus Wolf size_t count, loff_t *f_pos) 812874bcba6SMarcus Wolf { 813874bcba6SMarcus Wolf struct pi433_instance *instance; 814874bcba6SMarcus Wolf struct pi433_device *device; 81557f8965aSStefano Manni int retval; 8165451dab9SValentin Vidic unsigned int required, available, copied; 817874bcba6SMarcus Wolf 818874bcba6SMarcus Wolf instance = filp->private_data; 819874bcba6SMarcus Wolf device = instance->device; 820874bcba6SMarcus Wolf 821874bcba6SMarcus Wolf /* check, whether internal buffer (tx thread) is big enough for requested size */ 822874bcba6SMarcus Wolf if (count > MAX_MSG_SIZE) 823874bcba6SMarcus Wolf return -EMSGSIZE; 824874bcba6SMarcus Wolf 825874bcba6SMarcus Wolf /* write the following sequence into fifo: 826056eeda2SDerek Robson * - tx_cfg 827056eeda2SDerek Robson * - size of message 828056eeda2SDerek Robson * - message 829056eeda2SDerek Robson */ 830874bcba6SMarcus Wolf mutex_lock(&device->tx_fifo_lock); 8315451dab9SValentin Vidic 8325451dab9SValentin Vidic required = sizeof(instance->tx_cfg) + sizeof(size_t) + count; 8335451dab9SValentin Vidic available = kfifo_avail(&device->tx_fifo); 8345451dab9SValentin Vidic if (required > available) { 8355451dab9SValentin Vidic dev_dbg(device->dev, "write to fifo failed: %d bytes required but %d available", 8365451dab9SValentin Vidic required, available); 8375451dab9SValentin Vidic mutex_unlock(&device->tx_fifo_lock); 8385451dab9SValentin Vidic return -EAGAIN; 8395451dab9SValentin Vidic } 8405451dab9SValentin Vidic 84120e5f042SEisha Chen-yen-su retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg, 84220e5f042SEisha Chen-yen-su sizeof(instance->tx_cfg)); 843874bcba6SMarcus Wolf if (retval != sizeof(instance->tx_cfg)) 844874bcba6SMarcus Wolf goto abort; 845874bcba6SMarcus Wolf 846874bcba6SMarcus Wolf retval = kfifo_in(&device->tx_fifo, &count, sizeof(size_t)); 847874bcba6SMarcus Wolf if (retval != sizeof(size_t)) 848874bcba6SMarcus Wolf goto abort; 849874bcba6SMarcus Wolf 850874bcba6SMarcus Wolf retval = kfifo_from_user(&device->tx_fifo, buf, count, &copied); 851874bcba6SMarcus Wolf if (retval || copied != count) 852874bcba6SMarcus Wolf goto abort; 853874bcba6SMarcus Wolf 854874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 855874bcba6SMarcus Wolf 856874bcba6SMarcus Wolf /* start transfer */ 857874bcba6SMarcus Wolf wake_up_interruptible(&device->tx_wait_queue); 858874bcba6SMarcus Wolf dev_dbg(device->dev, "write: generated new msg with %d bytes.", copied); 859874bcba6SMarcus Wolf 860dd111469SOliver Graute return copied; 861874bcba6SMarcus Wolf 862874bcba6SMarcus Wolf abort: 8635451dab9SValentin Vidic dev_warn(device->dev, 8645451dab9SValentin Vidic "write to fifo failed, non recoverable: 0x%x", retval); 865874bcba6SMarcus Wolf mutex_unlock(&device->tx_fifo_lock); 866874bcba6SMarcus Wolf return -EAGAIN; 867874bcba6SMarcus Wolf } 868874bcba6SMarcus Wolf 869874bcba6SMarcus Wolf static long 870874bcba6SMarcus Wolf pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 871874bcba6SMarcus Wolf { 872874bcba6SMarcus Wolf int retval = 0; 873874bcba6SMarcus Wolf struct pi433_instance *instance; 874874bcba6SMarcus Wolf struct pi433_device *device; 87550271d38SAl Viro void __user *argp = (void __user *)arg; 876874bcba6SMarcus Wolf 877874bcba6SMarcus Wolf /* Check type and command number */ 878874bcba6SMarcus Wolf if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC) 879874bcba6SMarcus Wolf return -ENOTTY; 880874bcba6SMarcus Wolf 881874bcba6SMarcus Wolf /* TODO? guard against device removal before, or while, 882874bcba6SMarcus Wolf * we issue this ioctl. --> device_get() 883874bcba6SMarcus Wolf */ 884874bcba6SMarcus Wolf instance = filp->private_data; 885874bcba6SMarcus Wolf device = instance->device; 886874bcba6SMarcus Wolf 88716fdcc7bSValentin Vidic if (!device) 888874bcba6SMarcus Wolf return -ESHUTDOWN; 889874bcba6SMarcus Wolf 890874bcba6SMarcus Wolf switch (cmd) { 891874bcba6SMarcus Wolf case PI433_IOC_RD_TX_CFG: 89250271d38SAl Viro if (copy_to_user(argp, &instance->tx_cfg, 89350271d38SAl Viro sizeof(struct pi433_tx_cfg))) 89450271d38SAl Viro return -EFAULT; 895874bcba6SMarcus Wolf break; 896874bcba6SMarcus Wolf case PI433_IOC_WR_TX_CFG: 89750271d38SAl Viro if (copy_from_user(&instance->tx_cfg, argp, 89850271d38SAl Viro sizeof(struct pi433_tx_cfg))) 89950271d38SAl Viro return -EFAULT; 900874bcba6SMarcus Wolf break; 901874bcba6SMarcus Wolf case PI433_IOC_RD_RX_CFG: 90250271d38SAl Viro if (copy_to_user(argp, &device->rx_cfg, 90350271d38SAl Viro sizeof(struct pi433_rx_cfg))) 90450271d38SAl Viro return -EFAULT; 905874bcba6SMarcus Wolf break; 906874bcba6SMarcus Wolf case PI433_IOC_WR_RX_CFG: 907874bcba6SMarcus Wolf mutex_lock(&device->rx_lock); 908874bcba6SMarcus Wolf 909874bcba6SMarcus Wolf /* during pendig read request, change of config not allowed */ 910874bcba6SMarcus Wolf if (device->rx_active) { 911874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 91250271d38SAl Viro return -EAGAIN; 913874bcba6SMarcus Wolf } 914874bcba6SMarcus Wolf 91550271d38SAl Viro if (copy_from_user(&device->rx_cfg, argp, 91650271d38SAl Viro sizeof(struct pi433_rx_cfg))) { 917874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 91850271d38SAl Viro return -EFAULT; 919874bcba6SMarcus Wolf } 920874bcba6SMarcus Wolf 921874bcba6SMarcus Wolf mutex_unlock(&device->rx_lock); 922874bcba6SMarcus Wolf break; 923874bcba6SMarcus Wolf default: 924874bcba6SMarcus Wolf retval = -EINVAL; 925874bcba6SMarcus Wolf } 926874bcba6SMarcus Wolf 927874bcba6SMarcus Wolf return retval; 928874bcba6SMarcus Wolf } 929874bcba6SMarcus Wolf 930874bcba6SMarcus Wolf #ifdef CONFIG_COMPAT 931874bcba6SMarcus Wolf static long 932874bcba6SMarcus Wolf pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 933874bcba6SMarcus Wolf { 934874bcba6SMarcus Wolf return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); 935874bcba6SMarcus Wolf } 936874bcba6SMarcus Wolf #else 937874bcba6SMarcus Wolf #define pi433_compat_ioctl NULL 938874bcba6SMarcus Wolf #endif /* CONFIG_COMPAT */ 939874bcba6SMarcus Wolf 940874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 941874bcba6SMarcus Wolf 942874bcba6SMarcus Wolf static int pi433_open(struct inode *inode, struct file *filp) 943874bcba6SMarcus Wolf { 944874bcba6SMarcus Wolf struct pi433_device *device; 945874bcba6SMarcus Wolf struct pi433_instance *instance; 946874bcba6SMarcus Wolf 947874bcba6SMarcus Wolf mutex_lock(&minor_lock); 948874bcba6SMarcus Wolf device = idr_find(&pi433_idr, iminor(inode)); 949874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 950874bcba6SMarcus Wolf if (!device) { 951874bcba6SMarcus Wolf pr_debug("device: minor %d unknown.\n", iminor(inode)); 952874bcba6SMarcus Wolf return -ENODEV; 953874bcba6SMarcus Wolf } 954874bcba6SMarcus Wolf 955874bcba6SMarcus Wolf if (!device->rx_buffer) { 956874bcba6SMarcus Wolf device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL); 95757fa80f5SValentin Vidic if (!device->rx_buffer) 958874bcba6SMarcus Wolf return -ENOMEM; 959874bcba6SMarcus Wolf } 960874bcba6SMarcus Wolf 961874bcba6SMarcus Wolf device->users++; 962874bcba6SMarcus Wolf instance = kzalloc(sizeof(*instance), GFP_KERNEL); 96369af5d92SSrishti Sharma if (!instance) { 964874bcba6SMarcus Wolf kfree(device->rx_buffer); 965874bcba6SMarcus Wolf device->rx_buffer = NULL; 966874bcba6SMarcus Wolf return -ENOMEM; 967874bcba6SMarcus Wolf } 968874bcba6SMarcus Wolf 969874bcba6SMarcus Wolf /* setup instance data*/ 970874bcba6SMarcus Wolf instance->device = device; 971874bcba6SMarcus Wolf instance->tx_cfg.bit_rate = 4711; 972874bcba6SMarcus Wolf // TODO: fill instance->tx_cfg; 973874bcba6SMarcus Wolf 974874bcba6SMarcus Wolf /* instance data as context */ 975874bcba6SMarcus Wolf filp->private_data = instance; 976874bcba6SMarcus Wolf nonseekable_open(inode, filp); 977874bcba6SMarcus Wolf 978874bcba6SMarcus Wolf return 0; 979874bcba6SMarcus Wolf } 980874bcba6SMarcus Wolf 981874bcba6SMarcus Wolf static int pi433_release(struct inode *inode, struct file *filp) 982874bcba6SMarcus Wolf { 983874bcba6SMarcus Wolf struct pi433_instance *instance; 984874bcba6SMarcus Wolf struct pi433_device *device; 985874bcba6SMarcus Wolf 986874bcba6SMarcus Wolf instance = filp->private_data; 987874bcba6SMarcus Wolf device = instance->device; 988874bcba6SMarcus Wolf kfree(instance); 989874bcba6SMarcus Wolf filp->private_data = NULL; 990874bcba6SMarcus Wolf 991874bcba6SMarcus Wolf /* last close? */ 992874bcba6SMarcus Wolf device->users--; 993874bcba6SMarcus Wolf 994874bcba6SMarcus Wolf if (!device->users) { 995874bcba6SMarcus Wolf kfree(device->rx_buffer); 996874bcba6SMarcus Wolf device->rx_buffer = NULL; 99716fdcc7bSValentin Vidic if (!device->spi) 998874bcba6SMarcus Wolf kfree(device); 999874bcba6SMarcus Wolf } 1000874bcba6SMarcus Wolf 1001874bcba6SMarcus Wolf return 0; 1002874bcba6SMarcus Wolf } 1003874bcba6SMarcus Wolf 1004874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1005874bcba6SMarcus Wolf 1006469b84f3SValentin Vidic static int setup_gpio(struct pi433_device *device) 1007874bcba6SMarcus Wolf { 1008874bcba6SMarcus Wolf char name[5]; 1009874bcba6SMarcus Wolf int retval; 1010874bcba6SMarcus Wolf int i; 1011ee26e236SCihangir Akturk const irq_handler_t DIO_irq_handler[NUM_DIO] = { 1012ee26e236SCihangir Akturk DIO0_irq_handler, 1013ee26e236SCihangir Akturk DIO1_irq_handler 1014ee26e236SCihangir Akturk }; 1015874bcba6SMarcus Wolf 1016e27de55dSValentin Vidic for (i = 0; i < NUM_DIO; i++) { 1017874bcba6SMarcus Wolf /* "construct" name and get the gpio descriptor */ 1018874bcba6SMarcus Wolf snprintf(name, sizeof(name), "DIO%d", i); 101920e5f042SEisha Chen-yen-su device->gpiod[i] = gpiod_get(&device->spi->dev, name, 102020e5f042SEisha Chen-yen-su 0 /*GPIOD_IN*/); 1021874bcba6SMarcus Wolf 102269af5d92SSrishti Sharma if (device->gpiod[i] == ERR_PTR(-ENOENT)) { 102320e5f042SEisha Chen-yen-su dev_dbg(&device->spi->dev, 102420e5f042SEisha Chen-yen-su "Could not find entry for %s. Ignoring.", name); 1025874bcba6SMarcus Wolf continue; 1026874bcba6SMarcus Wolf } 1027874bcba6SMarcus Wolf 1028874bcba6SMarcus Wolf if (device->gpiod[i] == ERR_PTR(-EBUSY)) 1029874bcba6SMarcus Wolf dev_dbg(&device->spi->dev, "%s is busy.", name); 1030874bcba6SMarcus Wolf 103191086b82SValentin Vidic if (IS_ERR(device->gpiod[i])) { 1032874bcba6SMarcus Wolf retval = PTR_ERR(device->gpiod[i]); 1033874bcba6SMarcus Wolf /* release already allocated gpios */ 1034c976752eSOliver Graute for (i--; i >= 0; i--) { 1035874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1036874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1037874bcba6SMarcus Wolf } 1038874bcba6SMarcus Wolf return retval; 1039874bcba6SMarcus Wolf } 1040874bcba6SMarcus Wolf 1041874bcba6SMarcus Wolf /* configure the pin */ 1042874bcba6SMarcus Wolf gpiod_unexport(device->gpiod[i]); 1043874bcba6SMarcus Wolf retval = gpiod_direction_input(device->gpiod[i]); 10446b167a67SValentin Vidic if (retval) 10456b167a67SValentin Vidic return retval; 1046874bcba6SMarcus Wolf 1047874bcba6SMarcus Wolf /* configure irq */ 1048874bcba6SMarcus Wolf device->irq_num[i] = gpiod_to_irq(device->gpiod[i]); 104969af5d92SSrishti Sharma if (device->irq_num[i] < 0) { 1050874bcba6SMarcus Wolf device->gpiod[i] = ERR_PTR(-EINVAL);//(struct gpio_desc *)device->irq_num[i]; 1051874bcba6SMarcus Wolf return device->irq_num[i]; 1052874bcba6SMarcus Wolf } 1053874bcba6SMarcus Wolf retval = request_irq(device->irq_num[i], 1054874bcba6SMarcus Wolf DIO_irq_handler[i], 1055874bcba6SMarcus Wolf 0, /* flags */ 1056874bcba6SMarcus Wolf name, 1057874bcba6SMarcus Wolf device); 1058874bcba6SMarcus Wolf 1059874bcba6SMarcus Wolf if (retval) 1060874bcba6SMarcus Wolf return retval; 1061874bcba6SMarcus Wolf 10622ebd34caSHarsha Sharma dev_dbg(&device->spi->dev, "%s successfully configured", name); 1063874bcba6SMarcus Wolf } 1064874bcba6SMarcus Wolf 1065874bcba6SMarcus Wolf return 0; 1066874bcba6SMarcus Wolf } 1067874bcba6SMarcus Wolf 1068469b84f3SValentin Vidic static void free_gpio(struct pi433_device *device) 1069874bcba6SMarcus Wolf { 1070874bcba6SMarcus Wolf int i; 1071874bcba6SMarcus Wolf 107291086b82SValentin Vidic for (i = 0; i < NUM_DIO; i++) { 1073874bcba6SMarcus Wolf /* check if gpiod is valid */ 1074874bcba6SMarcus Wolf if (IS_ERR(device->gpiod[i])) 1075874bcba6SMarcus Wolf continue; 1076874bcba6SMarcus Wolf 1077874bcba6SMarcus Wolf free_irq(device->irq_num[i], device); 1078874bcba6SMarcus Wolf gpiod_put(device->gpiod[i]); 1079874bcba6SMarcus Wolf } 1080874bcba6SMarcus Wolf } 1081874bcba6SMarcus Wolf 1082874bcba6SMarcus Wolf static int pi433_get_minor(struct pi433_device *device) 1083874bcba6SMarcus Wolf { 1084874bcba6SMarcus Wolf int retval = -ENOMEM; 1085874bcba6SMarcus Wolf 1086874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1087874bcba6SMarcus Wolf retval = idr_alloc(&pi433_idr, device, 0, N_PI433_MINORS, GFP_KERNEL); 1088874bcba6SMarcus Wolf if (retval >= 0) { 1089874bcba6SMarcus Wolf device->minor = retval; 1090874bcba6SMarcus Wolf retval = 0; 1091874bcba6SMarcus Wolf } else if (retval == -ENOSPC) { 1092d2cb4845SMarcin Ciupak dev_err(&device->spi->dev, "too many pi433 devices\n"); 1093874bcba6SMarcus Wolf retval = -EINVAL; 1094874bcba6SMarcus Wolf } 1095874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1096874bcba6SMarcus Wolf return retval; 1097874bcba6SMarcus Wolf } 1098874bcba6SMarcus Wolf 1099874bcba6SMarcus Wolf static void pi433_free_minor(struct pi433_device *dev) 1100874bcba6SMarcus Wolf { 1101874bcba6SMarcus Wolf mutex_lock(&minor_lock); 1102874bcba6SMarcus Wolf idr_remove(&pi433_idr, dev->minor); 1103874bcba6SMarcus Wolf mutex_unlock(&minor_lock); 1104874bcba6SMarcus Wolf } 1105c144df8dSValentin Vidic 1106874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1107874bcba6SMarcus Wolf 1108874bcba6SMarcus Wolf static const struct file_operations pi433_fops = { 1109874bcba6SMarcus Wolf .owner = THIS_MODULE, 1110874bcba6SMarcus Wolf /* REVISIT switch to aio primitives, so that userspace 1111874bcba6SMarcus Wolf * gets more complete API coverage. It'll simplify things 1112874bcba6SMarcus Wolf * too, except for the locking. 1113874bcba6SMarcus Wolf */ 1114874bcba6SMarcus Wolf .write = pi433_write, 1115874bcba6SMarcus Wolf .read = pi433_read, 1116874bcba6SMarcus Wolf .unlocked_ioctl = pi433_ioctl, 1117874bcba6SMarcus Wolf .compat_ioctl = pi433_compat_ioctl, 1118874bcba6SMarcus Wolf .open = pi433_open, 1119874bcba6SMarcus Wolf .release = pi433_release, 1120874bcba6SMarcus Wolf .llseek = no_llseek, 1121874bcba6SMarcus Wolf }; 1122874bcba6SMarcus Wolf 1123874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1124874bcba6SMarcus Wolf 1125874bcba6SMarcus Wolf static int pi433_probe(struct spi_device *spi) 1126874bcba6SMarcus Wolf { 1127874bcba6SMarcus Wolf struct pi433_device *device; 1128874bcba6SMarcus Wolf int retval; 1129874bcba6SMarcus Wolf 1130874bcba6SMarcus Wolf /* setup spi parameters */ 1131874bcba6SMarcus Wolf spi->mode = 0x00; 1132874bcba6SMarcus Wolf spi->bits_per_word = 8; 1133874bcba6SMarcus Wolf /* spi->max_speed_hz = 10000000; 1MHz already set by device tree overlay */ 1134874bcba6SMarcus Wolf 1135874bcba6SMarcus Wolf retval = spi_setup(spi); 113691086b82SValentin Vidic if (retval) { 1137874bcba6SMarcus Wolf dev_dbg(&spi->dev, "configuration of SPI interface failed!\n"); 1138874bcba6SMarcus Wolf return retval; 113983e3e2efSValentin Vidic } 114083e3e2efSValentin Vidic 1141874bcba6SMarcus Wolf dev_dbg(&spi->dev, 1142874bcba6SMarcus Wolf "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed", 1143874bcba6SMarcus Wolf spi->mode, spi->bits_per_word, spi->max_speed_hz); 1144874bcba6SMarcus Wolf 1145874bcba6SMarcus Wolf /* Ping the chip by reading the version register */ 1146874bcba6SMarcus Wolf retval = spi_w8r8(spi, 0x10); 1147874bcba6SMarcus Wolf if (retval < 0) 1148874bcba6SMarcus Wolf return retval; 1149874bcba6SMarcus Wolf 11506feb5c82SXiangyang Zhang switch (retval) { 1151874bcba6SMarcus Wolf case 0x24: 115228eb8555SColin Ian King dev_dbg(&spi->dev, "found pi433 (ver. 0x%x)", retval); 1153874bcba6SMarcus Wolf break; 1154874bcba6SMarcus Wolf default: 1155874bcba6SMarcus Wolf dev_dbg(&spi->dev, "unknown chip version: 0x%x", retval); 1156874bcba6SMarcus Wolf return -ENODEV; 1157874bcba6SMarcus Wolf } 1158874bcba6SMarcus Wolf 1159874bcba6SMarcus Wolf /* Allocate driver data */ 1160874bcba6SMarcus Wolf device = kzalloc(sizeof(*device), GFP_KERNEL); 1161874bcba6SMarcus Wolf if (!device) 1162874bcba6SMarcus Wolf return -ENOMEM; 1163874bcba6SMarcus Wolf 1164874bcba6SMarcus Wolf /* Initialize the driver data */ 1165874bcba6SMarcus Wolf device->spi = spi; 1166874bcba6SMarcus Wolf device->rx_active = false; 1167874bcba6SMarcus Wolf device->tx_active = false; 1168874bcba6SMarcus Wolf device->interrupt_rx_allowed = false; 1169874bcba6SMarcus Wolf 1170874bcba6SMarcus Wolf /* init wait queues */ 1171874bcba6SMarcus Wolf init_waitqueue_head(&device->tx_wait_queue); 1172874bcba6SMarcus Wolf init_waitqueue_head(&device->rx_wait_queue); 1173874bcba6SMarcus Wolf init_waitqueue_head(&device->fifo_wait_queue); 1174874bcba6SMarcus Wolf 1175874bcba6SMarcus Wolf /* init fifo */ 1176874bcba6SMarcus Wolf INIT_KFIFO(device->tx_fifo); 1177874bcba6SMarcus Wolf 1178874bcba6SMarcus Wolf /* init mutexes and locks */ 1179874bcba6SMarcus Wolf mutex_init(&device->tx_fifo_lock); 1180874bcba6SMarcus Wolf mutex_init(&device->rx_lock); 1181874bcba6SMarcus Wolf 1182874bcba6SMarcus Wolf /* setup GPIO (including irq_handler) for the different DIOs */ 1183469b84f3SValentin Vidic retval = setup_gpio(device); 118469af5d92SSrishti Sharma if (retval) { 1185874bcba6SMarcus Wolf dev_dbg(&spi->dev, "setup of GPIOs failed"); 1186874bcba6SMarcus Wolf goto GPIO_failed; 1187874bcba6SMarcus Wolf } 1188874bcba6SMarcus Wolf 1189874bcba6SMarcus Wolf /* setup the radio module */ 119054fc95a7SSimon Sandström retval = rf69_set_mode(spi, standby); 119154fc95a7SSimon Sandström if (retval < 0) 119254fc95a7SSimon Sandström goto minor_failed; 119354fc95a7SSimon Sandström retval = rf69_set_data_mode(spi, DATAMODUL_MODE_PACKET); 119454fc95a7SSimon Sandström if (retval < 0) 119554fc95a7SSimon Sandström goto minor_failed; 119654fc95a7SSimon Sandström retval = rf69_enable_amplifier(spi, MASK_PALEVEL_PA0); 119754fc95a7SSimon Sandström if (retval < 0) 119854fc95a7SSimon Sandström goto minor_failed; 119954fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA1); 120054fc95a7SSimon Sandström if (retval < 0) 120154fc95a7SSimon Sandström goto minor_failed; 120254fc95a7SSimon Sandström retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA2); 120354fc95a7SSimon Sandström if (retval < 0) 120454fc95a7SSimon Sandström goto minor_failed; 120554fc95a7SSimon Sandström retval = rf69_set_output_power_level(spi, 13); 120654fc95a7SSimon Sandström if (retval < 0) 120754fc95a7SSimon Sandström goto minor_failed; 12080b897065SValentin Vidic retval = rf69_set_antenna_impedance(spi, fifty_ohm); 120954fc95a7SSimon Sandström if (retval < 0) 121054fc95a7SSimon Sandström goto minor_failed; 1211874bcba6SMarcus Wolf 1212874bcba6SMarcus Wolf /* determ minor number */ 1213874bcba6SMarcus Wolf retval = pi433_get_minor(device); 121469af5d92SSrishti Sharma if (retval) { 1215d2cb4845SMarcin Ciupak dev_dbg(&spi->dev, "get of minor number failed"); 1216874bcba6SMarcus Wolf goto minor_failed; 1217874bcba6SMarcus Wolf } 1218874bcba6SMarcus Wolf 1219874bcba6SMarcus Wolf /* create device */ 1220874bcba6SMarcus Wolf device->devt = MKDEV(MAJOR(pi433_dev), device->minor); 1221874bcba6SMarcus Wolf device->dev = device_create(pi433_class, 1222874bcba6SMarcus Wolf &spi->dev, 1223874bcba6SMarcus Wolf device->devt, 1224874bcba6SMarcus Wolf device, 122599ee4774SMarcin Ciupak "pi433.%d", 122699ee4774SMarcin Ciupak device->minor); 1227874bcba6SMarcus Wolf if (IS_ERR(device->dev)) { 1228874bcba6SMarcus Wolf pr_err("pi433: device register failed\n"); 1229874bcba6SMarcus Wolf retval = PTR_ERR(device->dev); 1230874bcba6SMarcus Wolf goto device_create_failed; 123191086b82SValentin Vidic } else { 1232874bcba6SMarcus Wolf dev_dbg(device->dev, 1233874bcba6SMarcus Wolf "created device for major %d, minor %d\n", 1234874bcba6SMarcus Wolf MAJOR(pi433_dev), 1235874bcba6SMarcus Wolf device->minor); 1236874bcba6SMarcus Wolf } 1237874bcba6SMarcus Wolf 1238d2cb4845SMarcin Ciupak /* start tx thread */ 1239d2cb4845SMarcin Ciupak device->tx_task_struct = kthread_run(pi433_tx_thread, 1240d2cb4845SMarcin Ciupak device, 124199ee4774SMarcin Ciupak "pi433.%d_tx_task", 124299ee4774SMarcin Ciupak device->minor); 1243d2cb4845SMarcin Ciupak if (IS_ERR(device->tx_task_struct)) { 1244d2cb4845SMarcin Ciupak dev_dbg(device->dev, "start of send thread failed"); 1245d2cb4845SMarcin Ciupak goto send_thread_failed; 1246d2cb4845SMarcin Ciupak } 1247d2cb4845SMarcin Ciupak 1248874bcba6SMarcus Wolf /* create cdev */ 1249874bcba6SMarcus Wolf device->cdev = cdev_alloc(); 1250874bcba6SMarcus Wolf device->cdev->owner = THIS_MODULE; 1251874bcba6SMarcus Wolf cdev_init(device->cdev, &pi433_fops); 1252874bcba6SMarcus Wolf retval = cdev_add(device->cdev, device->devt, 1); 125369af5d92SSrishti Sharma if (retval) { 1254874bcba6SMarcus Wolf dev_dbg(device->dev, "register of cdev failed"); 1255874bcba6SMarcus Wolf goto cdev_failed; 1256874bcba6SMarcus Wolf } 1257874bcba6SMarcus Wolf 1258874bcba6SMarcus Wolf /* spi setup */ 1259874bcba6SMarcus Wolf spi_set_drvdata(spi, device); 1260874bcba6SMarcus Wolf 1261874bcba6SMarcus Wolf return 0; 1262874bcba6SMarcus Wolf 1263874bcba6SMarcus Wolf cdev_failed: 1264d2cb4845SMarcin Ciupak kthread_stop(device->tx_task_struct); 1265d2cb4845SMarcin Ciupak send_thread_failed: 1266874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1267874bcba6SMarcus Wolf device_create_failed: 1268874bcba6SMarcus Wolf pi433_free_minor(device); 1269874bcba6SMarcus Wolf minor_failed: 1270469b84f3SValentin Vidic free_gpio(device); 1271874bcba6SMarcus Wolf GPIO_failed: 1272874bcba6SMarcus Wolf kfree(device); 1273874bcba6SMarcus Wolf 1274874bcba6SMarcus Wolf return retval; 1275874bcba6SMarcus Wolf } 1276874bcba6SMarcus Wolf 1277874bcba6SMarcus Wolf static int pi433_remove(struct spi_device *spi) 1278874bcba6SMarcus Wolf { 1279874bcba6SMarcus Wolf struct pi433_device *device = spi_get_drvdata(spi); 1280874bcba6SMarcus Wolf 1281874bcba6SMarcus Wolf /* free GPIOs */ 1282469b84f3SValentin Vidic free_gpio(device); 1283874bcba6SMarcus Wolf 1284874bcba6SMarcus Wolf /* make sure ops on existing fds can abort cleanly */ 1285874bcba6SMarcus Wolf device->spi = NULL; 1286874bcba6SMarcus Wolf 1287874bcba6SMarcus Wolf kthread_stop(device->tx_task_struct); 1288874bcba6SMarcus Wolf 1289874bcba6SMarcus Wolf device_destroy(pi433_class, device->devt); 1290874bcba6SMarcus Wolf 1291874bcba6SMarcus Wolf cdev_del(device->cdev); 1292874bcba6SMarcus Wolf 1293874bcba6SMarcus Wolf pi433_free_minor(device); 1294874bcba6SMarcus Wolf 1295874bcba6SMarcus Wolf if (device->users == 0) 1296874bcba6SMarcus Wolf kfree(device); 1297874bcba6SMarcus Wolf 1298874bcba6SMarcus Wolf return 0; 1299874bcba6SMarcus Wolf } 1300874bcba6SMarcus Wolf 1301874bcba6SMarcus Wolf static const struct of_device_id pi433_dt_ids[] = { 1302874bcba6SMarcus Wolf { .compatible = "Smarthome-Wolf,pi433" }, 1303874bcba6SMarcus Wolf {}, 1304874bcba6SMarcus Wolf }; 1305874bcba6SMarcus Wolf 1306874bcba6SMarcus Wolf MODULE_DEVICE_TABLE(of, pi433_dt_ids); 1307874bcba6SMarcus Wolf 1308874bcba6SMarcus Wolf static struct spi_driver pi433_spi_driver = { 1309874bcba6SMarcus Wolf .driver = { 1310874bcba6SMarcus Wolf .name = "pi433", 1311874bcba6SMarcus Wolf .owner = THIS_MODULE, 1312874bcba6SMarcus Wolf .of_match_table = of_match_ptr(pi433_dt_ids), 1313874bcba6SMarcus Wolf }, 1314874bcba6SMarcus Wolf .probe = pi433_probe, 1315874bcba6SMarcus Wolf .remove = pi433_remove, 1316874bcba6SMarcus Wolf 1317874bcba6SMarcus Wolf /* NOTE: suspend/resume methods are not necessary here. 1318874bcba6SMarcus Wolf * We don't do anything except pass the requests to/from 1319874bcba6SMarcus Wolf * the underlying controller. The refrigerator handles 1320874bcba6SMarcus Wolf * most issues; the controller driver handles the rest. 1321874bcba6SMarcus Wolf */ 1322874bcba6SMarcus Wolf }; 1323874bcba6SMarcus Wolf 1324874bcba6SMarcus Wolf /*-------------------------------------------------------------------------*/ 1325874bcba6SMarcus Wolf 1326874bcba6SMarcus Wolf static int __init pi433_init(void) 1327874bcba6SMarcus Wolf { 1328874bcba6SMarcus Wolf int status; 1329874bcba6SMarcus Wolf 1330874bcba6SMarcus Wolf /* If MAX_MSG_SIZE is smaller then FIFO_SIZE, the driver won't 1331056eeda2SDerek Robson * work stable - risk of buffer overflow 1332056eeda2SDerek Robson */ 1333874bcba6SMarcus Wolf if (MAX_MSG_SIZE < FIFO_SIZE) 1334874bcba6SMarcus Wolf return -EINVAL; 1335874bcba6SMarcus Wolf 1336874bcba6SMarcus Wolf /* Claim device numbers. Then register a class 1337874bcba6SMarcus Wolf * that will key udev/mdev to add/remove /dev nodes. Last, register 1338874bcba6SMarcus Wolf * Last, register the driver which manages those device numbers. 1339874bcba6SMarcus Wolf */ 1340c7265435SHariPrasath Elango status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433"); 1341874bcba6SMarcus Wolf if (status < 0) 1342874bcba6SMarcus Wolf return status; 1343874bcba6SMarcus Wolf 1344874bcba6SMarcus Wolf pi433_class = class_create(THIS_MODULE, "pi433"); 134569af5d92SSrishti Sharma if (IS_ERR(pi433_class)) { 13469be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13479be5755cSOliver Graute pi433_spi_driver.driver.name); 1348874bcba6SMarcus Wolf return PTR_ERR(pi433_class); 1349874bcba6SMarcus Wolf } 1350874bcba6SMarcus Wolf 1351874bcba6SMarcus Wolf status = spi_register_driver(&pi433_spi_driver); 135269af5d92SSrishti Sharma if (status < 0) { 1353874bcba6SMarcus Wolf class_destroy(pi433_class); 13549be5755cSOliver Graute unregister_chrdev(MAJOR(pi433_dev), 13559be5755cSOliver Graute pi433_spi_driver.driver.name); 1356874bcba6SMarcus Wolf } 1357874bcba6SMarcus Wolf 1358874bcba6SMarcus Wolf return status; 1359874bcba6SMarcus Wolf } 1360874bcba6SMarcus Wolf 1361874bcba6SMarcus Wolf module_init(pi433_init); 1362874bcba6SMarcus Wolf 1363874bcba6SMarcus Wolf static void __exit pi433_exit(void) 1364874bcba6SMarcus Wolf { 1365874bcba6SMarcus Wolf spi_unregister_driver(&pi433_spi_driver); 1366874bcba6SMarcus Wolf class_destroy(pi433_class); 1367874bcba6SMarcus Wolf unregister_chrdev(MAJOR(pi433_dev), pi433_spi_driver.driver.name); 1368874bcba6SMarcus Wolf } 1369874bcba6SMarcus Wolf module_exit(pi433_exit); 1370874bcba6SMarcus Wolf 1371874bcba6SMarcus Wolf MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>"); 1372874bcba6SMarcus Wolf MODULE_DESCRIPTION("Driver for Pi433"); 1373874bcba6SMarcus Wolf MODULE_LICENSE("GPL"); 1374874bcba6SMarcus Wolf MODULE_ALIAS("spi:pi433"); 1375