960 lines
19 KiB
C
960 lines
19 KiB
C
/*
|
|
* Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
|
|
* Copyright (C) 2012 by Dimitri Stolnikov <horiz0n@gmx.net>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
//#define USE_SSE
|
|
|
|
#ifdef USE_SSE
|
|
#include <immintrin.h>
|
|
#endif
|
|
|
|
#include <libusb.h>
|
|
|
|
/*
|
|
* All libusb callback functions should be marked with the LIBUSB_CALL macro
|
|
* to ensure that they are compiled with the same calling convention as libusb.
|
|
*
|
|
* If the macro isn't available in older libusb versions, we simply define it.
|
|
*/
|
|
#ifndef LIBUSB_CALL
|
|
#define LIBUSB_CALL
|
|
#endif
|
|
|
|
#include "mirisdr.h"
|
|
#include "mirisdr_reg.h"
|
|
#include "tuner_msi001.h"
|
|
|
|
typedef struct mirisdr_tuner {
|
|
/* tuner interface */
|
|
int (*init)(void *);
|
|
int (*exit)(void *);
|
|
int (*set_freq)(void *, uint32_t freq /* Hz */);
|
|
int (*set_bw)(void *, int bw /* Hz */);
|
|
int (*set_gain)(void *, int gain /* dB */);
|
|
int (*set_gain_mode)(void *, int manual);
|
|
} mirisdr_tuner_t;
|
|
|
|
enum mirisdr_async_status {
|
|
mirisdr_INACTIVE = 0,
|
|
mirisdr_CANCELING,
|
|
mirisdr_RUNNING
|
|
};
|
|
|
|
struct mirisdr_dev {
|
|
libusb_context *ctx;
|
|
struct libusb_device_handle *devh;
|
|
uint32_t xfer_buf_num;
|
|
uint32_t xfer_iso_pack;
|
|
uint32_t xfer_buf_len;
|
|
struct libusb_transfer **xfer;
|
|
unsigned char **xfer_buf;
|
|
mirisdr_read_async_cb_t cb;
|
|
void *cb_ctx;
|
|
enum mirisdr_async_status async_status;
|
|
/* adc context */
|
|
uint32_t rate; /* Hz */
|
|
uint32_t adc_clock; /* Hz */
|
|
/* tuner context */
|
|
mirisdr_tuner_t *tuner;
|
|
uint32_t freq; /* Hz */
|
|
int gain; /* dB */
|
|
/* samples context */
|
|
int headerflag;
|
|
uint32_t addr;
|
|
};
|
|
|
|
typedef struct mirisdr_dongle {
|
|
uint16_t vid;
|
|
uint16_t pid;
|
|
const char *name;
|
|
} mirisdr_dongle_t;
|
|
|
|
static mirisdr_dongle_t known_devices[] = {
|
|
{ 0x1df7, 0x2500, "Mirics MSi2500 default (e.g. VTX3D card)" },
|
|
{ 0x04bb, 0x0537, "IO-DATA GV-TV100 stick" }
|
|
};
|
|
|
|
#define DEFAULT_BUF_NUMBER 32
|
|
#define DEFAULT_ISO_PACKETS 8
|
|
#define DEFAULT_BUF_LENGTH (3072 * DEFAULT_ISO_PACKETS)
|
|
|
|
#define DEF_ADC_FREQ 4000000
|
|
|
|
#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
|
|
#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT)
|
|
#define FUNC(group, function) ((group << 8) | function)
|
|
|
|
#define CTRL_TIMEOUT 300
|
|
#define ISO_TIMEOUT 0
|
|
|
|
int _msi001_init(void *dev) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_exit(void *dev) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_set_freq(void *dev, uint32_t freq) {
|
|
return msi001_init(dev, freq);
|
|
}
|
|
|
|
int msi001_set_bw(void *dev, int bw) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_set_lna_gain(void *dev, int32_t gain) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_mixer_gain_set(void *dev, int8_t gain) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_set_enh_gain(void *dev, int32_t gain) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_set_gain(void *dev, int gain) {
|
|
return 0;
|
|
}
|
|
|
|
int msi001_set_gain_mode(void *dev, int manual) {
|
|
return 0;
|
|
}
|
|
|
|
static mirisdr_tuner_t tuner = {
|
|
_msi001_init, msi001_exit,
|
|
msi001_set_freq,
|
|
msi001_set_bw, msi001_set_gain, msi001_set_gain_mode
|
|
};
|
|
|
|
int msi2500_write_reg(mirisdr_dev_t *dev, uint8_t reg, uint32_t val)
|
|
{
|
|
uint16_t wValue = (val & 0xff) << 8 | reg;
|
|
uint16_t wIndex = (val >> 8) & 0xffff;
|
|
|
|
int r = libusb_control_transfer(dev->devh, 0x42, 0x41, wValue, wIndex, NULL, 0, CTRL_TIMEOUT);
|
|
|
|
// fprintf(stderr, "%s: reg %02x -> %06x\n", __FUNCTION__, reg, val);
|
|
return r;
|
|
}
|
|
|
|
void mirisdr_init_baseband(mirisdr_dev_t *dev)
|
|
{
|
|
/* TODO figure out what that does and why it's needed */
|
|
libusb_control_transfer(dev->devh, 0x42, 0x43, 0x0, 0x0, NULL, 0, CTRL_TIMEOUT);
|
|
|
|
/* initialisation */
|
|
|
|
msi2500_write_reg(dev, 0x05, 0x00000c);
|
|
msi2500_write_reg(dev, 0x00, 0x000200);
|
|
msi2500_write_reg(dev, 0x02, 0x004801);
|
|
|
|
/* IF filter bw + compression scheme? */
|
|
msi2500_write_reg(dev, 0x07, 0x0000a5);
|
|
|
|
/* sample rate = 9.14 MS/s */
|
|
msi2500_write_reg(dev, 0x04, 0x04923d);
|
|
msi2500_write_reg(dev, 0x03, 0x01c907);
|
|
|
|
//6M sample rate
|
|
// msi2500_write_reg(dev, 0x04, 0x9220b);
|
|
// msi2500_write_reg(dev, 0x03, 0x14a0b);
|
|
|
|
// doesn't work yet
|
|
// fprintf(stderr, "setting fs\n");
|
|
// mirisdr_set_samp_rate(dev, 8000000);
|
|
|
|
msi2500_write_reg(dev, 0x13, 0x006b46);
|
|
msi2500_write_reg(dev, 0x14, 0x0000f5);
|
|
msi2500_write_reg(dev, 0x12, 0x802800);
|
|
|
|
msi2500_write_reg(dev, 0x29, 0x032201);
|
|
|
|
/* enable lock led, deselect eeprom CS */
|
|
msi2500_write_reg(dev, 0x08, 0x006680);
|
|
|
|
/* tuner init */
|
|
msi2500_write_reg(dev, 0x09, 0x0094b3);
|
|
msi2500_write_reg(dev, 0x09, 0x00800e);
|
|
msi2500_write_reg(dev, 0x09, 0x200256);
|
|
|
|
/* set gains, TODO remove, use tuner driver */
|
|
msi2500_write_reg(dev, 0x09, 0x014281);
|
|
}
|
|
|
|
int mirisdr_deinit_baseband(mirisdr_dev_t *dev)
|
|
{
|
|
int r = 0;
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (dev->tuner && dev->tuner->exit) {
|
|
// r = dev->tuner->exit(dev); /* deinitialize tuner */
|
|
}
|
|
|
|
/* disable lock led */
|
|
msi2500_write_reg(dev, 0x08, 0x006600);
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_get_usb_strings(mirisdr_dev_t *dev, char *manufact, char *product,
|
|
char *serial)
|
|
{
|
|
struct libusb_device_descriptor dd;
|
|
libusb_device *device = NULL;
|
|
const int buf_max = 256;
|
|
int r = 0;
|
|
|
|
if (!dev || !dev->devh)
|
|
return -1;
|
|
|
|
device = libusb_get_device(dev->devh);
|
|
|
|
r = libusb_get_device_descriptor(device, &dd);
|
|
if (r < 0)
|
|
return -1;
|
|
|
|
if (manufact) {
|
|
memset(manufact, 0, buf_max);
|
|
libusb_get_string_descriptor_ascii(dev->devh, dd.iManufacturer,
|
|
(unsigned char *)manufact,
|
|
buf_max);
|
|
}
|
|
|
|
if (product) {
|
|
memset(product, 0, buf_max);
|
|
libusb_get_string_descriptor_ascii(dev->devh, dd.iProduct,
|
|
(unsigned char *)product,
|
|
buf_max);
|
|
}
|
|
|
|
if (serial) {
|
|
memset(serial, 0, buf_max);
|
|
libusb_get_string_descriptor_ascii(dev->devh, dd.iSerialNumber,
|
|
(unsigned char *)serial,
|
|
buf_max);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_set_center_freq(mirisdr_dev_t *dev, uint32_t freq)
|
|
{
|
|
int r = -2;
|
|
|
|
if (!dev || !dev->tuner)
|
|
return -1;
|
|
|
|
if (dev->tuner->set_freq)
|
|
r = dev->tuner->set_freq(dev, freq);
|
|
|
|
if (!r)
|
|
dev->freq = freq;
|
|
else
|
|
dev->freq = 0;
|
|
|
|
return r;
|
|
}
|
|
|
|
uint32_t mirisdr_get_center_freq(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev || !dev->tuner)
|
|
return 0;
|
|
|
|
return dev->freq;
|
|
}
|
|
|
|
int mirisdr_get_tuner_gains(mirisdr_dev_t *dev, int *gains)
|
|
{
|
|
const int msi001_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215,
|
|
240, 290, 340, 420, 430, 450, 470, 490 };
|
|
int len = sizeof(msi001_gains);
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (!gains) { /* no buffer provided, just return the count */
|
|
return len / sizeof(int);
|
|
} else {
|
|
if (len)
|
|
memcpy(gains, msi001_gains, len);
|
|
|
|
return len / sizeof(int);
|
|
}
|
|
}
|
|
|
|
int mirisdr_set_tuner_gain(mirisdr_dev_t *dev, int gain)
|
|
{
|
|
int r = -2;
|
|
|
|
if (!dev || !dev->tuner)
|
|
return -1;
|
|
|
|
if (dev->tuner->set_gain)
|
|
r = dev->tuner->set_gain((void *)dev, gain);
|
|
|
|
if (!r)
|
|
dev->gain = gain;
|
|
else
|
|
dev->gain = 0;
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_get_tuner_gain(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev || !dev->tuner)
|
|
return 0;
|
|
|
|
return dev->gain;
|
|
}
|
|
|
|
int mirisdr_set_tuner_gain_mode(mirisdr_dev_t *dev, int mode)
|
|
{
|
|
int r = -2;
|
|
|
|
if (!dev || !dev->tuner)
|
|
return -1;
|
|
|
|
if (dev->tuner->set_gain_mode)
|
|
r = dev->tuner->set_gain_mode((void *)dev, mode);
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_set_tuner_lna_gain(mirisdr_dev_t *dev, int gain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_set_tuner_mixer_gain(mirisdr_dev_t *dev, int gain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_set_tuner_mixer_enh(mirisdr_dev_t *dev, int enh)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_set_tuner_if_gain(mirisdr_dev_t *dev, int stage, int gain)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_set_sample_rate(mirisdr_dev_t *dev, uint32_t samp_rate)
|
|
{
|
|
int n;
|
|
int r = 0;
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
/* TODO */
|
|
// mirisdr_set_samp_rate(dev, samp_rate);
|
|
|
|
if (r >= 0) {
|
|
if (dev->tuner && dev->tuner->set_bw)
|
|
dev->tuner->set_bw(dev, samp_rate);
|
|
|
|
dev->rate = samp_rate;
|
|
} else {
|
|
dev->rate = 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
uint32_t mirisdr_get_sample_rate(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev)
|
|
return 0;
|
|
|
|
return dev->rate;
|
|
}
|
|
|
|
static mirisdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid)
|
|
{
|
|
unsigned int i;
|
|
mirisdr_dongle_t *device = NULL;
|
|
|
|
for (i = 0; i < sizeof(known_devices)/sizeof(mirisdr_dongle_t); i++ ) {
|
|
if (known_devices[i].vid == vid && known_devices[i].pid == pid) {
|
|
device = &known_devices[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return device;
|
|
}
|
|
|
|
uint32_t mirisdr_get_device_count(void)
|
|
{
|
|
int i;
|
|
libusb_context *ctx;
|
|
libusb_device **list;
|
|
struct libusb_device_descriptor dd;
|
|
uint32_t device_count = 0;
|
|
ssize_t cnt;
|
|
|
|
libusb_init(&ctx);
|
|
|
|
cnt = libusb_get_device_list(ctx, &list);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
libusb_get_device_descriptor(list[i], &dd);
|
|
|
|
if (find_known_device(dd.idVendor, dd.idProduct))
|
|
device_count++;
|
|
}
|
|
|
|
libusb_free_device_list(list, 1);
|
|
|
|
libusb_exit(ctx);
|
|
|
|
return device_count;
|
|
}
|
|
|
|
const char *mirisdr_get_device_name(uint32_t index)
|
|
{
|
|
int i;
|
|
libusb_context *ctx;
|
|
libusb_device **list;
|
|
struct libusb_device_descriptor dd;
|
|
mirisdr_dongle_t *device = NULL;
|
|
uint32_t device_count = 0;
|
|
ssize_t cnt;
|
|
|
|
libusb_init(&ctx);
|
|
|
|
cnt = libusb_get_device_list(ctx, &list);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
libusb_get_device_descriptor(list[i], &dd);
|
|
|
|
device = find_known_device(dd.idVendor, dd.idProduct);
|
|
|
|
if (device) {
|
|
device_count++;
|
|
|
|
if (index == device_count - 1)
|
|
break;
|
|
|
|
device = NULL;
|
|
}
|
|
}
|
|
|
|
libusb_free_device_list(list, 1);
|
|
|
|
libusb_exit(ctx);
|
|
|
|
if (device)
|
|
return device->name;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
int mirisdr_get_device_usb_strings(uint32_t index, char *manufact,
|
|
char *product, char *serial)
|
|
{
|
|
int r = -2;
|
|
int i;
|
|
libusb_context *ctx;
|
|
libusb_device **list;
|
|
struct libusb_device_descriptor dd;
|
|
mirisdr_dev_t devt;
|
|
uint32_t device_count = 0;
|
|
ssize_t cnt;
|
|
|
|
libusb_init(&ctx);
|
|
|
|
cnt = libusb_get_device_list(ctx, &list);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
libusb_get_device_descriptor(list[i], &dd);
|
|
|
|
if (find_known_device(dd.idVendor, dd.idProduct))
|
|
device_count++;
|
|
|
|
if (index == device_count - 1) {
|
|
r = libusb_open(list[i], &devt.devh);
|
|
if (!r) {
|
|
r = mirisdr_get_usb_strings(&devt,
|
|
manufact,
|
|
product,
|
|
serial);
|
|
libusb_close(devt.devh);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
libusb_free_device_list(list, 1);
|
|
|
|
libusb_exit(ctx);
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_open(mirisdr_dev_t **out_dev, uint32_t index)
|
|
{
|
|
int r;
|
|
int i;
|
|
libusb_device **list;
|
|
mirisdr_dev_t *dev = NULL;
|
|
libusb_device *device = NULL;
|
|
uint32_t device_count = 0;
|
|
struct libusb_device_descriptor dd;
|
|
ssize_t cnt;
|
|
|
|
dev = malloc(sizeof(mirisdr_dev_t));
|
|
if (NULL == dev)
|
|
return -ENOMEM;
|
|
|
|
memset(dev, 0, sizeof(mirisdr_dev_t));
|
|
|
|
libusb_init(&dev->ctx);
|
|
|
|
cnt = libusb_get_device_list(dev->ctx, &list);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
device = list[i];
|
|
|
|
libusb_get_device_descriptor(list[i], &dd);
|
|
|
|
if (find_known_device(dd.idVendor, dd.idProduct))
|
|
device_count++;
|
|
|
|
if (index == device_count - 1)
|
|
break;
|
|
|
|
device = NULL;
|
|
}
|
|
|
|
if (!device) {
|
|
r = -1;
|
|
goto err;
|
|
}
|
|
|
|
r = libusb_open(device, &dev->devh);
|
|
if (r < 0) {
|
|
libusb_free_device_list(list, 1);
|
|
fprintf(stderr, "usb_open error %d\n", r);
|
|
goto err;
|
|
}
|
|
|
|
libusb_free_device_list(list, 1);
|
|
|
|
r = libusb_claim_interface(dev->devh, 0);
|
|
if (r < 0) {
|
|
fprintf(stderr, "usb_claim_interface error %d\n", r);
|
|
goto err;
|
|
}
|
|
|
|
dev->adc_clock = DEF_ADC_FREQ;
|
|
|
|
mirisdr_init_baseband(dev);
|
|
|
|
dev->tuner = &tuner; /* so far we have only one tuner */
|
|
|
|
if (dev->tuner->init) {
|
|
r = dev->tuner->init(dev);
|
|
}
|
|
|
|
r = libusb_set_interface_alt_setting(dev->devh, 0, 1);
|
|
|
|
*out_dev = dev;
|
|
|
|
return 0;
|
|
err:
|
|
if (dev) {
|
|
if (dev->ctx)
|
|
libusb_exit(dev->ctx);
|
|
|
|
free(dev);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_close(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev)
|
|
return -1;
|
|
|
|
mirisdr_deinit_baseband(dev);
|
|
|
|
libusb_release_interface(dev->devh, 0);
|
|
libusb_close(dev->devh);
|
|
|
|
libusb_exit(dev->ctx);
|
|
|
|
free(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_reset_buffer(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev)
|
|
return -1;
|
|
|
|
/* TODO: implement */
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_read_sync(mirisdr_dev_t *dev, void *buf, int len, int *n_read)
|
|
{
|
|
if (!dev)
|
|
return -1;
|
|
|
|
// return libusb_bulk_transfer(dev->devh, 0x86, buf, len, n_read, BULK_TIMEOUT);
|
|
return -1;
|
|
}
|
|
|
|
void hexdump(uint8_t *inbuf, int cnt)
|
|
{
|
|
int i;
|
|
for (i = 0; i < cnt; i++) {
|
|
printf("%02x ", inbuf[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
int mirisdr_convert_samples(mirisdr_dev_t *dev, unsigned char* inbuf, int16_t *outsamples, int length)
|
|
{
|
|
int i, j, k, l, block;
|
|
uint32_t offs;
|
|
uint8_t *ip;
|
|
uint32_t address;
|
|
int op = 0;
|
|
|
|
ip = inbuf;
|
|
|
|
block = length / 1024;
|
|
while (block--) {
|
|
/* parse header */
|
|
#if 1
|
|
address = ip[1] + (ip[2] << 8) + (ip[3] << 16);
|
|
if (address != dev->addr)
|
|
fprintf(stderr, "Lost samples!\n");
|
|
|
|
dev->addr = address + (ip[0] >> 7) + 1;
|
|
|
|
if (((ip[5] & 0x40) && dev->headerflag)) {
|
|
hexdump(ip, 16);
|
|
dev->headerflag = 0;
|
|
} else if ((!(ip[5] & 0x40) && !dev->headerflag)) {
|
|
hexdump(ip, 16);
|
|
dev->headerflag = 1;
|
|
}
|
|
#endif
|
|
|
|
/* skip header */
|
|
ip += 16;
|
|
|
|
/* 16-sample-ips per block */
|
|
k = 6;
|
|
while (k--) {
|
|
uint32_t flag;
|
|
for (j = 0; j < 16; j++) {
|
|
for (i = 0; i < 10; i += 5) {
|
|
outsamples[op++] = (ip[i+0] << 6) | ((ip[i+1] & 0x03) << 14);
|
|
outsamples[op++] = ((ip[i+1] & 0xfc) << 4) | ((ip[i+2] & 0x0f) << 12);
|
|
outsamples[op++] = ((ip[i+2] & 0xf0) << 2) | ((ip[i+3] & 0x3f) << 10);
|
|
outsamples[op++] = (ip[i+3] & 0xc0) | (ip[i+4] << 8);
|
|
}
|
|
/* 10 bytes per 8 samples */
|
|
ip += 10;
|
|
}
|
|
|
|
flag = *(uint32_t*)ip;
|
|
flag = 0;
|
|
for (j = 0; j < 16; j++) {
|
|
#ifdef USE_SSE
|
|
/* Hoernchen's SSE accelerated version */
|
|
__m128i* addr = (__m128i*)&(outsamples[op-128+j*8+0]);
|
|
__m128i v;
|
|
switch(flag & 0x3) {
|
|
case 0:
|
|
v = _mm_loadu_si128(addr);
|
|
v = _mm_srai_epi16(v, 2);
|
|
_mm_storeu_si128(addr, v);
|
|
break;
|
|
case 1:
|
|
v = _mm_loadu_si128(addr);
|
|
v = _mm_srai_epi16(v, 1);
|
|
_mm_storeu_si128(addr, v);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
break;
|
|
}
|
|
|
|
#else
|
|
switch (flag & 0x03) {
|
|
case 0:
|
|
outsamples[op-128+j*8+0] >>= 2;
|
|
outsamples[op-128+j*8+1] >>= 2;
|
|
outsamples[op-128+j*8+2] >>= 2;
|
|
outsamples[op-128+j*8+3] >>= 2;
|
|
outsamples[op-128+j*8+4] >>= 2;
|
|
outsamples[op-128+j*8+5] >>= 2;
|
|
outsamples[op-128+j*8+6] >>= 2;
|
|
outsamples[op-128+j*8+7] >>= 2;
|
|
break;
|
|
case 1:
|
|
outsamples[op-128+j*8+0] >>= 1;
|
|
outsamples[op-128+j*8+1] >>= 1;
|
|
outsamples[op-128+j*8+2] >>= 1;
|
|
outsamples[op-128+j*8+3] >>= 1;
|
|
outsamples[op-128+j*8+4] >>= 1;
|
|
outsamples[op-128+j*8+5] >>= 1;
|
|
outsamples[op-128+j*8+6] >>= 1;
|
|
outsamples[op-128+j*8+7] >>= 1;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
break;
|
|
|
|
}
|
|
#endif
|
|
flag >>= 2;
|
|
}
|
|
/* flagbytes */
|
|
ip += 4;
|
|
}
|
|
ip += 24;
|
|
}
|
|
|
|
return op;
|
|
// fwrite(outsamples, op * sizeof(uint16_t) , 1, file);
|
|
}
|
|
|
|
static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer)
|
|
{
|
|
int i, len, total_len = 0;
|
|
static unsigned char* iso_packet_buf;
|
|
mirisdr_dev_t *dev = (mirisdr_dev_t *)xfer->user_data;
|
|
int16_t outsamples[768 * 3 * 8];
|
|
|
|
// if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
|
|
for (i = 0; i < xfer->num_iso_packets; i++) {
|
|
struct libusb_iso_packet_descriptor *pack = &xfer->iso_packet_desc[i];
|
|
|
|
if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
|
|
// fprintf(stderr, "transfer status: %d\n", xfer->status);
|
|
// mirisdr_cancel_async(dev); /* abort async loop */
|
|
}
|
|
|
|
if (pack->actual_length > 0) {
|
|
iso_packet_buf = libusb_get_iso_packet_buffer_simple(xfer, i);
|
|
if (iso_packet_buf) {
|
|
len = mirisdr_convert_samples(dev, iso_packet_buf, outsamples + total_len, pack->actual_length);
|
|
total_len += len;
|
|
}
|
|
// if (pack->actual_length != 3072)
|
|
// fprintf(stderr, "pack%u length:%u, actual_length:%u\n", i, pack->length, pack->actual_length);
|
|
}
|
|
}
|
|
|
|
if (dev->cb)
|
|
dev->cb((uint8_t*)outsamples, total_len * sizeof(int16_t), dev->cb_ctx);
|
|
|
|
/* resubmit transfer */
|
|
if (libusb_submit_transfer(xfer) < 0) {
|
|
fprintf(stderr, "error re-submitting URB\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int _mirisdr_alloc_async_buffers(mirisdr_dev_t *dev)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (!dev->xfer) {
|
|
dev->xfer = malloc(dev->xfer_buf_num *
|
|
sizeof(struct libusb_transfer *));
|
|
|
|
for(i = 0; i < dev->xfer_buf_num; ++i)
|
|
dev->xfer[i] = libusb_alloc_transfer(dev->xfer_iso_pack);
|
|
}
|
|
|
|
if (!dev->xfer_buf) {
|
|
dev->xfer_buf = malloc(dev->xfer_buf_num *
|
|
sizeof(unsigned char *));
|
|
|
|
for(i = 0; i < dev->xfer_buf_num; ++i)
|
|
dev->xfer_buf[i] = malloc(dev->xfer_buf_len);
|
|
}
|
|
|
|
printf("%s\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
static int _mirisdr_free_async_buffers(mirisdr_dev_t *dev)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (dev->xfer) {
|
|
for(i = 0; i < dev->xfer_buf_num; ++i) {
|
|
if (dev->xfer[i]) {
|
|
libusb_free_transfer(dev->xfer[i]);
|
|
}
|
|
}
|
|
|
|
free(dev->xfer);
|
|
dev->xfer = NULL;
|
|
}
|
|
|
|
if (dev->xfer_buf) {
|
|
for(i = 0; i < dev->xfer_buf_num; ++i) {
|
|
if (dev->xfer_buf[i])
|
|
free(dev->xfer_buf[i]);
|
|
}
|
|
|
|
free(dev->xfer_buf);
|
|
dev->xfer_buf = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mirisdr_read_async(mirisdr_dev_t *dev, mirisdr_read_async_cb_t cb, void *ctx,
|
|
uint32_t buf_num, uint32_t buf_len)
|
|
{
|
|
unsigned int i;
|
|
int r, num_iso_pack = 8;
|
|
struct timeval tv = { 1, 0 };
|
|
|
|
if (!dev)
|
|
return -1;
|
|
|
|
dev->cb = cb;
|
|
dev->cb_ctx = ctx;
|
|
|
|
// if (buf_num > 0)
|
|
// dev->xfer_buf_num = buf_num;
|
|
|
|
// else
|
|
dev->xfer_buf_num = DEFAULT_BUF_NUMBER;
|
|
dev->xfer_iso_pack = DEFAULT_ISO_PACKETS;
|
|
|
|
// if (buf_len > 0 && buf_len % 512 == 0) /* len must be multiple of 512 */
|
|
// dev->xfer_buf_len = buf_len;
|
|
// else
|
|
dev->xfer_buf_len = DEFAULT_BUF_LENGTH;
|
|
|
|
_mirisdr_alloc_async_buffers(dev);
|
|
|
|
for(i = 0; i < dev->xfer_buf_num; ++i) {
|
|
libusb_fill_iso_transfer(dev->xfer[i],
|
|
dev->devh,
|
|
0x81,
|
|
dev->xfer_buf[i],
|
|
dev->xfer_buf_len,
|
|
num_iso_pack,
|
|
_libusb_callback,
|
|
(void *)dev,
|
|
ISO_TIMEOUT);
|
|
|
|
libusb_set_iso_packet_lengths(dev->xfer[i],
|
|
dev->xfer_buf_len/dev->xfer_iso_pack);
|
|
|
|
libusb_submit_transfer(dev->xfer[i]);
|
|
}
|
|
|
|
dev->async_status = mirisdr_RUNNING;
|
|
|
|
while (mirisdr_INACTIVE != dev->async_status) {
|
|
r = libusb_handle_events_timeout(dev->ctx, &tv);
|
|
if (r < 0) {
|
|
fprintf(stderr, "handle_events returned: %d\n", r);
|
|
if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (mirisdr_CANCELING == dev->async_status) {
|
|
dev->async_status = mirisdr_INACTIVE;
|
|
|
|
if (!dev->xfer)
|
|
break;
|
|
|
|
for(i = 0; i < dev->xfer_buf_num; ++i) {
|
|
if (!dev->xfer[i])
|
|
continue;
|
|
|
|
if (dev->xfer[i]->status == LIBUSB_TRANSFER_COMPLETED) {
|
|
libusb_cancel_transfer(dev->xfer[i]);
|
|
dev->async_status = mirisdr_CANCELING;
|
|
}
|
|
}
|
|
|
|
if (mirisdr_INACTIVE == dev->async_status)
|
|
break;
|
|
}
|
|
}
|
|
|
|
_mirisdr_free_async_buffers(dev);
|
|
|
|
return r;
|
|
}
|
|
|
|
int mirisdr_cancel_async(mirisdr_dev_t *dev)
|
|
{
|
|
if (!dev)
|
|
return -1;
|
|
|
|
if (mirisdr_RUNNING == dev->async_status) {
|
|
dev->async_status = mirisdr_CANCELING;
|
|
return 0;
|
|
}
|
|
|
|
return -2;
|
|
}
|
|
|
|
int mirisdr_reg_write_fn(void *dev, uint8_t reg, uint32_t val)
|
|
{
|
|
if (dev)
|
|
return msi2500_write_reg(((mirisdr_dev_t *)dev), reg, val);
|
|
|
|
return -1;
|
|
}
|