diff options
Diffstat (limited to 'drivers/staging/pi433/pi433_if.c')
-rw-r--r-- | drivers/staging/pi433/pi433_if.c | 1438 |
1 files changed, 0 insertions, 1438 deletions
diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c deleted file mode 100644 index b6c4917d515e..000000000000 --- a/drivers/staging/pi433/pi433_if.c +++ /dev/null @@ -1,1438 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * userspace interface for pi433 radio module - * - * Pi433 is a 433MHz radio module for the Raspberry Pi. - * It is based on the HopeRf Module RFM69CW. Therefore inside of this - * driver, you'll find an abstraction of the rf69 chip. - * - * If needed, this driver could be extended, to also support other - * devices, basing on HopeRfs rf69. - * - * The driver can also be extended, to support other modules of - * HopeRf with a similar interace - e. g. RFM69HCW, RFM12, RFM95, ... - * - * Copyright (C) 2016 Wolf-Entwicklungen - * Marcus Wolf <linux@wolf-entwicklungen.de> - */ - -#undef DEBUG - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/ioctl.h> -#include <linux/uaccess.h> -#include <linux/fs.h> -#include <linux/device.h> -#include <linux/cdev.h> -#include <linux/err.h> -#include <linux/kfifo.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/gpio/consumer.h> -#include <linux/kthread.h> -#include <linux/wait.h> -#include <linux/spi/spi.h> -#ifdef CONFIG_COMPAT -#include <linux/compat.h> -#endif -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -#include "pi433_if.h" -#include "rf69.h" - -#define N_PI433_MINORS BIT(MINORBITS) /*32*/ /* ... up to 256 */ -#define MAX_MSG_SIZE 900 /* min: FIFO_SIZE! */ -#define MSG_FIFO_SIZE 65536 /* 65536 = 2^16 */ -#define FIFO_THRESHOLD 15 /* bytes */ -#define NUM_DIO 2 - -static dev_t pi433_dev; -static DEFINE_IDR(pi433_idr); -static DEFINE_MUTEX(minor_lock); /* Protect idr accesses */ -static struct dentry *root_dir; /* debugfs root directory for the driver */ - -/* mainly for udev to create /dev/pi433 */ -static const struct class pi433_class = { - .name = "pi433", -}; - -/* - * tx config is instance specific - * so with each open a new tx config struct is needed - */ -/* - * rx config is device specific - * so we have just one rx config, ebedded in device struct - */ -struct pi433_device { - /* device handling related values */ - dev_t devt; - int minor; - struct device *dev; - struct cdev *cdev; - struct spi_device *spi; - - /* irq related values */ - struct gpio_desc *gpiod[NUM_DIO]; - int irq_num[NUM_DIO]; - u8 irq_state[NUM_DIO]; - - /* tx related values */ - STRUCT_KFIFO_REC_1(MSG_FIFO_SIZE) tx_fifo; - struct mutex tx_fifo_lock; /* serialize userspace writers */ - struct task_struct *tx_task_struct; - wait_queue_head_t tx_wait_queue; - u8 free_in_fifo; - char buffer[MAX_MSG_SIZE]; - - /* rx related values */ - struct pi433_rx_cfg rx_cfg; - u8 *rx_buffer; - unsigned int rx_buffer_size; - u32 rx_bytes_to_drop; - u32 rx_bytes_dropped; - unsigned int rx_position; - struct mutex rx_lock; /* protects rx_* variable accesses */ - wait_queue_head_t rx_wait_queue; - - /* fifo wait queue */ - struct task_struct *fifo_task_struct; - wait_queue_head_t fifo_wait_queue; - - /* flags */ - bool rx_active; - bool tx_active; - bool interrupt_rx_allowed; -}; - -struct pi433_instance { - struct pi433_device *device; - struct pi433_tx_cfg tx_cfg; - - /* control flags */ - bool tx_cfg_initialized; -}; - -/*-------------------------------------------------------------------------*/ - -/* GPIO interrupt handlers */ -static irqreturn_t DIO0_irq_handler(int irq, void *dev_id) -{ - struct pi433_device *device = dev_id; - - if (device->irq_state[DIO0] == DIO_PACKET_SENT) { - device->free_in_fifo = FIFO_SIZE; - dev_dbg(device->dev, "DIO0 irq: Packet sent\n"); - wake_up_interruptible(&device->fifo_wait_queue); - } else if (device->irq_state[DIO0] == DIO_RSSI_DIO0) { - dev_dbg(device->dev, "DIO0 irq: RSSI level over threshold\n"); - wake_up_interruptible(&device->rx_wait_queue); - } else if (device->irq_state[DIO0] == DIO_PAYLOAD_READY) { - dev_dbg(device->dev, "DIO0 irq: Payload ready\n"); - device->free_in_fifo = 0; - wake_up_interruptible(&device->fifo_wait_queue); - } - - return IRQ_HANDLED; -} - -static irqreturn_t DIO1_irq_handler(int irq, void *dev_id) -{ - struct pi433_device *device = dev_id; - - if (device->irq_state[DIO1] == DIO_FIFO_NOT_EMPTY_DIO1) { - device->free_in_fifo = FIFO_SIZE; - } else if (device->irq_state[DIO1] == DIO_FIFO_LEVEL) { - if (device->rx_active) - device->free_in_fifo = FIFO_THRESHOLD - 1; - else - device->free_in_fifo = FIFO_SIZE - FIFO_THRESHOLD - 1; - } - dev_dbg(device->dev, - "DIO1 irq: %d bytes free in fifo\n", device->free_in_fifo); - wake_up_interruptible(&device->fifo_wait_queue); - - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -static int -rf69_set_rx_cfg(struct pi433_device *dev, struct pi433_rx_cfg *rx_cfg) -{ - int ret; - int payload_length; - - /* receiver config */ - ret = rf69_set_frequency(dev->spi, rx_cfg->frequency); - if (ret < 0) - return ret; - ret = rf69_set_modulation(dev->spi, rx_cfg->modulation); - if (ret < 0) - return ret; - ret = rf69_set_bit_rate(dev->spi, rx_cfg->bit_rate); - if (ret < 0) - return ret; - ret = rf69_set_antenna_impedance(dev->spi, rx_cfg->antenna_impedance); - if (ret < 0) - return ret; - ret = rf69_set_rssi_threshold(dev->spi, rx_cfg->rssi_threshold); - if (ret < 0) - return ret; - ret = rf69_set_ook_threshold_dec(dev->spi, rx_cfg->threshold_decrement); - if (ret < 0) - return ret; - ret = rf69_set_bandwidth(dev->spi, rx_cfg->bw_mantisse, - rx_cfg->bw_exponent); - if (ret < 0) - return ret; - ret = rf69_set_bandwidth_during_afc(dev->spi, rx_cfg->bw_mantisse, - rx_cfg->bw_exponent); - if (ret < 0) - return ret; - ret = rf69_set_dagc(dev->spi, rx_cfg->dagc); - if (ret < 0) - return ret; - - dev->rx_bytes_to_drop = rx_cfg->bytes_to_drop; - - /* packet config */ - /* enable */ - if (rx_cfg->enable_sync == OPTION_ON) { - ret = rf69_enable_sync(dev->spi); - if (ret < 0) - return ret; - - ret = rf69_set_fifo_fill_condition(dev->spi, - after_sync_interrupt); - if (ret < 0) - return ret; - } else { - ret = rf69_disable_sync(dev->spi); - if (ret < 0) - return ret; - - ret = rf69_set_fifo_fill_condition(dev->spi, always); - if (ret < 0) - return ret; - } - if (rx_cfg->enable_length_byte == OPTION_ON) { - ret = rf69_set_packet_format(dev->spi, packet_length_var); - if (ret < 0) - return ret; - } else { - ret = rf69_set_packet_format(dev->spi, packet_length_fix); - if (ret < 0) - return ret; - } - ret = rf69_set_address_filtering(dev->spi, - rx_cfg->enable_address_filtering); - if (ret < 0) - return ret; - - if (rx_cfg->enable_crc == OPTION_ON) { - ret = rf69_enable_crc(dev->spi); - if (ret < 0) - return ret; - } else { - ret = rf69_disable_crc(dev->spi); - if (ret < 0) - return ret; - } - - /* lengths */ - ret = rf69_set_sync_size(dev->spi, rx_cfg->sync_length); - if (ret < 0) - return ret; - if (rx_cfg->enable_length_byte == OPTION_ON) { - ret = rf69_set_payload_length(dev->spi, 0xff); - if (ret < 0) - return ret; - } else if (rx_cfg->fixed_message_length != 0) { - payload_length = rx_cfg->fixed_message_length; - if (rx_cfg->enable_length_byte == OPTION_ON) - payload_length++; - if (rx_cfg->enable_address_filtering != filtering_off) - payload_length++; - ret = rf69_set_payload_length(dev->spi, payload_length); - if (ret < 0) - return ret; - } else { - ret = rf69_set_payload_length(dev->spi, 0); - if (ret < 0) - return ret; - } - - /* values */ - if (rx_cfg->enable_sync == OPTION_ON) { - ret = rf69_set_sync_values(dev->spi, rx_cfg->sync_pattern); - if (ret < 0) - return ret; - } - if (rx_cfg->enable_address_filtering != filtering_off) { - ret = rf69_set_node_address(dev->spi, rx_cfg->node_address); - if (ret < 0) - return ret; - ret = rf69_set_broadcast_address(dev->spi, - rx_cfg->broadcast_address); - if (ret < 0) - return ret; - } - - return 0; -} - -static int -rf69_set_tx_cfg(struct pi433_device *dev, struct pi433_tx_cfg *tx_cfg) -{ - int ret; - - ret = rf69_set_frequency(dev->spi, tx_cfg->frequency); - if (ret < 0) - return ret; - ret = rf69_set_modulation(dev->spi, tx_cfg->modulation); - if (ret < 0) - return ret; - ret = rf69_set_bit_rate(dev->spi, tx_cfg->bit_rate); - if (ret < 0) - return ret; - ret = rf69_set_deviation(dev->spi, tx_cfg->dev_frequency); - if (ret < 0) - return ret; - ret = rf69_set_pa_ramp(dev->spi, tx_cfg->pa_ramp); - if (ret < 0) - return ret; - ret = rf69_set_modulation_shaping(dev->spi, tx_cfg->mod_shaping); - if (ret < 0) - return ret; - ret = rf69_set_tx_start_condition(dev->spi, tx_cfg->tx_start_condition); - if (ret < 0) - return ret; - - /* packet format enable */ - if (tx_cfg->enable_preamble == OPTION_ON) { - ret = rf69_set_preamble_length(dev->spi, - tx_cfg->preamble_length); - if (ret < 0) - return ret; - } else { - ret = rf69_set_preamble_length(dev->spi, 0); - if (ret < 0) - return ret; - } - - if (tx_cfg->enable_sync == OPTION_ON) { - ret = rf69_set_sync_size(dev->spi, tx_cfg->sync_length); - if (ret < 0) - return ret; - ret = rf69_set_sync_values(dev->spi, tx_cfg->sync_pattern); - if (ret < 0) - return ret; - ret = rf69_enable_sync(dev->spi); - if (ret < 0) - return ret; - } else { - ret = rf69_disable_sync(dev->spi); - if (ret < 0) - return ret; - } - - if (tx_cfg->enable_length_byte == OPTION_ON) { - ret = rf69_set_packet_format(dev->spi, packet_length_var); - if (ret < 0) - return ret; - } else { - ret = rf69_set_packet_format(dev->spi, packet_length_fix); - if (ret < 0) - return ret; - } - - if (tx_cfg->enable_crc == OPTION_ON) { - ret = rf69_enable_crc(dev->spi); - if (ret < 0) - return ret; - } else { - ret = rf69_disable_crc(dev->spi); - if (ret < 0) - return ret; - } - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int pi433_start_rx(struct pi433_device *dev) -{ - int retval; - - /* return without action, if no pending read request */ - if (!dev->rx_active) - return 0; - - /* setup for receiving */ - retval = rf69_set_rx_cfg(dev, &dev->rx_cfg); - if (retval) - return retval; - - /* setup rssi irq */ - retval = rf69_set_dio_mapping(dev->spi, DIO0, DIO_RSSI_DIO0); - if (retval < 0) - return retval; - dev->irq_state[DIO0] = DIO_RSSI_DIO0; - irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); - - /* setup fifo level interrupt */ - retval = rf69_set_fifo_threshold(dev->spi, FIFO_SIZE - FIFO_THRESHOLD); - if (retval < 0) - return retval; - retval = rf69_set_dio_mapping(dev->spi, DIO1, DIO_FIFO_LEVEL); - if (retval < 0) - return retval; - dev->irq_state[DIO1] = DIO_FIFO_LEVEL; - irq_set_irq_type(dev->irq_num[DIO1], IRQ_TYPE_EDGE_RISING); - - /* set module to receiving mode */ - retval = rf69_set_mode(dev->spi, receive); - if (retval < 0) - return retval; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int pi433_receive(void *data) -{ - struct pi433_device *dev = data; - struct spi_device *spi = dev->spi; - int bytes_to_read, bytes_total; - int retval; - - dev->interrupt_rx_allowed = false; - - /* wait for any tx to finish */ - dev_dbg(dev->dev, "rx: going to wait for any tx to finish\n"); - retval = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active); - if (retval) { - /* wait was interrupted */ - dev->interrupt_rx_allowed = true; - wake_up_interruptible(&dev->tx_wait_queue); - return retval; - } - - /* prepare status vars */ - dev->free_in_fifo = FIFO_SIZE; - dev->rx_position = 0; - dev->rx_bytes_dropped = 0; - - /* setup radio module to listen for something "in the air" */ - retval = pi433_start_rx(dev); - if (retval) - return retval; - - /* now check RSSI, if low wait for getting high (RSSI interrupt) */ - while (!(rf69_read_reg(spi, REG_IRQFLAGS1) & MASK_IRQFLAGS1_RSSI)) { - /* allow tx to interrupt us while waiting for high RSSI */ - dev->interrupt_rx_allowed = true; - wake_up_interruptible(&dev->tx_wait_queue); - - /* wait for RSSI level to become high */ - dev_dbg(dev->dev, "rx: going to wait for high RSSI level\n"); - retval = wait_event_interruptible(dev->rx_wait_queue, - rf69_read_reg(spi, REG_IRQFLAGS1) & - MASK_IRQFLAGS1_RSSI); - if (retval) /* wait was interrupted */ - goto abort; - dev->interrupt_rx_allowed = false; - - /* cross check for ongoing tx */ - if (!dev->tx_active) - break; - } - - /* configure payload ready irq */ - retval = rf69_set_dio_mapping(spi, DIO0, DIO_PAYLOAD_READY); - if (retval < 0) - goto abort; - dev->irq_state[DIO0] = DIO_PAYLOAD_READY; - irq_set_irq_type(dev->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); - - /* fixed or unlimited length? */ - if (dev->rx_cfg.fixed_message_length != 0) { - if (dev->rx_cfg.fixed_message_length > dev->rx_buffer_size) { - retval = -1; - goto abort; - } - bytes_total = dev->rx_cfg.fixed_message_length; - dev_dbg(dev->dev, "rx: msg len set to %d by fixed length\n", - bytes_total); - } else { - bytes_total = dev->rx_buffer_size; - dev_dbg(dev->dev, "rx: msg len set to %d as requested by read\n", - bytes_total); - } - - /* length byte enabled? */ - if (dev->rx_cfg.enable_length_byte == OPTION_ON) { - retval = wait_event_interruptible(dev->fifo_wait_queue, - dev->free_in_fifo < FIFO_SIZE); - if (retval) /* wait was interrupted */ - goto abort; - - rf69_read_fifo(spi, (u8 *)&bytes_total, 1); - if (bytes_total > dev->rx_buffer_size) { - retval = -1; - goto abort; - } - dev->free_in_fifo++; - dev_dbg(dev->dev, "rx: msg len reset to %d due to length byte\n", - bytes_total); - } - - /* address byte enabled? */ - if (dev->rx_cfg.enable_address_filtering != filtering_off) { - u8 dummy; - - bytes_total--; - - retval = wait_event_interruptible(dev->fifo_wait_queue, - dev->free_in_fifo < FIFO_SIZE); - if (retval) /* wait was interrupted */ - goto abort; - - rf69_read_fifo(spi, &dummy, 1); - dev->free_in_fifo++; - dev_dbg(dev->dev, "rx: address byte stripped off\n"); - } - - /* get payload */ - while (dev->rx_position < bytes_total) { - if (!(rf69_read_reg(spi, REG_IRQFLAGS2) & MASK_IRQFLAGS2_PAYLOAD_READY)) { - retval = wait_event_interruptible(dev->fifo_wait_queue, - dev->free_in_fifo < FIFO_SIZE); - if (retval) /* wait was interrupted */ - goto abort; - } - - /* need to drop bytes or acquire? */ - if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) - bytes_to_read = dev->rx_bytes_to_drop - - dev->rx_bytes_dropped; - else - bytes_to_read = bytes_total - dev->rx_position; - - /* access the fifo */ - if (bytes_to_read > FIFO_SIZE - dev->free_in_fifo) - bytes_to_read = FIFO_SIZE - dev->free_in_fifo; - retval = rf69_read_fifo(spi, - &dev->rx_buffer[dev->rx_position], - bytes_to_read); - if (retval) /* read failed */ - goto abort; - - dev->free_in_fifo += bytes_to_read; - - /* adjust status vars */ - if (dev->rx_bytes_to_drop > dev->rx_bytes_dropped) - dev->rx_bytes_dropped += bytes_to_read; - else - dev->rx_position += bytes_to_read; - } - - /* rx done, wait was interrupted or error occurred */ -abort: - dev->interrupt_rx_allowed = true; - if (rf69_set_mode(dev->spi, standby)) - pr_err("rf69_set_mode(): radio module failed to go standby\n"); - wake_up_interruptible(&dev->tx_wait_queue); - - if (retval) - return retval; - else - return bytes_total; -} - -static int pi433_tx_thread(void *data) -{ - struct pi433_device *device = data; - struct spi_device *spi = device->spi; - struct pi433_tx_cfg tx_cfg; - size_t size; - bool rx_interrupted = false; - int position, repetitions; - int retval; - - while (1) { - /* wait for fifo to be populated or for request to terminate*/ - dev_dbg(device->dev, "thread: going to wait for new messages\n"); - wait_event_interruptible(device->tx_wait_queue, - (!kfifo_is_empty(&device->tx_fifo) || - kthread_should_stop())); - if (kthread_should_stop()) - return 0; - - /* - * get data from fifo in the following order: - * - tx_cfg - * - size of message - * - message - */ - retval = kfifo_out(&device->tx_fifo, &tx_cfg, sizeof(tx_cfg)); - if (retval != sizeof(tx_cfg)) { - dev_dbg(device->dev, - "reading tx_cfg from fifo failed: got %d byte(s), expected %d\n", - retval, (unsigned int)sizeof(tx_cfg)); - continue; - } - - retval = kfifo_out(&device->tx_fifo, &size, sizeof(size_t)); - if (retval != sizeof(size_t)) { - dev_dbg(device->dev, - "reading msg size from fifo failed: got %d, expected %d\n", - retval, (unsigned int)sizeof(size_t)); - continue; - } - - /* use fixed message length, if requested */ - if (tx_cfg.fixed_message_length != 0) - size = tx_cfg.fixed_message_length; - - /* increase size, if len byte is requested */ - if (tx_cfg.enable_length_byte == OPTION_ON) - size++; - - /* increase size, if adr byte is requested */ - if (tx_cfg.enable_address_byte == OPTION_ON) - size++; - - /* prime buffer */ - memset(device->buffer, 0, size); - position = 0; - - /* add length byte, if requested */ - if (tx_cfg.enable_length_byte == OPTION_ON) - /* - * according to spec, length byte itself must be - * excluded from the length calculation - */ - device->buffer[position++] = size - 1; - - /* add adr byte, if requested */ - if (tx_cfg.enable_address_byte == OPTION_ON) - device->buffer[position++] = tx_cfg.address_byte; - - /* finally get message data from fifo */ - retval = kfifo_out(&device->tx_fifo, &device->buffer[position], - sizeof(device->buffer) - position); - dev_dbg(device->dev, - "read %d message byte(s) from fifo queue.\n", retval); - - /* - * if rx is active, we need to interrupt the waiting for - * incoming telegrams, to be able to send something. - * We are only allowed, if currently no reception takes - * place otherwise we need to wait for the incoming telegram - * to finish - */ - wait_event_interruptible(device->tx_wait_queue, - !device->rx_active || - device->interrupt_rx_allowed); - - /* - * prevent race conditions - * irq will be reenabled after tx config is set - */ - disable_irq(device->irq_num[DIO0]); - device->tx_active = true; - - /* clear fifo, set fifo threshold, set payload length */ - retval = rf69_set_mode(spi, standby); /* this clears the fifo */ - if (retval < 0) - goto abort; - - if (device->rx_active && !rx_interrupted) { - /* - * rx is currently waiting for a telegram; - * we need to set the radio module to standby - */ - rx_interrupted = true; - } - - retval = rf69_set_fifo_threshold(spi, FIFO_THRESHOLD); - if (retval < 0) - goto abort; - if (tx_cfg.enable_length_byte == OPTION_ON) { - retval = rf69_set_payload_length(spi, size * tx_cfg.repetitions); - if (retval < 0) - goto abort; - } else { - retval = rf69_set_payload_length(spi, 0); - if (retval < 0) - goto abort; - } - - /* configure the rf chip */ - retval = rf69_set_tx_cfg(device, &tx_cfg); - if (retval < 0) - goto abort; - - /* enable fifo level interrupt */ - retval = rf69_set_dio_mapping(spi, DIO1, DIO_FIFO_LEVEL); - if (retval < 0) - goto abort; - device->irq_state[DIO1] = DIO_FIFO_LEVEL; - irq_set_irq_type(device->irq_num[DIO1], IRQ_TYPE_EDGE_FALLING); - - /* enable packet sent interrupt */ - retval = rf69_set_dio_mapping(spi, DIO0, DIO_PACKET_SENT); - if (retval < 0) - goto abort; - device->irq_state[DIO0] = DIO_PACKET_SENT; - irq_set_irq_type(device->irq_num[DIO0], IRQ_TYPE_EDGE_RISING); - enable_irq(device->irq_num[DIO0]); /* was disabled by rx active check */ - - /* enable transmission */ - retval = rf69_set_mode(spi, transmit); - if (retval < 0) - goto abort; - - /* transfer this msg (and repetitions) to chip fifo */ - device->free_in_fifo = FIFO_SIZE; - position = 0; - repetitions = tx_cfg.repetitions; - while ((repetitions > 0) && (size > position)) { - if ((size - position) > device->free_in_fifo) { - /* msg to big for fifo - take a part */ - int write_size = device->free_in_fifo; - - device->free_in_fifo = 0; - rf69_write_fifo(spi, - &device->buffer[position], - write_size); - position += write_size; - } else { - /* msg fits into fifo - take all */ - device->free_in_fifo -= size; - repetitions--; - rf69_write_fifo(spi, - &device->buffer[position], - (size - position)); - position = 0; /* reset for next repetition */ - } - - retval = wait_event_interruptible(device->fifo_wait_queue, - device->free_in_fifo > 0); - if (retval) { - dev_dbg(device->dev, "ABORT\n"); - goto abort; - } - } - - /* we are done. Wait for packet to get sent */ - dev_dbg(device->dev, - "thread: wait for packet to get sent/fifo to be empty\n"); - wait_event_interruptible(device->fifo_wait_queue, - device->free_in_fifo == FIFO_SIZE || - kthread_should_stop()); - if (kthread_should_stop()) - return 0; - - /* STOP_TRANSMISSION */ - dev_dbg(device->dev, "thread: Packet sent. Set mode to stby.\n"); - retval = rf69_set_mode(spi, standby); - if (retval < 0) - goto abort; - - /* everything sent? */ - if (kfifo_is_empty(&device->tx_fifo)) { -abort: - if (rx_interrupted) { - rx_interrupted = false; - pi433_start_rx(device); - } - device->tx_active = false; - wake_up_interruptible(&device->rx_wait_queue); - } - } -} - -/*-------------------------------------------------------------------------*/ - -static ssize_t -pi433_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) -{ - struct pi433_instance *instance; - struct pi433_device *device; - int bytes_received; - ssize_t retval; - - /* check, whether internal buffer is big enough for requested size */ - if (size > MAX_MSG_SIZE) - return -EMSGSIZE; - - instance = filp->private_data; - device = instance->device; - - /* just one read request at a time */ - mutex_lock(&device->rx_lock); - if (device->rx_active) { - mutex_unlock(&device->rx_lock); - return -EAGAIN; - } - - device->rx_active = true; - mutex_unlock(&device->rx_lock); - - /* start receiving */ - /* will block until something was received*/ - device->rx_buffer_size = size; - bytes_received = pi433_receive(device); - - /* release rx */ - mutex_lock(&device->rx_lock); - device->rx_active = false; - mutex_unlock(&device->rx_lock); - - /* if read was successful copy to user space*/ - if (bytes_received > 0) { - retval = copy_to_user(buf, device->rx_buffer, bytes_received); - if (retval) - return -EFAULT; - } - - return bytes_received; -} - -static ssize_t -pi433_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) -{ - struct pi433_instance *instance; - struct pi433_device *device; - int retval; - unsigned int required, available, copied; - - instance = filp->private_data; - device = instance->device; - - /* - * check, whether internal buffer (tx thread) is big enough - * for requested size - */ - if (count > MAX_MSG_SIZE) - return -EMSGSIZE; - - /* - * check if tx_cfg has been initialized otherwise we won't be able to - * config the RF trasmitter correctly due to invalid settings - */ - if (!instance->tx_cfg_initialized) { - dev_notice_once(device->dev, - "write: failed due to unconfigured tx_cfg (see PI433_IOC_WR_TX_CFG)\n"); - return -EINVAL; - } - - /* - * write the following sequence into fifo: - * - tx_cfg - * - size of message - * - message - */ - mutex_lock(&device->tx_fifo_lock); - - required = sizeof(instance->tx_cfg) + sizeof(size_t) + count; - available = kfifo_avail(&device->tx_fifo); - if (required > available) { - dev_dbg(device->dev, "write to fifo failed: %d bytes required but %d available\n", - required, available); - mutex_unlock(&device->tx_fifo_lock); - return -EAGAIN; - } - - retval = kfifo_in(&device->tx_fifo, &instance->tx_cfg, - sizeof(instance->tx_cfg)); - if (retval != sizeof(instance->tx_cfg)) - goto abort; - - retval = kfifo_in(&device->tx_fifo, &count, sizeof(size_t)); - if (retval != sizeof(size_t)) - goto abort; - - retval = kfifo_from_user(&device->tx_fifo, buf, count, &copied); - if (retval || copied != count) - goto abort; - - mutex_unlock(&device->tx_fifo_lock); - - /* start transfer */ - wake_up_interruptible(&device->tx_wait_queue); - dev_dbg(device->dev, "write: generated new msg with %d bytes.\n", copied); - - return copied; - -abort: - dev_warn(device->dev, - "write to fifo failed, non recoverable: 0x%x\n", retval); - mutex_unlock(&device->tx_fifo_lock); - return -EAGAIN; -} - -static long pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct pi433_instance *instance; - struct pi433_device *device; - struct pi433_tx_cfg tx_cfg; - void __user *argp = (void __user *)arg; - - /* Check type and command number */ - if (_IOC_TYPE(cmd) != PI433_IOC_MAGIC) - return -ENOTTY; - - instance = filp->private_data; - device = instance->device; - - if (!device) - return -ESHUTDOWN; - - switch (cmd) { - case PI433_IOC_RD_TX_CFG: - if (copy_to_user(argp, &instance->tx_cfg, - sizeof(struct pi433_tx_cfg))) - return -EFAULT; - break; - case PI433_IOC_WR_TX_CFG: - if (copy_from_user(&tx_cfg, argp, sizeof(struct pi433_tx_cfg))) - return -EFAULT; - mutex_lock(&device->tx_fifo_lock); - memcpy(&instance->tx_cfg, &tx_cfg, sizeof(struct pi433_tx_cfg)); - instance->tx_cfg_initialized = true; - mutex_unlock(&device->tx_fifo_lock); - break; - case PI433_IOC_RD_RX_CFG: - if (copy_to_user(argp, &device->rx_cfg, - sizeof(struct pi433_rx_cfg))) - return -EFAULT; - break; - case PI433_IOC_WR_RX_CFG: - mutex_lock(&device->rx_lock); - - /* during pendig read request, change of config not allowed */ - if (device->rx_active) { - mutex_unlock(&device->rx_lock); - return -EAGAIN; - } - - if (copy_from_user(&device->rx_cfg, argp, - sizeof(struct pi433_rx_cfg))) { - mutex_unlock(&device->rx_lock); - return -EFAULT; - } - - mutex_unlock(&device->rx_lock); - break; - default: - return -EINVAL; - } - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int pi433_open(struct inode *inode, struct file *filp) -{ - struct pi433_device *device; - struct pi433_instance *instance; - - mutex_lock(&minor_lock); - device = idr_find(&pi433_idr, iminor(inode)); - mutex_unlock(&minor_lock); - if (!device) { - pr_debug("device: minor %d unknown.\n", iminor(inode)); - return -ENODEV; - } - - instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) - return -ENOMEM; - - /* setup instance data*/ - instance->device = device; - - /* instance data as context */ - filp->private_data = instance; - stream_open(inode, filp); - - return 0; -} - -static int pi433_release(struct inode *inode, struct file *filp) -{ - struct pi433_instance *instance; - - instance = filp->private_data; - kfree(instance); - filp->private_data = NULL; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static int setup_gpio(struct pi433_device *device) -{ - char name[5]; - int retval; - int i; - const irq_handler_t DIO_irq_handler[NUM_DIO] = { - DIO0_irq_handler, - DIO1_irq_handler - }; - - for (i = 0; i < NUM_DIO; i++) { - /* "construct" name and get the gpio descriptor */ - snprintf(name, sizeof(name), "DIO%d", i); - device->gpiod[i] = gpiod_get(&device->spi->dev, name, - 0 /*GPIOD_IN*/); - - if (device->gpiod[i] == ERR_PTR(-ENOENT)) { - dev_dbg(&device->spi->dev, - "Could not find entry for %s. Ignoring.\n", name); - continue; - } - - if (device->gpiod[i] == ERR_PTR(-EBUSY)) - dev_dbg(&device->spi->dev, "%s is busy.\n", name); - - if (IS_ERR(device->gpiod[i])) { - retval = PTR_ERR(device->gpiod[i]); - /* release already allocated gpios */ - for (i--; i >= 0; i--) { - free_irq(device->irq_num[i], device); - gpiod_put(device->gpiod[i]); - } - return retval; - } - - /* configure the pin */ - retval = gpiod_direction_input(device->gpiod[i]); - if (retval) - return retval; - - /* configure irq */ - device->irq_num[i] = gpiod_to_irq(device->gpiod[i]); - if (device->irq_num[i] < 0) { - device->gpiod[i] = ERR_PTR(-EINVAL); - return device->irq_num[i]; - } - retval = request_irq(device->irq_num[i], - DIO_irq_handler[i], - 0, /* flags */ - name, - device); - - if (retval) - return retval; - - dev_dbg(&device->spi->dev, "%s successfully configured\n", name); - } - - return 0; -} - -static void free_gpio(struct pi433_device *device) -{ - int i; - - for (i = 0; i < NUM_DIO; i++) { - /* check if gpiod is valid */ - if (IS_ERR(device->gpiod[i])) - continue; - - free_irq(device->irq_num[i], device); - gpiod_put(device->gpiod[i]); - } -} - -static int pi433_get_minor(struct pi433_device *device) -{ - int retval = -ENOMEM; - - mutex_lock(&minor_lock); - retval = idr_alloc(&pi433_idr, device, 0, N_PI433_MINORS, GFP_KERNEL); - if (retval >= 0) { - device->minor = retval; - retval = 0; - } else if (retval == -ENOSPC) { - dev_err(&device->spi->dev, "too many pi433 devices\n"); - retval = -EINVAL; - } - mutex_unlock(&minor_lock); - return retval; -} - -static void pi433_free_minor(struct pi433_device *dev) -{ - mutex_lock(&minor_lock); - idr_remove(&pi433_idr, dev->minor); - mutex_unlock(&minor_lock); -} - -/*-------------------------------------------------------------------------*/ - -static const struct file_operations pi433_fops = { - .owner = THIS_MODULE, - /* - * REVISIT switch to aio primitives, so that userspace - * gets more complete API coverage. It'll simplify things - * too, except for the locking. - */ - .write = pi433_write, - .read = pi433_read, - .unlocked_ioctl = pi433_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = pi433_open, - .release = pi433_release, - .llseek = no_llseek, -}; - -static int pi433_debugfs_regs_show(struct seq_file *m, void *p) -{ - struct pi433_device *dev; - u8 reg_data[114]; - int i; - char *fmt = "0x%02x, 0x%02x\n"; - int ret; - - dev = m->private; - - mutex_lock(&dev->tx_fifo_lock); - mutex_lock(&dev->rx_lock); - - // wait for on-going operations to finish - ret = wait_event_interruptible(dev->rx_wait_queue, !dev->tx_active); - if (ret) - goto out_unlock; - - ret = wait_event_interruptible(dev->tx_wait_queue, !dev->rx_active); - if (ret) - goto out_unlock; - - // skip FIFO register (0x0) otherwise this can affect some of uC ops - for (i = 1; i < 0x50; i++) - reg_data[i] = rf69_read_reg(dev->spi, i); - - reg_data[REG_TESTLNA] = rf69_read_reg(dev->spi, REG_TESTLNA); - reg_data[REG_TESTPA1] = rf69_read_reg(dev->spi, REG_TESTPA1); - reg_data[REG_TESTPA2] = rf69_read_reg(dev->spi, REG_TESTPA2); - reg_data[REG_TESTDAGC] = rf69_read_reg(dev->spi, REG_TESTDAGC); - reg_data[REG_TESTAFC] = rf69_read_reg(dev->spi, REG_TESTAFC); - - seq_puts(m, "# reg, val\n"); - - for (i = 1; i < 0x50; i++) - seq_printf(m, fmt, i, reg_data[i]); - - seq_printf(m, fmt, REG_TESTLNA, reg_data[REG_TESTLNA]); - seq_printf(m, fmt, REG_TESTPA1, reg_data[REG_TESTPA1]); - seq_printf(m, fmt, REG_TESTPA2, reg_data[REG_TESTPA2]); - seq_printf(m, fmt, REG_TESTDAGC, reg_data[REG_TESTDAGC]); - seq_printf(m, fmt, REG_TESTAFC, reg_data[REG_TESTAFC]); - -out_unlock: - mutex_unlock(&dev->rx_lock); - mutex_unlock(&dev->tx_fifo_lock); - - return ret; -} -DEFINE_SHOW_ATTRIBUTE(pi433_debugfs_regs); - -/*-------------------------------------------------------------------------*/ - -static int pi433_probe(struct spi_device *spi) -{ - struct pi433_device *device; - int retval; - struct dentry *entry; - - /* setup spi parameters */ - spi->mode = 0x00; - spi->bits_per_word = 8; - /* - * spi->max_speed_hz = 10000000; - * 1MHz already set by device tree overlay - */ - - retval = spi_setup(spi); - if (retval) { - dev_dbg(&spi->dev, "configuration of SPI interface failed!\n"); - return retval; - } - - dev_dbg(&spi->dev, - "spi interface setup: mode 0x%2x, %d bits per word, %dhz max speed\n", - spi->mode, spi->bits_per_word, spi->max_speed_hz); - - /* read chip version */ - retval = rf69_get_version(spi); - if (retval < 0) - return retval; - - switch (retval) { - case 0x24: - dev_dbg(&spi->dev, "found pi433 (ver. 0x%x)\n", retval); - break; - default: - dev_dbg(&spi->dev, "unknown chip version: 0x%x\n", retval); - return -ENODEV; - } - - /* Allocate driver data */ - device = kzalloc(sizeof(*device), GFP_KERNEL); - if (!device) - return -ENOMEM; - - /* Initialize the driver data */ - device->spi = spi; - device->rx_active = false; - device->tx_active = false; - device->interrupt_rx_allowed = false; - - /* init rx buffer */ - device->rx_buffer = kmalloc(MAX_MSG_SIZE, GFP_KERNEL); - if (!device->rx_buffer) { - retval = -ENOMEM; - goto RX_failed; - } - - /* init wait queues */ - init_waitqueue_head(&device->tx_wait_queue); - init_waitqueue_head(&device->rx_wait_queue); - init_waitqueue_head(&device->fifo_wait_queue); - - /* init fifo */ - INIT_KFIFO(device->tx_fifo); - - /* init mutexes and locks */ - mutex_init(&device->tx_fifo_lock); - mutex_init(&device->rx_lock); - - /* setup GPIO (including irq_handler) for the different DIOs */ - retval = setup_gpio(device); - if (retval) { - dev_dbg(&spi->dev, "setup of GPIOs failed\n"); - goto GPIO_failed; - } - - /* setup the radio module */ - retval = rf69_set_mode(spi, standby); - if (retval < 0) - goto minor_failed; - retval = rf69_set_data_mode(spi, DATAMODUL_MODE_PACKET); - if (retval < 0) - goto minor_failed; - retval = rf69_enable_amplifier(spi, MASK_PALEVEL_PA0); - if (retval < 0) - goto minor_failed; - retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA1); - if (retval < 0) - goto minor_failed; - retval = rf69_disable_amplifier(spi, MASK_PALEVEL_PA2); - if (retval < 0) - goto minor_failed; - retval = rf69_set_output_power_level(spi, 13); - if (retval < 0) - goto minor_failed; - retval = rf69_set_antenna_impedance(spi, fifty_ohm); - if (retval < 0) - goto minor_failed; - - /* determ minor number */ - retval = pi433_get_minor(device); - if (retval) { - dev_dbg(&spi->dev, "get of minor number failed\n"); - goto minor_failed; - } - - /* create device */ - device->devt = MKDEV(MAJOR(pi433_dev), device->minor); - device->dev = device_create(&pi433_class, - &spi->dev, - device->devt, - device, - "pi433.%d", - device->minor); - if (IS_ERR(device->dev)) { - pr_err("pi433: device register failed\n"); - retval = PTR_ERR(device->dev); - goto device_create_failed; - } else { - dev_dbg(device->dev, - "created device for major %d, minor %d\n", - MAJOR(pi433_dev), - device->minor); - } - - /* start tx thread */ - device->tx_task_struct = kthread_run(pi433_tx_thread, - device, - "pi433.%d_tx_task", - device->minor); - if (IS_ERR(device->tx_task_struct)) { - dev_dbg(device->dev, "start of send thread failed\n"); - retval = PTR_ERR(device->tx_task_struct); - goto send_thread_failed; - } - - /* create cdev */ - device->cdev = cdev_alloc(); - if (!device->cdev) { - dev_dbg(device->dev, "allocation of cdev failed\n"); - retval = -ENOMEM; - goto cdev_failed; - } - device->cdev->owner = THIS_MODULE; - cdev_init(device->cdev, &pi433_fops); - retval = cdev_add(device->cdev, device->devt, 1); - if (retval) { - dev_dbg(device->dev, "register of cdev failed\n"); - goto del_cdev; - } - - /* spi setup */ - spi_set_drvdata(spi, device); - - entry = debugfs_create_dir(dev_name(device->dev), root_dir); - debugfs_create_file("regs", 0400, entry, device, &pi433_debugfs_regs_fops); - - return 0; - -del_cdev: - cdev_del(device->cdev); -cdev_failed: - kthread_stop(device->tx_task_struct); -send_thread_failed: - device_destroy(&pi433_class, device->devt); -device_create_failed: - pi433_free_minor(device); -minor_failed: - free_gpio(device); -GPIO_failed: - kfree(device->rx_buffer); -RX_failed: - kfree(device); - - return retval; -} - -static void pi433_remove(struct spi_device *spi) -{ - struct pi433_device *device = spi_get_drvdata(spi); - - debugfs_lookup_and_remove(dev_name(device->dev), root_dir); - - /* free GPIOs */ - free_gpio(device); - - /* make sure ops on existing fds can abort cleanly */ - device->spi = NULL; - - kthread_stop(device->tx_task_struct); - - device_destroy(&pi433_class, device->devt); - - cdev_del(device->cdev); - - pi433_free_minor(device); - - kfree(device->rx_buffer); - kfree(device); -} - -static const struct of_device_id pi433_dt_ids[] = { - { .compatible = "Smarthome-Wolf,pi433" }, - {}, -}; - -MODULE_DEVICE_TABLE(of, pi433_dt_ids); - -static struct spi_driver pi433_spi_driver = { - .driver = { - .name = "pi433", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(pi433_dt_ids), - }, - .probe = pi433_probe, - .remove = pi433_remove, - - /* - * NOTE: suspend/resume methods are not necessary here. - * We don't do anything except pass the requests to/from - * the underlying controller. The refrigerator handles - * most issues; the controller driver handles the rest. - */ -}; - -/*-------------------------------------------------------------------------*/ - -static int __init pi433_init(void) -{ - int status; - - /* - * If MAX_MSG_SIZE is smaller then FIFO_SIZE, the driver won't - * work stable - risk of buffer overflow - */ - if (MAX_MSG_SIZE < FIFO_SIZE) - return -EINVAL; - - /* - * Claim device numbers. Then register a class - * that will key udev/mdev to add/remove /dev nodes. - * Last, register the driver which manages those device numbers. - */ - status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433"); - if (status < 0) - return status; - - status = class_register(&pi433_class); - if (status) { - unregister_chrdev(MAJOR(pi433_dev), - pi433_spi_driver.driver.name); - return status; - } - - root_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); - - status = spi_register_driver(&pi433_spi_driver); - if (status < 0) { - class_unregister(&pi433_class); - unregister_chrdev(MAJOR(pi433_dev), - pi433_spi_driver.driver.name); - } - - return status; -} - -module_init(pi433_init); - -static void __exit pi433_exit(void) -{ - spi_unregister_driver(&pi433_spi_driver); - class_unregister(&pi433_class); - unregister_chrdev(MAJOR(pi433_dev), pi433_spi_driver.driver.name); - debugfs_remove(root_dir); -} -module_exit(pi433_exit); - -MODULE_AUTHOR("Marcus Wolf, <linux@wolf-entwicklungen.de>"); -MODULE_DESCRIPTION("Driver for Pi433"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:pi433"); |