// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AirSpy SDR driver
*
* Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
/* AirSpy USB API commands (from AirSpy Library) */
enum {
CMD_INVALID = 0x00,
CMD_RECEIVER_MODE = 0x01,
CMD_SI5351C_WRITE = 0x02,
CMD_SI5351C_READ = 0x03,
CMD_R820T_WRITE = 0x04,
CMD_R820T_READ = 0x05,
CMD_SPIFLASH_ERASE = 0x06,
CMD_SPIFLASH_WRITE = 0x07,
CMD_SPIFLASH_READ = 0x08,
CMD_BOARD_ID_READ = 0x09,
CMD_VERSION_STRING_READ = 0x0a,
CMD_BOARD_PARTID_SERIALNO_READ = 0x0b,
CMD_SET_SAMPLE_RATE = 0x0c,
CMD_SET_FREQ = 0x0d,
CMD_SET_LNA_GAIN = 0x0e,
CMD_SET_MIXER_GAIN = 0x0f,
CMD_SET_VGA_GAIN = 0x10,
CMD_SET_LNA_AGC = 0x11,
CMD_SET_MIXER_AGC = 0x12,
CMD_SET_PACKING = 0x13,
};
/*
* bEndpointAddress 0x81 EP 1 IN
* Transfer Type Bulk
* wMaxPacketSize 0x0200 1x 512 bytes
*/
#define MAX_BULK_BUFS (6)
#define BULK_BUFFER_SIZE (128 * 512)
static const struct v4l2_frequency_band bands[] = {
{
.tuner = 0,
.type = V4L2_TUNER_ADC,
.index = 0,
.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
.rangelow = 20000000,
.rangehigh = 20000000,
},
};
static const struct v4l2_frequency_band bands_rf[] = {
{
.tuner = 1,
.type = V4L2_TUNER_RF,
.index = 0,
.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
.rangelow = 24000000,
.rangehigh = 1750000000,
},
};
/* stream formats */
struct airspy_format {
u32 pixelformat;
u32 buffersize;
};
/* format descriptions for capture and preview */
static struct airspy_format formats[] = {
{
.pixelformat = V4L2_SDR_FMT_RU12LE,
.buffersize = BULK_BUFFER_SIZE,
},
};
static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
/* intermediate buffers with raw data from the USB device */
struct airspy_frame_buf {
/* common v4l buffer stuff -- must be first */
struct vb2_v4l2_buffer vb;
struct list_head list;
};
struct airspy {
#define POWER_ON 1
#define USB_STATE_URB_BUF 2
unsigned long flags;
struct device *dev;
struct usb_device *udev;
struct video_device vdev;
struct v4l2_device v4l2_dev;
/* videobuf2 queue and queued buffers list */
struct vb2_queue vb_queue;
struct list_head queued_bufs;
spinlock_t queued_bufs_lock; /* Protects queued_bufs */
unsigned sequence; /* Buffer sequence counter */
unsigned int vb_full; /* vb is full and packets dropped */
/* Note if taking both locks v4l2_lock must always be locked first! */
struct mutex v4l2_lock; /* Protects everything else */
struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */
struct urb *urb_list[MAX_BULK_BUFS];
int buf_num;
unsigned long buf_size;
u8 *buf_list[MAX_BULK_BUFS];
dma_addr_t dma_addr[MAX_BULK_BUFS];
int urbs_initialized;
int urbs_submitted;
/* USB control message buffer */
#define BUF_SIZE 128
u8 buf[BUF_SIZE];
/* Current configuration */
unsigned int f_adc;
unsigned int f_rf;
u32 pixelformat;
u32 buffersize;
/* Controls */
struct v4l2_ctrl_handler hdl;
struct v4l2_ctrl *lna_gain_auto;
struct v4l2_ctrl *lna_gain;
struct v4l2_ctrl *mixer_gain_auto;
struct v4l2_ctrl *mixer_gain;
struct v4l2_ctrl *if_gain;
/* Sample rate calc */
unsigned long jiffies_next;
unsigned int sample;
unsigned int sample_measured;
};
#define airspy_dbg_usb_control_msg(_dev, _r, _t, _v, _i, _b, _l) { \
char *_direction; \
if (_t & USB_DIR_IN) \
_direction = "<<<"; \
else \
_direction = ">>>"; \
dev_dbg(_dev, "%02x %02x %02x %02x %02x %02x %02x %02x %s %*ph\n", \
_t, _r, _v & 0xff, _v >> 8, _i & 0xff, _i >> 8, \
_l & 0xff, _l >> 8, _direction, _l, _b); \
}
/* execute firmware command */
static int airspy_ctrl_msg(struct airspy *s, u8 request, u16 value, u16 index,
u8 *data, u16 size)
{
int ret;
unsigned int pipe;
u8 requesttype;
switch (request) {
case CMD_RECEIVER_MODE:
case CMD_SET_FREQ:
pipe = usb_sndctrlpipe(s->udev, 0);
requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
break;
case CMD_BOARD_ID_READ:
case CMD_VERSION_STRING_READ:
case CMD_BOARD_PARTID_SERIALNO_READ:
case CMD_SET_LNA_GAIN:
case CMD_SET_MIXER_GAIN:
case CMD_SET_VGA_GAIN:
case CMD_SET_LNA_AGC:
case CMD_SET_MIXER_AGC:
pipe = usb_rcvctrlpipe(s->udev, 0);
requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
break;
default:
dev_err(s->dev, "Unknown command %02x\n", request);
ret = -EINVAL;
goto err;
}
/* write request */
if (!(requesttype & USB_DIR_IN))
memcpy(s->buf, data, size);
ret = usb_control_msg(s->udev, pipe, request, requesttyp