2376 lines
66 KiB
C
2376 lines
66 KiB
C
/*
|
|
* HID driver for the Android TV remote
|
|
* providing keys and microphone audio functionality
|
|
*
|
|
* Copyright (C) 2014 Google, Inc.
|
|
* Copyright (c) 2015-2019, NVIDIA CORPORATION, All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/hid.h>
|
|
#include <linux/hiddev.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/iio/imu/tsfw_icm20628.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/list.h>
|
|
#include <linux/ratelimit.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/time.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/version.h>
|
|
#include <sound/core.h>
|
|
#include <sound/control.h>
|
|
#include <sound/info.h>
|
|
#include <sound/initval.h>
|
|
#include <sound/pcm.h>
|
|
|
|
#include "hid-ids.h"
|
|
#include "sbcdec.h"
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
#define snd_atvr_log(...) pr_info("snd_atvr: " __VA_ARGS__)
|
|
|
|
#define JOYSTICK_FUZZ 64
|
|
#define TRIGGER_FUZZ 64
|
|
#define JOYSTICK_FLAT 64
|
|
#define TRIGGER_FLAT 0
|
|
|
|
#define ADPCM_AUDIO_REPORT_ID 30
|
|
|
|
#define MSBC_AUDIO1_REPORT_ID 0xF7
|
|
#define MSBC_AUDIO2_REPORT_ID 0xFA
|
|
#define MSBC_AUDIO3_REPORT_ID 0xFB
|
|
|
|
#define INPUT_REPORT_ID 2
|
|
#define INPUT_EVT_INTR_DATA_ID 10
|
|
|
|
#define KEYCODE_PRESENT_IN_AUDIO_PACKET_FLAG 0x80
|
|
|
|
/* defaults */
|
|
#define MAX_PCM_DEVICES 1
|
|
#define MAX_PCM_SUBSTREAMS 4
|
|
#define MAX_MIDI_DEVICES 0
|
|
|
|
/* Define these all in one place so they stay in sync. */
|
|
#define USE_RATE_MIN 8000
|
|
#define USE_RATE_MAX 16000
|
|
#define USE_RATE_MAX_PEPPER 8000
|
|
#define USE_RATES_ARRAY {USE_RATE_MIN}
|
|
#define USE_RATES_MASK (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000)
|
|
#define USE_RATES_MASK_PEPPER SNDRV_PCM_RATE_8000
|
|
|
|
#define MAX_FRAMES_PER_BUFFER (8192)
|
|
|
|
#define USE_CHANNELS_MIN 1
|
|
#define USE_CHANNELS_MAX 2
|
|
#define USE_CHANNELS_MAX_PEPPER 1
|
|
#define USE_PERIODS_MIN 1
|
|
#define USE_PERIODS_MAX 1024
|
|
|
|
#define MAX_PCM_BUFFER_SIZE (MAX_FRAMES_PER_BUFFER * sizeof(int16_t))
|
|
#define MIN_PERIOD_SIZE 64
|
|
#define MAX_PERIOD_SIZE (MAX_PCM_BUFFER_SIZE / 8)
|
|
#define USE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
|
|
|
#define PACKET_TYPE_ADPCM 0
|
|
#define PACKET_TYPE_MSBC 1
|
|
|
|
/* timer callback occurs every 20ms, and silence max timeout is 5 seconds */
|
|
#define MAX_SILENCE_COUNTER 250
|
|
|
|
/* Normally SBC has a H2 header but because we want
|
|
* to embed keycode support while audio is active without
|
|
* incuring an additional packet in the connection interval,
|
|
* we only use half the H2 header. A normal H2 header has
|
|
* a 12-bit synchronization word and a 2-bit sequence number
|
|
* (SN0, SN1). The sequence number is duplicated, so each
|
|
* pair of bits in the sequence number shall be always 00
|
|
* or 11 (see 5.7.2 of HFP_SPEC_V16). We only receive
|
|
* the second byte of the H2 header that has the latter part
|
|
* of the sync word and the entire sequence number.
|
|
*
|
|
* 0 70 7
|
|
* b100000000001XXYY - where X is SN0 repeated and Y is SN1 repeated
|
|
*
|
|
* So the sequence numbers are:
|
|
* b1000000000010000 - 0x01 0x08 - only the 0x08 is received
|
|
* b1000000000011100 - 0x01 0x38 - only the 0x38 is received
|
|
* b1000000000010011 - 0x01 0xc8 - only the 0xc8 is received
|
|
* b1000000000011111 - 0x01 0xf8 - only the 0xf8 is received
|
|
*
|
|
* Each mSBC frame is split over 3 BLE frames, where each BLE packet has
|
|
* a 20 byte payload.
|
|
* The first BLE packet has the format:
|
|
* byte 0: keycode LSB
|
|
* byte 1: keycode MSB, with most significant bit 0 for no key
|
|
* code active and 1 if keycode is active
|
|
* byte 2: Second byte of H2
|
|
* bytes 3-19: then four byte SBC header, then 13 bytes of audio data
|
|
*
|
|
* The second and third packet are purely 20 bytes of audio
|
|
* data. Second packet arrives on report 0xFA and third packet
|
|
* arrives on report 0xFB.
|
|
*
|
|
* The mSBC decoder works on a mSBC frame, including the four byte SBC header,
|
|
* so we have to accumulate 3 BLE packets before sending it to the decoder.
|
|
*/
|
|
#define NUM_SEQUENCES 4
|
|
const uint8_t msbc_sequence_table[NUM_SEQUENCES] = {0x08, 0x38, 0xc8, 0xf8};
|
|
#define BLE_PACKETS_PER_MSBC_FRAME 3
|
|
#define MSBC_PACKET1_BYTES 17
|
|
#define MSBC_PACKET2_BYTES 20
|
|
#define MSBC_PACKET3_BYTES 20
|
|
|
|
#define BYTES_PER_MSBC_FRAME \
|
|
(MSBC_PACKET1_BYTES + MSBC_PACKET2_BYTES + MSBC_PACKET3_BYTES)
|
|
|
|
const uint8_t msbc_start_offset_in_packet[BLE_PACKETS_PER_MSBC_FRAME] = {
|
|
1, /* SBC header starts after 1 byte sequence num portion of H2 */
|
|
0,
|
|
0
|
|
};
|
|
const uint8_t msbc_start_offset_in_buffer[BLE_PACKETS_PER_MSBC_FRAME] = {
|
|
0,
|
|
MSBC_PACKET1_BYTES,
|
|
MSBC_PACKET1_BYTES + MSBC_PACKET2_BYTES
|
|
};
|
|
const uint8_t msbc_bytes_in_packet[BLE_PACKETS_PER_MSBC_FRAME] = {
|
|
/* includes the SBC header but not the sequence num or keycode */
|
|
MSBC_PACKET1_BYTES,
|
|
MSBC_PACKET2_BYTES,
|
|
MSBC_PACKET3_BYTES
|
|
};
|
|
|
|
struct fifo_packet {
|
|
uint8_t type;
|
|
uint8_t num_bytes;
|
|
/* Expect no more than 20 bytes. But align struct size to power of 2. */
|
|
uint8_t raw_data[1022];
|
|
};
|
|
|
|
#define MAX_SAMPLES_PER_PACKET 128
|
|
#define MIN_SAMPLES_PER_PACKET_P2 32
|
|
#define MAX_PACKETS_PER_BUFFER \
|
|
(MAX_FRAMES_PER_BUFFER / MIN_SAMPLES_PER_PACKET_P2)
|
|
#define MAX_BUFFER_SIZE \
|
|
(MAX_PACKETS_PER_BUFFER * sizeof(struct fifo_packet))
|
|
|
|
#define SND_ATVR_RUNNING_TIMEOUT_MSEC (500)
|
|
|
|
#define TIMER_STATE_BEFORE_DECODE 0
|
|
#define TIMER_STATE_DURING_DECODE 1
|
|
#define TIMER_STATE_AFTER_DECODE 2
|
|
|
|
#define MAX_DEBUG_REPORTS 5
|
|
#define HID_GEN_DESK_DEBUG (HID_UP_GENDESK | 0xFFFF)
|
|
/* Time difference tolerance in ms */
|
|
#define MAX_PACKET_DIFF_TOLERANCE 100
|
|
/*
|
|
* Max time(in ms) below which packet delivery check is done.
|
|
* Once this time passes, time difference checks is ignored as only 2 bytes are
|
|
* used for getting time difference and it rounds off after 65 seconds.
|
|
*/
|
|
#define MAX_TIME_BETWEEN_PACKETS 65000
|
|
|
|
struct hid_debug_data {
|
|
u8 id;
|
|
u8 seq_num;
|
|
ktime_t time;
|
|
};
|
|
|
|
/* move hid device, sound card into per device data structure */
|
|
struct shdr_device {
|
|
struct hid_device *hdev;
|
|
struct snd_card *shdr_card;
|
|
struct tsfw_icm20628_fn_dev *snsr_fns;
|
|
struct tsfw_icm20628_state *st;
|
|
struct work_struct snsr_probe_work;
|
|
struct delayed_work hid_miss_war_work;
|
|
struct mutex hid_miss_war_lock;
|
|
int hid_miss_war_timeout;
|
|
u32 last_ljsx, last_ljsy; /* Last left joystick x, y */
|
|
u32 last_rjsx, last_rjsy; /* Last right joystick x, y */
|
|
struct hid_debug_data debug_info[MAX_DEBUG_REPORTS];
|
|
};
|
|
|
|
/* counter of how many continous silent timer callback in a row */
|
|
static unsigned int silence_counter;
|
|
static int num_remotes;
|
|
static struct mutex snd_cards_lock;
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
|
/* enable all cards by default */
|
|
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
|
/* remember snd cards already in use */
|
|
static bool cards_in_use[SNDRV_CARDS] = {false};
|
|
/* Linux does not like NULL initialization. */
|
|
static char *model[SNDRV_CARDS]; /* = {[0 ... (SNDRV_CARDS - 1)] = NULL}; */
|
|
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
|
|
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
MODULE_PARM_DESC(index, "Index value for SHIELD Remote soundcard.");
|
|
module_param_array(id, charp, NULL, 0444);
|
|
MODULE_PARM_DESC(id, "ID string for SHIELD Remote soundcard.");
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
MODULE_PARM_DESC(enable, "Enable this SHIELD Remote soundcard.");
|
|
module_param_array(model, charp, NULL, 0444);
|
|
MODULE_PARM_DESC(model, "Soundcard model.");
|
|
module_param_array(pcm_devs, int, NULL, 0444);
|
|
MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for SHIELD Remote driver.");
|
|
module_param_array(pcm_substreams, int, NULL, 0444);
|
|
MODULE_PARM_DESC(pcm_substreams,
|
|
"PCM substreams # (1-128) for SHIELD Remote driver?");
|
|
|
|
static void atvr_ts_joystick_missreport_stats_inc(struct hid_device *hdev);
|
|
|
|
/* Debug feature to save captured raw and decoded audio into buffers
|
|
* and make them available for reading from misc devices.
|
|
* It will record the last session only and only up to the buffer size.
|
|
* The recording is cleared on read.
|
|
*/
|
|
#define DEBUG_WITH_MISC_DEVICE 0
|
|
|
|
/* Debug feature to trace audio packets being received */
|
|
#define DEBUG_AUDIO_RECEPTION 0
|
|
|
|
/* Debug feature to trace tx audio packets */
|
|
#define DEBUG_AUDIO_TX 0
|
|
|
|
/* Debug feature to trace HID reports we see */
|
|
/* #define DEBUG_HID_RAW_INPUT */
|
|
|
|
/* Debug timer related issue */
|
|
/* #define DEBUG_TIMER */
|
|
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
static int16_t large_pcm_buffer[1280*1024];
|
|
static int large_pcm_index;
|
|
|
|
static struct miscdevice pcm_dev_node;
|
|
static int pcm_dev_open(struct inode *inode, struct file *file)
|
|
{
|
|
/* nothing special to do here right now. */
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t pcm_dev_read(struct file *file, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
const uint8_t *data = (const uint8_t *)large_pcm_buffer;
|
|
size_t bytes_left = large_pcm_index * sizeof(int16_t) - *ppos;
|
|
if (count > bytes_left)
|
|
count = bytes_left;
|
|
if (copy_to_user(buffer, &data[*ppos], count))
|
|
return -EFAULT;
|
|
|
|
*ppos += count;
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations pcm_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = pcm_dev_open,
|
|
.llseek = no_llseek,
|
|
.read = pcm_dev_read,
|
|
};
|
|
|
|
static uint8_t raw_adpcm_buffer[640*1024];
|
|
static int raw_adpcm_index;
|
|
static struct miscdevice adpcm_dev_node;
|
|
static int adpcm_dev_open(struct inode *inode, struct file *file)
|
|
{
|
|
/* nothing special to do here right now. */
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t adpcm_dev_read(struct file *file, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
size_t bytes_left = raw_adpcm_index - *ppos;
|
|
if (count > bytes_left)
|
|
count = bytes_left;
|
|
if (copy_to_user(buffer, &raw_adpcm_buffer[*ppos], count))
|
|
return -EFAULT;
|
|
|
|
*ppos += count;
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations adpcm_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = adpcm_dev_open,
|
|
.llseek = no_llseek,
|
|
.read = adpcm_dev_read,
|
|
};
|
|
|
|
static uint8_t raw_msbc_buffer[640*1024];
|
|
static int raw_msbc_index;
|
|
static struct miscdevice msbc_dev_node;
|
|
static int msbc_dev_open(struct inode *inode, struct file *file)
|
|
{
|
|
/* nothing special to do here right now. */
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t msbc_dev_read(struct file *file, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
size_t bytes_left = raw_msbc_index - *ppos;
|
|
if (count > bytes_left)
|
|
count = bytes_left;
|
|
if (copy_to_user(buffer, &raw_msbc_buffer[*ppos], count))
|
|
return -EFAULT;
|
|
|
|
*ppos += count;
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations msbc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = msbc_dev_open,
|
|
.llseek = no_llseek,
|
|
.read = msbc_dev_read,
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
struct simple_atomic_fifo {
|
|
/* Read and write cursors are modified by different threads. */
|
|
uint read_cursor;
|
|
uint write_cursor;
|
|
/* Size must be a power of two. */
|
|
uint size;
|
|
/* internal mask is 2*size - 1
|
|
* This allows us to tell the difference between full and empty. */
|
|
uint internal_mask;
|
|
uint external_mask;
|
|
};
|
|
|
|
struct snd_atvr {
|
|
struct snd_card *card;
|
|
struct snd_pcm *pcm;
|
|
struct snd_pcm_hardware pcm_hw;
|
|
|
|
uint32_t sample_rate;
|
|
|
|
uint previous_jiffies; /* Used to detect underflows. */
|
|
uint timeout_jiffies;
|
|
struct timer_list decoding_timer;
|
|
uint timer_state;
|
|
uint32_t timer_enabled;
|
|
uint timer_callback_count;
|
|
|
|
int16_t peak_level;
|
|
struct simple_atomic_fifo fifo_controller;
|
|
struct fifo_packet *fifo_packet_buffer;
|
|
|
|
/* IMA/DVI ADPCM Decoder */
|
|
int pcm_value;
|
|
int step_index;
|
|
bool first_packet;
|
|
|
|
/* msbc decoder */
|
|
uint8_t msbc_frame_data[BYTES_PER_MSBC_FRAME];
|
|
int16_t audio_output[MAX_SAMPLES_PER_PACKET];
|
|
uint8_t packet_in_frame;
|
|
uint8_t seq_index;
|
|
|
|
/*
|
|
* Write_index is the circular buffer position.
|
|
* It is advanced by the BTLE thread after decoding.
|
|
* It is read by ALSA in snd_atvr_pcm_pointer().
|
|
* It is not declared volatile because that is not
|
|
* allowed in the Linux kernel.
|
|
*/
|
|
uint32_t write_index;
|
|
uint32_t frames_per_buffer;
|
|
/* count frames generated so far in this period */
|
|
uint32_t frames_in_period;
|
|
int16_t *pcm_buffer;
|
|
|
|
/* pointer to hid device */
|
|
struct hid_device *hdev;
|
|
struct mutex hdev_lock;
|
|
|
|
int card_index; /* sound card index */
|
|
/* count of packets received from this device */
|
|
int packet_counter;
|
|
spinlock_t s_substream_lock;
|
|
spinlock_t timer_lock;
|
|
uint32_t substream_state;
|
|
bool pcm_stopped;
|
|
};
|
|
|
|
#define ATVR_REMOVE 1
|
|
#define ATVR_TIMER_DISABLED 0
|
|
#define ATVR_TIMER_ENABLED 1
|
|
|
|
#define TS_HOSTCMD_REPORT_SIZE 33
|
|
#define JAR_HOSTCMD_REPORT_SIZE 19
|
|
|
|
static void atvr_hid_miss_stats_inc(void);
|
|
|
|
static int atvr_mic_ctrl(struct hid_device *hdev, bool enable)
|
|
{
|
|
unsigned char report[TS_HOSTCMD_REPORT_SIZE] = {
|
|
0x04, 0x0e, 0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00,
|
|
};
|
|
int ret;
|
|
int report_size = JAR_HOSTCMD_REPORT_SIZE;
|
|
|
|
report[3] = enable ? 0x01 : 0x00;
|
|
hid_info(hdev, "%s remote mic\n", enable ? "enable" : "disable");
|
|
|
|
/* for BLE devices (Pepper, Friday), send the message to userspace so that
|
|
** gattservice can be used to send message to pepper.
|
|
** this is to prevent concurrent mic ctrl msgs */
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_PEPPER ||
|
|
hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY) {
|
|
report[0] = 0x03;
|
|
report[4] = 0x01;
|
|
return hid_report_raw_event(hdev, 0, &report[0],
|
|
report_size, 0);
|
|
}
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE)
|
|
report_size = TS_HOSTCMD_REPORT_SIZE;
|
|
|
|
ret = hid_hw_output_report(hdev, report, report_size);
|
|
if (ret == -ENOSYS)
|
|
ret = hid_hw_raw_request(hdev, report[0], report,
|
|
report_size, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
|
if (ret < 0)
|
|
hid_info(hdev, "failed to send mic ctrl report, err=%d\n", ret);
|
|
else
|
|
ret = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int atvr_ts_sensor_set(struct hid_device *hdev, bool enable)
|
|
{
|
|
u8 report[TS_HOSTCMD_REPORT_SIZE] = { 0x04, 0x5a };
|
|
u8 sample_rate = 70;
|
|
int ret;
|
|
|
|
hid_info(hdev, "%s enable: %d\n", __func__, enable);
|
|
|
|
if (enable) {
|
|
report[3] = 0x01; /* set */
|
|
report[4] = 0x01; /* enable */
|
|
report[5] = 0x00; /* real data source, 0x01 for dummy */
|
|
report[6] = sample_rate;
|
|
hid_info(hdev, "enable ts sensor %dHz\n", sample_rate);
|
|
} else {
|
|
report[3] = 0x01; /* set */
|
|
hid_info(hdev, "disable ts sensor\n");
|
|
}
|
|
|
|
ret = hid_hw_output_report(hdev, report, TS_HOSTCMD_REPORT_SIZE);
|
|
if (ret == -ENOSYS)
|
|
ret = hid_hw_raw_request(hdev, report[0], report,
|
|
TS_HOSTCMD_REPORT_SIZE, HID_OUTPUT_REPORT,
|
|
HID_REQ_SET_REPORT);
|
|
if (ret < 0)
|
|
hid_info(hdev, "failed to send ts sensor ctrl report, err=%d\n",
|
|
ret);
|
|
else
|
|
ret = 0;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(atvr_ts_sensor_set);
|
|
|
|
/***************************************************************************/
|
|
/************* Atomic FIFO *************************************************/
|
|
/***************************************************************************/
|
|
/*
|
|
* This FIFO is atomic if used by no more than 2 threads.
|
|
* One thread modifies the read cursor and the other
|
|
* thread modifies the write_cursor.
|
|
* Size and mask are not modified while being used.
|
|
*
|
|
* The read and write cursors range internally from 0 to (2*size)-1.
|
|
* This allows us to tell the difference between full and empty.
|
|
* When we get the cursors for external use we mask with size-1.
|
|
*
|
|
* Memory barriers required on SMP platforms.
|
|
*/
|
|
static int atomic_fifo_init(struct simple_atomic_fifo *fifo_ptr, uint size)
|
|
{
|
|
/* Make sure size is a power of 2. */
|
|
if ((size & (size-1)) != 0) {
|
|
pr_err("%s:%d - ERROR FIFO size = %d, not power of 2!\n",
|
|
__func__, __LINE__, size);
|
|
return -EINVAL;
|
|
}
|
|
fifo_ptr->read_cursor = 0;
|
|
fifo_ptr->write_cursor = 0;
|
|
fifo_ptr->size = size;
|
|
fifo_ptr->internal_mask = (size * 2) - 1;
|
|
fifo_ptr->external_mask = size - 1;
|
|
smp_wmb();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static uint atomic_fifo_available_to_read(struct simple_atomic_fifo *fifo_ptr)
|
|
{
|
|
smp_rmb();
|
|
return (fifo_ptr->write_cursor - fifo_ptr->read_cursor)
|
|
& fifo_ptr->internal_mask;
|
|
}
|
|
|
|
static uint atomic_fifo_available_to_write(struct simple_atomic_fifo *fifo_ptr)
|
|
{
|
|
smp_rmb();
|
|
return fifo_ptr->size - atomic_fifo_available_to_read(fifo_ptr);
|
|
}
|
|
|
|
static void atomic_fifo_advance_read(
|
|
struct simple_atomic_fifo *fifo_ptr,
|
|
uint frames)
|
|
{
|
|
smp_rmb();
|
|
BUG_ON(frames > atomic_fifo_available_to_read(fifo_ptr));
|
|
fifo_ptr->read_cursor = (fifo_ptr->read_cursor + frames)
|
|
& fifo_ptr->internal_mask;
|
|
smp_wmb();
|
|
}
|
|
|
|
static void atomic_fifo_advance_write(
|
|
struct simple_atomic_fifo *fifo_ptr,
|
|
uint frames)
|
|
{
|
|
smp_rmb();
|
|
BUG_ON(frames > atomic_fifo_available_to_write(fifo_ptr));
|
|
fifo_ptr->write_cursor = (fifo_ptr->write_cursor + frames)
|
|
& fifo_ptr->internal_mask;
|
|
smp_wmb();
|
|
}
|
|
|
|
static uint atomic_fifo_get_read_index(struct simple_atomic_fifo *fifo_ptr)
|
|
{
|
|
smp_rmb();
|
|
return fifo_ptr->read_cursor & fifo_ptr->external_mask;
|
|
}
|
|
|
|
static uint atomic_fifo_get_write_index(struct simple_atomic_fifo *fifo_ptr)
|
|
{
|
|
smp_rmb();
|
|
return fifo_ptr->write_cursor & fifo_ptr->external_mask;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
static void snd_atvr_handle_frame_advance(
|
|
struct snd_pcm_substream *substream, uint num_frames)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
atvr_snd->frames_in_period += num_frames;
|
|
/* Tell ALSA if we have advanced by one or more periods. */
|
|
if (atvr_snd->frames_in_period >= substream->runtime->period_size) {
|
|
snd_pcm_period_elapsed(substream);
|
|
atvr_snd->frames_in_period %= substream->runtime->period_size;
|
|
}
|
|
}
|
|
|
|
static uint32_t snd_atvr_bump_write_index(
|
|
struct snd_pcm_substream *substream,
|
|
uint32_t num_samples)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
uint32_t pos = atvr_snd->write_index;
|
|
|
|
/* Advance write position. */
|
|
pos += num_samples;
|
|
/* Wrap around at end of the circular buffer. */
|
|
pos %= atvr_snd->frames_per_buffer;
|
|
atvr_snd->write_index = pos;
|
|
|
|
snd_atvr_handle_frame_advance(substream, num_samples);
|
|
|
|
return pos;
|
|
}
|
|
|
|
/*
|
|
* Decode an IMA/DVI ADPCM packet and write the PCM data into a circular buffer.
|
|
* ADPCM is 4:1 16kHz@256kbps -> 16kHz@64kbps.
|
|
* ADPCM is 4:1 8kHz@128kbps -> 8kHz@32kbps.
|
|
*/
|
|
static const int ima_index_table[16] = {
|
|
-1, -1, -1, -1, /* +0 - +3, decrease the step size */
|
|
2, 4, 6, 8, /* +4 - +7, increase the step size */
|
|
-1, -1, -1, -1, /* -0 - -3, decrease the step size */
|
|
2, 4, 6, 8 /* -4 - -7, increase the step size */
|
|
};
|
|
static const int16_t ima_step_table[89] = {
|
|
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
|
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
|
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
|
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
|
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
|
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
|
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
|
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
|
};
|
|
|
|
static void decode_adpcm_nibble(uint8_t nibble, struct snd_atvr *atvr_snd,
|
|
struct snd_pcm_substream *substream)
|
|
{
|
|
int step_index = atvr_snd->step_index;
|
|
int value = atvr_snd->pcm_value;
|
|
int step = ima_step_table[step_index];
|
|
int diff;
|
|
|
|
diff = step >> 3;
|
|
if (nibble & 1)
|
|
diff += (step >> 2);
|
|
if (nibble & 2)
|
|
diff += (step >> 1);
|
|
if (nibble & 4)
|
|
diff += step;
|
|
|
|
if (nibble & 8) {
|
|
value -= diff;
|
|
if (value < -32768)
|
|
value = -32768;
|
|
} else {
|
|
value += diff;
|
|
if (value > 32767)
|
|
value = 32767;
|
|
}
|
|
atvr_snd->pcm_value = value;
|
|
|
|
/* copy to stream */
|
|
atvr_snd->pcm_buffer[atvr_snd->write_index] = value;
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
if (large_pcm_index < ARRAY_SIZE(large_pcm_buffer))
|
|
large_pcm_buffer[large_pcm_index++] = value;
|
|
#endif
|
|
snd_atvr_bump_write_index(substream, 1);
|
|
if (value > atvr_snd->peak_level)
|
|
atvr_snd->peak_level = value;
|
|
|
|
/* update step_index */
|
|
step_index += ima_index_table[nibble];
|
|
/* clamp step_index */
|
|
if (step_index < 0)
|
|
step_index = 0;
|
|
else if (step_index >= ARRAY_SIZE(ima_step_table))
|
|
step_index = ARRAY_SIZE(ima_step_table) - 1;
|
|
atvr_snd->step_index = step_index;
|
|
}
|
|
|
|
static int snd_atvr_decode_adpcm_packet(
|
|
struct snd_pcm_substream *substream,
|
|
const uint8_t *adpcm_input,
|
|
size_t num_bytes
|
|
)
|
|
{
|
|
uint i;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
|
|
/* Decode IMA ADPCM data to PCM. */
|
|
if (atvr_snd->first_packet) {
|
|
/* the first two bytes of the first packet
|
|
* is the unencoded first 16-bit sample, high
|
|
* byte first.
|
|
*/
|
|
int value = ((int)adpcm_input[0] << 8) | adpcm_input[1];
|
|
pr_info("%s: first packet, initial value is %d (0x%x, 0x%x)\n",
|
|
__func__, value, adpcm_input[0], adpcm_input[1]);
|
|
atvr_snd->pcm_value = value;
|
|
atvr_snd->pcm_buffer[atvr_snd->write_index] = value;
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
if (raw_adpcm_index < ARRAY_SIZE(raw_adpcm_buffer))
|
|
raw_adpcm_buffer[raw_adpcm_index++] = adpcm_input[0];
|
|
if (raw_adpcm_index < ARRAY_SIZE(raw_adpcm_buffer))
|
|
raw_adpcm_buffer[raw_adpcm_index++] = adpcm_input[1];
|
|
if (large_pcm_index < ARRAY_SIZE(large_pcm_buffer))
|
|
large_pcm_buffer[large_pcm_index++] = value;
|
|
#endif
|
|
snd_atvr_bump_write_index(substream, 1);
|
|
atvr_snd->peak_level = value;
|
|
atvr_snd->first_packet = false;
|
|
i = 2;
|
|
} else {
|
|
i = 0;
|
|
}
|
|
|
|
for (; i < num_bytes; i++) {
|
|
uint8_t raw = adpcm_input[i];
|
|
uint8_t nibble;
|
|
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
if (raw_adpcm_index < ARRAY_SIZE(raw_adpcm_buffer))
|
|
raw_adpcm_buffer[raw_adpcm_index++] = raw;
|
|
#endif
|
|
|
|
/* process first nibble */
|
|
nibble = (raw >> 4) & 0x0f;
|
|
decode_adpcm_nibble(nibble, atvr_snd, substream);
|
|
|
|
/* process second nibble */
|
|
nibble = raw & 0x0f;
|
|
decode_adpcm_nibble(nibble, atvr_snd, substream);
|
|
}
|
|
|
|
return num_bytes * 2;
|
|
}
|
|
|
|
/*
|
|
* Decode an mSBC packet and write the PCM data into a circular buffer.
|
|
*/
|
|
#define BLOCKS_PER_PACKET 15
|
|
#define NUM_BITS 26
|
|
|
|
static int snd_atvr_decode_msbc_packet(
|
|
struct snd_pcm_substream *substream,
|
|
const uint8_t *sbc_input,
|
|
size_t num_bytes
|
|
)
|
|
{
|
|
uint num_samples = 0;
|
|
uint remaining;
|
|
uint i;
|
|
uint32_t pos;
|
|
uint read_index;
|
|
uint write_index;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
if (num_bytes < BYTES_PER_MSBC_FRAME) {
|
|
/* assume we have a BLE frame that needs to be reconstructed */
|
|
if (atvr_snd->packet_in_frame == 0) {
|
|
if (sbc_input[0] !=
|
|
msbc_sequence_table[atvr_snd->seq_index]) {
|
|
|
|
snd_atvr_log(
|
|
"sequence_num err, 0x%02x != 0x%02x\n",
|
|
sbc_input[0],
|
|
msbc_sequence_table[atvr_snd->seq_index]);
|
|
|
|
return 0;
|
|
}
|
|
atvr_snd->seq_index++;
|
|
if (atvr_snd->seq_index == NUM_SEQUENCES)
|
|
atvr_snd->seq_index = 0;
|
|
|
|
/* subtract the sequence number */
|
|
num_bytes--;
|
|
}
|
|
if (num_bytes !=
|
|
msbc_bytes_in_packet[atvr_snd->packet_in_frame]) {
|
|
|
|
pr_err(
|
|
"%s: received %zd audio bytes but expected %d bytes\n",
|
|
__func__, num_bytes,
|
|
msbc_bytes_in_packet[atvr_snd->packet_in_frame]);
|
|
|
|
return 0;
|
|
}
|
|
write_index =
|
|
msbc_start_offset_in_buffer[atvr_snd->packet_in_frame];
|
|
read_index =
|
|
msbc_start_offset_in_packet[atvr_snd->packet_in_frame];
|
|
memcpy(&atvr_snd->msbc_frame_data[write_index],
|
|
&sbc_input[read_index],
|
|
msbc_bytes_in_packet[atvr_snd->packet_in_frame]);
|
|
atvr_snd->packet_in_frame++;
|
|
if (atvr_snd->packet_in_frame < BLE_PACKETS_PER_MSBC_FRAME) {
|
|
/* we don't have a complete mSBC frame yet,
|
|
* just return */
|
|
return 0;
|
|
}
|
|
/* reset for next mSBC frame */
|
|
atvr_snd->packet_in_frame = 0;
|
|
/* we have a complete mSBC frame, send it to the decoder */
|
|
num_samples = sbc_decode(BLOCKS_PER_PACKET, NUM_BITS,
|
|
atvr_snd->msbc_frame_data,
|
|
BYTES_PER_MSBC_FRAME,
|
|
&atvr_snd->audio_output[0]);
|
|
} else {
|
|
/* we have a complete mSBC frame, send it to the decoder */
|
|
num_samples = sbc_decode(BLOCKS_PER_PACKET, NUM_BITS,
|
|
sbc_input,
|
|
BYTES_PER_MSBC_FRAME,
|
|
&atvr_snd->audio_output[0]);
|
|
}
|
|
/* Write PCM data to the buffer. */
|
|
pos = atvr_snd->write_index;
|
|
read_index = 0;
|
|
if ((pos + num_samples) > atvr_snd->frames_per_buffer) {
|
|
for (i = pos; i < atvr_snd->frames_per_buffer; i++) {
|
|
int16_t sample = atvr_snd->audio_output[read_index++];
|
|
if (sample > atvr_snd->peak_level)
|
|
atvr_snd->peak_level = sample;
|
|
atvr_snd->pcm_buffer[i] = sample;
|
|
}
|
|
|
|
remaining = (pos + num_samples) - atvr_snd->frames_per_buffer;
|
|
for (i = 0; i < remaining; i++) {
|
|
int16_t sample = atvr_snd->audio_output[read_index++];
|
|
if (sample > atvr_snd->peak_level)
|
|
atvr_snd->peak_level = sample;
|
|
atvr_snd->pcm_buffer[i] = sample;
|
|
}
|
|
|
|
} else {
|
|
for (i = 0; i < num_samples; i++) {
|
|
int16_t sample = atvr_snd->audio_output[read_index++];
|
|
if (sample > atvr_snd->peak_level)
|
|
atvr_snd->peak_level = sample;
|
|
atvr_snd->pcm_buffer[i + pos] = sample;
|
|
}
|
|
}
|
|
|
|
snd_atvr_bump_write_index(substream, num_samples);
|
|
|
|
return num_samples;
|
|
}
|
|
|
|
static int snd_atvr_alloc_audio_buffs(struct snd_atvr *atvr_snd)
|
|
{
|
|
int ret;
|
|
|
|
ret = atomic_fifo_init(&atvr_snd->fifo_controller,
|
|
MAX_PACKETS_PER_BUFFER);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Allocate the maximum buffer now and then just use part of it when
|
|
* the substream starts. We don't need DMA because it will just
|
|
* get written to by the BTLE code.
|
|
*/
|
|
|
|
if (atvr_snd->pcm_buffer == NULL)
|
|
atvr_snd->pcm_buffer = vmalloc(MAX_PCM_BUFFER_SIZE);
|
|
if (atvr_snd->pcm_buffer == NULL) {
|
|
pr_err("%s:%d - ERROR PCM buffer allocation failed\n",
|
|
__func__, __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (atvr_snd->fifo_packet_buffer == NULL)
|
|
atvr_snd->fifo_packet_buffer = vmalloc(MAX_BUFFER_SIZE);
|
|
if (atvr_snd->fifo_packet_buffer == NULL) {
|
|
pr_err("%s:%d - ERROR buffer allocation failed\n",
|
|
__func__, __LINE__);
|
|
vfree(atvr_snd->pcm_buffer);
|
|
atvr_snd->pcm_buffer = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void snd_atvr_dealloc_audio_buffs(struct snd_atvr *atvr_snd)
|
|
{
|
|
vfree(atvr_snd->pcm_buffer);
|
|
vfree(atvr_snd->fifo_packet_buffer);
|
|
}
|
|
|
|
/**
|
|
* This is called by the event filter when it gets an audio packet
|
|
* from the AndroidTV remote. It writes the packet into a FIFO
|
|
* which is then read and decoded by the timer task.
|
|
* @param input pointer to data to be decoded
|
|
* @param num_bytes how many bytes in raw_input
|
|
* @return number of samples decoded or negative error.
|
|
*/
|
|
static void audio_dec(struct hid_device *hdev, const uint8_t *raw_input,
|
|
int type, size_t num_bytes)
|
|
{
|
|
bool dropped_packet = false;
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
struct snd_card *shdr_card;
|
|
struct snd_atvr *atvr_snd;
|
|
unsigned long flags;
|
|
uint writable;
|
|
|
|
if (shdr_dev == NULL)
|
|
return;
|
|
shdr_card = shdr_dev->shdr_card;
|
|
|
|
if (shdr_card == NULL)
|
|
return;
|
|
|
|
atvr_snd = shdr_card->private_data;
|
|
|
|
smp_rmb();
|
|
if (atvr_snd != NULL && atvr_snd->pcm_stopped == false) {
|
|
spin_lock_irqsave(&atvr_snd->s_substream_lock, flags);
|
|
|
|
if (atvr_snd->substream_state & ATVR_REMOVE) {
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock,
|
|
flags);
|
|
return;
|
|
}
|
|
|
|
/* Write data to a FIFO for decoding by the timer task. */
|
|
writable = atomic_fifo_available_to_write(
|
|
&atvr_snd->fifo_controller);
|
|
if (writable > 0) {
|
|
uint fifo_index = atomic_fifo_get_write_index(
|
|
&atvr_snd->fifo_controller);
|
|
struct fifo_packet *packet =
|
|
&atvr_snd->fifo_packet_buffer[fifo_index];
|
|
packet->type = type;
|
|
packet->num_bytes = (uint8_t)num_bytes;
|
|
memcpy(packet->raw_data, raw_input, num_bytes);
|
|
atomic_fifo_advance_write(
|
|
&atvr_snd->fifo_controller, 1);
|
|
} else {
|
|
dropped_packet = true;
|
|
atvr_snd->pcm_stopped = true;
|
|
smp_wmb();
|
|
}
|
|
atvr_snd->packet_counter++;
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock, flags);
|
|
}
|
|
|
|
if (dropped_packet)
|
|
snd_atvr_log("WARNING, raw audio packet dropped, FIFO full\n");
|
|
}
|
|
|
|
/*
|
|
* Note that smp_rmb() is called by snd_atvr_timer_callback()
|
|
* before calling this function.
|
|
*
|
|
* Reads:
|
|
* jiffies
|
|
* atvr_snd->previous_jiffies
|
|
* Writes:
|
|
* atvr_snd->previous_jiffies
|
|
* Returns:
|
|
* num_frames needed to catch up to the current time
|
|
*/
|
|
static uint snd_atvr_calc_frame_advance(struct snd_atvr *atvr_snd)
|
|
{
|
|
/* Determine how much time passed. */
|
|
uint now_jiffies = jiffies;
|
|
uint elapsed_jiffies = now_jiffies - atvr_snd->previous_jiffies;
|
|
/* Convert jiffies to frames. */
|
|
uint frames_by_time = jiffies_to_msecs(elapsed_jiffies)
|
|
* atvr_snd->sample_rate / 1000;
|
|
atvr_snd->previous_jiffies = now_jiffies;
|
|
|
|
/* Don't write more than one buffer full. */
|
|
if (frames_by_time > (atvr_snd->frames_per_buffer - 4))
|
|
frames_by_time = atvr_snd->frames_per_buffer - 4;
|
|
|
|
return frames_by_time;
|
|
}
|
|
|
|
/* Write zeros into the PCM buffer. */
|
|
static uint32_t snd_atvr_write_silence(struct snd_atvr *atvr_snd,
|
|
uint32_t pos,
|
|
int frames_to_advance)
|
|
{
|
|
/* Does it wrap? */
|
|
if ((pos + frames_to_advance) > atvr_snd->frames_per_buffer) {
|
|
/* Write to end of buffer. */
|
|
int16_t *destination = &atvr_snd->pcm_buffer[pos];
|
|
size_t num_frames = atvr_snd->frames_per_buffer - pos;
|
|
size_t num_bytes = num_frames * sizeof(int16_t);
|
|
memset(destination, 0, num_bytes);
|
|
/* Write from start of buffer to new pos. */
|
|
destination = &atvr_snd->pcm_buffer[0];
|
|
num_frames = frames_to_advance - num_frames;
|
|
num_bytes = num_frames * sizeof(int16_t);
|
|
memset(destination, 0, num_bytes);
|
|
} else {
|
|
/* Write within the buffer. */
|
|
int16_t *destination = &atvr_snd->pcm_buffer[pos];
|
|
size_t num_bytes = frames_to_advance * sizeof(int16_t);
|
|
memset(destination, 0, num_bytes);
|
|
}
|
|
/* Advance and wrap write_index */
|
|
pos += frames_to_advance;
|
|
pos %= atvr_snd->frames_per_buffer;
|
|
return pos;
|
|
}
|
|
|
|
/*
|
|
* Called by timer task to decode raw audio data from the FIFO into the PCM
|
|
* buffer. Returns the number of packets decoded.
|
|
*/
|
|
static uint snd_atvr_decode_from_fifo(struct snd_pcm_substream *substream)
|
|
{
|
|
uint i;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
uint readable = atomic_fifo_available_to_read(
|
|
&atvr_snd->fifo_controller);
|
|
for (i = 0; i < readable; i++) {
|
|
uint fifo_index = atomic_fifo_get_read_index(
|
|
&atvr_snd->fifo_controller);
|
|
struct fifo_packet *packet =
|
|
&atvr_snd->fifo_packet_buffer[fifo_index];
|
|
if (packet->type == PACKET_TYPE_ADPCM) {
|
|
snd_atvr_decode_adpcm_packet(substream,
|
|
packet->raw_data,
|
|
packet->num_bytes);
|
|
} else if (packet->type == PACKET_TYPE_MSBC) {
|
|
snd_atvr_decode_msbc_packet(substream,
|
|
packet->raw_data,
|
|
packet->num_bytes);
|
|
} else {
|
|
pr_err("Unknown packet type %d\n", packet->type);
|
|
}
|
|
|
|
atomic_fifo_advance_read(&atvr_snd->fifo_controller, 1);
|
|
}
|
|
|
|
return readable;
|
|
}
|
|
|
|
static int snd_atvr_schedule_timer(struct snd_pcm_substream *substream)
|
|
{
|
|
int ret;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
uint msec_to_sleep = (substream->runtime->period_size * 1000)
|
|
/ atvr_snd->sample_rate;
|
|
uint jiffies_to_sleep = msecs_to_jiffies(msec_to_sleep);
|
|
if (jiffies_to_sleep < 2)
|
|
jiffies_to_sleep = 2;
|
|
ret = mod_timer(&atvr_snd->decoding_timer, jiffies + jiffies_to_sleep);
|
|
if (ret < 0)
|
|
pr_err("%s:%d - ERROR in mod_timer, ret = %d\n",
|
|
__func__, __LINE__, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void snd_atvr_timer_callback(unsigned long data)
|
|
{
|
|
uint readable;
|
|
uint packets_read;
|
|
bool need_silence = false;
|
|
unsigned long flags;
|
|
struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
#ifdef DEBUG_TIMER
|
|
struct timeval t0, t1;
|
|
int diff;
|
|
#endif
|
|
|
|
/* timer_enabled will be false when stopping a stream. */
|
|
spin_lock_irqsave(&atvr_snd->timer_lock, flags);
|
|
if (!(atvr_snd->timer_enabled & ATVR_TIMER_ENABLED)) {
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
return;
|
|
}
|
|
|
|
if (silence_counter > MAX_SILENCE_COUNTER) {
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
printk_ratelimited(KERN_INFO "max silence timeout reached.\n");
|
|
return;
|
|
}
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
|
|
spin_lock_irqsave(&atvr_snd->s_substream_lock, flags);
|
|
if (atvr_snd->substream_state & ATVR_REMOVE) {
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock, flags);
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG_TIMER
|
|
do_gettimeofday(&t0);
|
|
#endif
|
|
atvr_snd->timer_callback_count++;
|
|
|
|
switch (atvr_snd->timer_state) {
|
|
case TIMER_STATE_BEFORE_DECODE:
|
|
readable = atomic_fifo_available_to_read(
|
|
&atvr_snd->fifo_controller);
|
|
if (readable > 0) {
|
|
atvr_snd->timer_state = TIMER_STATE_DURING_DECODE;
|
|
/* Fall through into next state. */
|
|
} else {
|
|
need_silence = true;
|
|
break;
|
|
}
|
|
|
|
case TIMER_STATE_DURING_DECODE:
|
|
packets_read = snd_atvr_decode_from_fifo(substream);
|
|
|
|
if (packets_read > 0) {
|
|
/* Defer timeout */
|
|
atvr_snd->previous_jiffies = jiffies;
|
|
break;
|
|
}
|
|
|
|
smp_rmb();
|
|
|
|
if (atvr_snd->pcm_stopped) {
|
|
atvr_snd->timer_state = TIMER_STATE_AFTER_DECODE;
|
|
/* Decoder died. Overflowed?
|
|
* Fall through into next state. */
|
|
} else if ((jiffies - atvr_snd->previous_jiffies) >
|
|
atvr_snd->timeout_jiffies) {
|
|
pr_debug("%s: audio UNDERFLOW detected\n", __func__);
|
|
/* Not fatal. Reset timeout. */
|
|
atvr_snd->previous_jiffies = jiffies;
|
|
break;
|
|
} else
|
|
break;
|
|
|
|
case TIMER_STATE_AFTER_DECODE:
|
|
need_silence = true;
|
|
break;
|
|
}
|
|
|
|
/* Write silence before and after decoding. */
|
|
if (need_silence) {
|
|
uint frames_to_silence = snd_atvr_calc_frame_advance(atvr_snd);
|
|
atvr_snd->write_index = snd_atvr_write_silence(
|
|
atvr_snd,
|
|
atvr_snd->write_index,
|
|
frames_to_silence);
|
|
#ifdef DEBUG_TIMER
|
|
do_gettimeofday(&t1);
|
|
#endif
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock, flags);
|
|
/* This can cause snd_atvr_pcm_trigger() to be called, which
|
|
* may try to stop the timer. */
|
|
snd_atvr_handle_frame_advance(substream, frames_to_silence);
|
|
} else {
|
|
#ifdef DEBUG_TIMER
|
|
do_gettimeofday(&t1);
|
|
#endif
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock, flags);
|
|
}
|
|
#ifdef DEBUG_TIMER
|
|
/* diff has unit of ms */
|
|
diff = (t1.tv_sec - t0.tv_sec) * 1000 +
|
|
(t1.tv_usec - t0.tv_usec) / 1000;
|
|
if (diff >= 3)
|
|
pr_err("callback took %d ms\n", diff);
|
|
#endif
|
|
|
|
spin_lock_irqsave(&atvr_snd->timer_lock, flags);
|
|
if (need_silence)
|
|
silence_counter += 1;
|
|
else
|
|
silence_counter = 0;
|
|
if (atvr_snd->timer_enabled & ATVR_TIMER_ENABLED)
|
|
snd_atvr_schedule_timer(substream);
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
}
|
|
|
|
static int snd_atvr_timer_start(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&atvr_snd->timer_lock, flags);
|
|
atvr_snd->timer_enabled = ATVR_TIMER_ENABLED;
|
|
silence_counter = 0;
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
|
|
atvr_snd->previous_jiffies = jiffies;
|
|
atvr_snd->timeout_jiffies =
|
|
msecs_to_jiffies(SND_ATVR_RUNNING_TIMEOUT_MSEC);
|
|
atvr_snd->timer_callback_count = 0;
|
|
|
|
snd_atvr_schedule_timer(substream);
|
|
return 0;
|
|
}
|
|
|
|
static void snd_atvr_timer_stop(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&atvr_snd->timer_lock, flags);
|
|
silence_counter = 0;
|
|
if (atvr_snd->timer_enabled)
|
|
atvr_snd->timer_enabled = ATVR_TIMER_DISABLED;
|
|
spin_unlock_irqrestore(&atvr_snd->timer_lock, flags);
|
|
}
|
|
|
|
/* ===================================================================== */
|
|
/*
|
|
* PCM interface
|
|
*/
|
|
|
|
static int snd_atvr_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
#if (DEBUG_AUDIO_TX == 1)
|
|
snd_atvr_log("%s starting audio\n", __func__);
|
|
#endif
|
|
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
large_pcm_index = 0;
|
|
raw_adpcm_index = 0;
|
|
raw_msbc_index = 0;
|
|
#endif
|
|
atvr_snd->packet_counter = 0;
|
|
atvr_snd->peak_level = -32768;
|
|
atvr_snd->previous_jiffies = jiffies;
|
|
atvr_snd->timer_state = TIMER_STATE_BEFORE_DECODE;
|
|
|
|
/* ADPCM decoder state */
|
|
atvr_snd->step_index = 0;
|
|
atvr_snd->pcm_value = 0;
|
|
atvr_snd->first_packet = true;
|
|
|
|
/* msbc decoder */
|
|
atvr_snd->packet_in_frame = 0;
|
|
atvr_snd->seq_index = 0;
|
|
|
|
atvr_snd->pcm_stopped = false;
|
|
smp_wmb();
|
|
snd_atvr_timer_start(substream);
|
|
return 0;
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
#if (DEBUG_AUDIO_TX == 1)
|
|
snd_atvr_log("%s stopping audio, peak = %d, # packets = %d\n",
|
|
__func__, atvr_snd->peak_level,
|
|
atvr_snd->packet_counter);
|
|
#endif
|
|
atvr_snd->pcm_stopped = true;
|
|
smp_wmb();
|
|
snd_atvr_timer_stop(substream);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int snd_atvr_pcm_prepare(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
#if (DEBUG_AUDIO_TX == 1)
|
|
snd_atvr_log("%s, rate = %d, period_size = %d, buffer_size = %d\n",
|
|
__func__, (int) runtime->rate,
|
|
(int) runtime->period_size,
|
|
(int) runtime->buffer_size);
|
|
#endif
|
|
if (runtime->buffer_size > MAX_FRAMES_PER_BUFFER)
|
|
return -EINVAL;
|
|
|
|
atvr_snd->sample_rate = runtime->rate;
|
|
atvr_snd->frames_per_buffer = runtime->buffer_size;
|
|
|
|
return 0; /* TODO - review */
|
|
}
|
|
|
|
static struct snd_pcm_hardware atvr_pcm_hardware = {
|
|
.info = (SNDRV_PCM_INFO_MMAP |
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
SNDRV_PCM_INFO_RESUME |
|
|
SNDRV_PCM_INFO_MMAP_VALID),
|
|
.formats = USE_FORMATS,
|
|
.rates = USE_RATES_MASK,
|
|
.rate_min = USE_RATE_MIN,
|
|
.rate_max = USE_RATE_MAX,
|
|
.channels_min = USE_CHANNELS_MIN,
|
|
.channels_max = USE_CHANNELS_MAX,
|
|
.buffer_bytes_max = MAX_PCM_BUFFER_SIZE,
|
|
.period_bytes_min = MIN_PERIOD_SIZE,
|
|
.period_bytes_max = MAX_PERIOD_SIZE,
|
|
.periods_min = USE_PERIODS_MIN,
|
|
.periods_max = USE_PERIODS_MAX,
|
|
.fifo_size = 0,
|
|
};
|
|
|
|
static struct snd_pcm_hardware atvr_pcm_hardware_pepper = {
|
|
.info = (SNDRV_PCM_INFO_MMAP |
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
SNDRV_PCM_INFO_RESUME |
|
|
SNDRV_PCM_INFO_MMAP_VALID),
|
|
.formats = USE_FORMATS,
|
|
.rates = USE_RATES_MASK_PEPPER,
|
|
.rate_min = USE_RATE_MIN,
|
|
.rate_max = USE_RATE_MAX_PEPPER,
|
|
.channels_min = USE_CHANNELS_MIN,
|
|
.channels_max = USE_CHANNELS_MAX_PEPPER,
|
|
.buffer_bytes_max = MAX_PCM_BUFFER_SIZE,
|
|
.period_bytes_min = MIN_PERIOD_SIZE,
|
|
.period_bytes_max = MAX_PERIOD_SIZE,
|
|
.periods_min = USE_PERIODS_MIN,
|
|
.periods_max = USE_PERIODS_MAX,
|
|
.fifo_size = 0,
|
|
};
|
|
|
|
static int snd_atvr_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *hw_params)
|
|
{
|
|
int ret = 0;
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
|
|
atvr_snd->write_index = 0;
|
|
smp_wmb();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int snd_atvr_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int snd_atvr_pcm_open(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
int ret;
|
|
|
|
mutex_lock(&atvr_snd->hdev_lock);
|
|
if (atvr_snd->hdev == NULL) {
|
|
pr_warn("%s: remote is not ready\n", __func__);
|
|
mutex_unlock(&atvr_snd->hdev_lock);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
ret = atvr_mic_ctrl(atvr_snd->hdev, true);
|
|
mutex_unlock(&atvr_snd->hdev_lock);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
runtime->hw = atvr_snd->pcm_hw;
|
|
if (substream->pcm->device & 1) {
|
|
runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
|
|
runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
|
|
}
|
|
if (substream->pcm->device & 2)
|
|
runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP
|
|
| SNDRV_PCM_INFO_MMAP_VALID);
|
|
|
|
#ifdef DEBUG_TIMER
|
|
snd_atvr_log("%s, built %s %s\n", __func__, __DATE__, __TIME__);
|
|
#endif
|
|
/* Initialize the timer for the opened substream */
|
|
setup_timer(&atvr_snd->decoding_timer, snd_atvr_timer_callback,
|
|
(unsigned long)substream);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int snd_atvr_pcm_close(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
int ret;
|
|
|
|
snd_atvr_timer_stop(substream);
|
|
del_timer_sync(&atvr_snd->decoding_timer);
|
|
|
|
#ifdef DEBUG_TIMER
|
|
if (atvr_snd->timer_callback_count > 0)
|
|
snd_atvr_log("processed %d packets in %d timer callbacks\n",
|
|
atvr_snd->packet_counter,
|
|
atvr_snd->timer_callback_count);
|
|
#endif
|
|
|
|
ret = atomic_fifo_init(&atvr_snd->fifo_controller,
|
|
MAX_PACKETS_PER_BUFFER);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_lock(&atvr_snd->hdev_lock);
|
|
if (atvr_snd->hdev)
|
|
atvr_mic_ctrl(atvr_snd->hdev, false);
|
|
else
|
|
pr_warn("%s: unexpected remote connection lost\n", __func__);
|
|
mutex_unlock(&atvr_snd->hdev_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static snd_pcm_uframes_t snd_atvr_pcm_pointer(
|
|
struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
/* write_index is written by another driver thread */
|
|
smp_rmb();
|
|
return atvr_snd->write_index;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
|
static int snd_atvr_pcm_copy(struct snd_pcm_substream *substream, int channel,
|
|
unsigned long pos, void __user *dst,
|
|
unsigned long count)
|
|
#else
|
|
static int snd_atvr_pcm_copy(struct snd_pcm_substream *substream,
|
|
int channel, snd_pcm_uframes_t pos,
|
|
void __user *dst, snd_pcm_uframes_t count)
|
|
#endif
|
|
{
|
|
struct snd_atvr *atvr_snd = snd_pcm_substream_chip(substream);
|
|
|
|
/* TODO Needs to be modified if we support more than 1 channel. */
|
|
/*
|
|
* Copy from PCM buffer to user memory.
|
|
* Are we reading past the end of the buffer?
|
|
*/
|
|
if ((pos + count) > atvr_snd->frames_per_buffer) {
|
|
const int16_t *source = &atvr_snd->pcm_buffer[pos];
|
|
int16_t __user *destination = dst;
|
|
size_t num_frames = atvr_snd->frames_per_buffer - pos;
|
|
size_t num_bytes = num_frames * sizeof(int16_t);
|
|
if (copy_to_user(destination, source, num_bytes))
|
|
return -EFAULT;
|
|
|
|
source = &atvr_snd->pcm_buffer[0];
|
|
destination += num_frames;
|
|
num_frames = count - num_frames;
|
|
num_bytes = num_frames * sizeof(int16_t);
|
|
if (copy_to_user(destination, source, num_bytes))
|
|
return -EFAULT;
|
|
} else {
|
|
const int16_t *source = &atvr_snd->pcm_buffer[pos];
|
|
int16_t __user *destination = dst;
|
|
size_t num_bytes = count * sizeof(int16_t);
|
|
if (copy_to_user(destination, source, num_bytes))
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
|
static int snd_atvr_pcm_silence(struct snd_pcm_substream *substream, int channel,
|
|
unsigned long pos, unsigned long count)
|
|
#else
|
|
static int snd_atvr_pcm_silence(struct snd_pcm_substream *substream,
|
|
int channel, snd_pcm_uframes_t pos,
|
|
snd_pcm_uframes_t count)
|
|
#endif
|
|
{
|
|
return 0; /* Do nothing. Only used by output? */
|
|
}
|
|
|
|
static struct snd_pcm_ops snd_atvr_pcm_ops_no_buf = {
|
|
.open = snd_atvr_pcm_open,
|
|
.close = snd_atvr_pcm_close,
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
.hw_params = snd_atvr_pcm_hw_params,
|
|
.hw_free = snd_atvr_pcm_hw_free,
|
|
.prepare = snd_atvr_pcm_prepare,
|
|
.trigger = snd_atvr_pcm_trigger,
|
|
.pointer = snd_atvr_pcm_pointer,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
|
.copy_user = snd_atvr_pcm_copy,
|
|
.fill_silence = snd_atvr_pcm_silence,
|
|
#else
|
|
.copy = snd_atvr_pcm_copy,
|
|
.silence = snd_atvr_pcm_silence,
|
|
#endif
|
|
};
|
|
|
|
static int snd_card_atvr_pcm(struct snd_atvr *atvr_snd,
|
|
int device,
|
|
int substreams)
|
|
{
|
|
struct snd_pcm *pcm;
|
|
struct snd_pcm_ops *ops;
|
|
int err;
|
|
|
|
err = snd_pcm_new(atvr_snd->card, "SHDR PCM", device,
|
|
0, /* no playback substreams */
|
|
1, /* 1 capture substream */
|
|
&pcm);
|
|
if (err < 0)
|
|
return err;
|
|
atvr_snd->pcm = pcm;
|
|
ops = &snd_atvr_pcm_ops_no_buf;
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
|
|
pcm->private_data = atvr_snd;
|
|
pcm->info_flags = 0;
|
|
strlcpy(pcm->name, "SHDR PCM", sizeof(pcm->name));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int atvr_snd_initialize(struct hid_device *hdev,
|
|
struct snd_card **p_shdr_card)
|
|
{
|
|
struct snd_atvr *atvr_snd;
|
|
struct snd_card *shdr_card;
|
|
int err;
|
|
int i;
|
|
int dev = 0;
|
|
struct hid_input *hidinput = list_first_entry_or_null(&hdev->inputs,
|
|
struct hid_input, list);
|
|
struct input_dev *shdr_input_dev;
|
|
|
|
if (hidinput == NULL)
|
|
return -ENODEV;
|
|
|
|
shdr_input_dev = hidinput->input;
|
|
|
|
while (dev < SNDRV_CARDS && cards_in_use[dev])
|
|
dev++;
|
|
|
|
if (dev >= SNDRV_CARDS)
|
|
return -ENODEV;
|
|
if (!enable[dev]) {
|
|
return -ENOENT;
|
|
}
|
|
cards_in_use[dev] = true;
|
|
|
|
err = snd_card_new(&hdev->dev, index[dev], id[dev], THIS_MODULE,
|
|
sizeof(struct snd_atvr), &shdr_card);
|
|
if (err < 0) {
|
|
pr_err("%s: snd_card_new() returned err %d\n",
|
|
__func__, err);
|
|
cards_in_use[dev] = false;
|
|
return err;
|
|
}
|
|
*p_shdr_card = shdr_card;
|
|
atvr_snd = shdr_card->private_data;
|
|
atvr_snd->card = shdr_card;
|
|
atvr_snd->card_index = dev;
|
|
mutex_init(&atvr_snd->hdev_lock);
|
|
spin_lock_init(&atvr_snd->s_substream_lock);
|
|
spin_lock_init(&atvr_snd->timer_lock);
|
|
atvr_snd->substream_state = 0;
|
|
err = snd_atvr_alloc_audio_buffs(atvr_snd);
|
|
|
|
if (err)
|
|
goto __nodev;
|
|
/* dummy initialization */
|
|
setup_timer(&atvr_snd->decoding_timer,
|
|
snd_atvr_timer_callback, 0);
|
|
|
|
for (i = 0; i < MAX_PCM_DEVICES && i < pcm_devs[dev]; i++) {
|
|
if (pcm_substreams[dev] < 1)
|
|
pcm_substreams[dev] = 1;
|
|
if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
|
|
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
|
|
err = snd_card_atvr_pcm(atvr_snd, i, pcm_substreams[dev]);
|
|
if (err < 0) {
|
|
pr_err("%s: snd_card_atvr_pcm() returned err %d\n",
|
|
__func__, err);
|
|
goto __nodev;
|
|
}
|
|
}
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_PEPPER ||
|
|
hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY)
|
|
atvr_snd->pcm_hw = atvr_pcm_hardware_pepper;
|
|
else
|
|
atvr_snd->pcm_hw = atvr_pcm_hardware;
|
|
|
|
strlcpy(shdr_card->driver, "SHIELD Rmt Aud", sizeof(shdr_card->driver));
|
|
strlcpy(shdr_card->shortname, "SHDRAudio",
|
|
sizeof(shdr_card->shortname));
|
|
sprintf(shdr_card->longname, "SHIELD Remote %i audio", dev + 1);
|
|
|
|
err = snd_card_register(shdr_card);
|
|
|
|
if (err)
|
|
goto __nodev;
|
|
|
|
return 0;
|
|
|
|
__nodev:
|
|
snd_card_free(shdr_card);
|
|
*p_shdr_card = NULL;
|
|
cards_in_use[dev] = false;
|
|
return err;
|
|
}
|
|
|
|
#define JAR_BUTTON_REPORT_ID 0x01
|
|
#define JAR_BUTTON_REPORT_SIZE 3
|
|
|
|
#define JAR_AUDIO_REPORT_ID 0xFD
|
|
#define JAR_AUDIO_REPORT_SIZE 233
|
|
#define JAR_AUDIO_FRAME_SIZE 0x3A
|
|
|
|
#define TS_BUTTON_REPORT_SIZE 19
|
|
|
|
#define PEP_BUTTON_REPORT_ID 0x2
|
|
#define PEP_BUTTON_REPORT_SIZE 3
|
|
|
|
static void atvr_pepper_button_release(struct work_struct *work)
|
|
{
|
|
struct shdr_device *shdr_dev =
|
|
container_of(work, struct shdr_device, hid_miss_war_work.work);
|
|
u8 fake_button_up[PEP_BUTTON_REPORT_SIZE] = {
|
|
PEP_BUTTON_REPORT_ID, 0x0, 0x0 };
|
|
|
|
hid_report_raw_event(shdr_dev->hdev, 0, fake_button_up,
|
|
sizeof(fake_button_up), 0);
|
|
atvr_hid_miss_stats_inc();
|
|
}
|
|
|
|
static int atvr_jarvis_break_events(struct hid_device *hdev,
|
|
struct hid_report *report,
|
|
u8 *data, int size)
|
|
{
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
unsigned int button_report_id = JAR_BUTTON_REPORT_ID;
|
|
unsigned int button_report_size = JAR_BUTTON_REPORT_SIZE;
|
|
unsigned int audio_report_id = JAR_AUDIO_REPORT_ID;
|
|
unsigned int audio_report_size = JAR_AUDIO_REPORT_SIZE;
|
|
|
|
/* breaks events apart if they are not proper */
|
|
pr_debug("%s: packet 0x%02x#%i\n", __func__, data[0], size);
|
|
|
|
/*
|
|
* break the ts events also similarly,
|
|
* just account for size differences
|
|
*/
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE)
|
|
button_report_size = TS_HOSTCMD_REPORT_SIZE;
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE &&
|
|
report->id == button_report_id) {
|
|
shdr_dev->last_ljsx = (data[10] << 8) | data[9];
|
|
shdr_dev->last_ljsy = (data[12] << 8) | data[11];
|
|
shdr_dev->last_rjsx = (data[14] << 8) | data[13];
|
|
shdr_dev->last_rjsy = (data[16] << 8) | data[15];
|
|
}
|
|
|
|
if ((hdev->product == USB_DEVICE_ID_NVIDIA_PEPPER ||
|
|
hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY) &&
|
|
report->id == PEP_BUTTON_REPORT_ID) {
|
|
int timeout;
|
|
|
|
mutex_lock(&shdr_dev->hid_miss_war_lock);
|
|
timeout = shdr_dev->hid_miss_war_timeout;
|
|
mutex_unlock(&shdr_dev->hid_miss_war_lock);
|
|
if (timeout <= 0)
|
|
return 0;
|
|
|
|
/*
|
|
* data[1] & data[2] will be set whenever a button is pressed
|
|
* and will be all zero when no button is pressed
|
|
* For Pepper, data[1] == 0 && data[2] == 0x80 indicates we are
|
|
* sending a repeat of previous HID event. We should ignore
|
|
* this case. For Friday, we don't need to ignore this case as
|
|
* there is no repeat bit.
|
|
*/
|
|
if (data[1] == 0 && data[2] == 0)
|
|
cancel_delayed_work_sync(&shdr_dev->hid_miss_war_work);
|
|
else if (hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY ||
|
|
(data[1] != 0 || data[2] != 0x80)) {
|
|
cancel_delayed_work_sync(&shdr_dev->hid_miss_war_work);
|
|
schedule_delayed_work(&shdr_dev->hid_miss_war_work,
|
|
msecs_to_jiffies(timeout));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (!((report->id == button_report_id &&
|
|
size >= button_report_size) ||
|
|
(report->id == audio_report_id &&
|
|
size >= audio_report_size)))
|
|
return 0;
|
|
|
|
/*
|
|
* This is a WAR for a CSR issue at the moment
|
|
* (packets get put together)
|
|
*/
|
|
while (size > 0) {
|
|
int amount = 0;
|
|
if ((data[0] == button_report_id) &&
|
|
(size >= button_report_size)) {
|
|
amount = button_report_size;
|
|
hid_report_raw_event(hdev, 0, data,
|
|
amount, 0);
|
|
} else if ((data[0] == audio_report_id) &&
|
|
(size >= audio_report_size)) {
|
|
u8 *frame = &data[1];
|
|
amount = audio_report_size;
|
|
while (frame < &data[JAR_AUDIO_REPORT_SIZE]) {
|
|
audio_dec(hdev, &frame[0], PACKET_TYPE_MSBC,
|
|
JAR_AUDIO_FRAME_SIZE);
|
|
frame = &frame[JAR_AUDIO_FRAME_SIZE];
|
|
}
|
|
} else {
|
|
pr_info("%s: unknown id or broken packet 0x%02x#%i\n",
|
|
__func__, data[0], size);
|
|
break;
|
|
}
|
|
/* skip over the HID_DATP indicator */
|
|
if (size > amount)
|
|
amount++;
|
|
data += amount;
|
|
size -= amount;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void atvr_set_hid_debug_report_idx(struct shdr_device *shdr_dev, u8 id)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < MAX_DEBUG_REPORTS; i++) {
|
|
if (!shdr_dev->debug_info[i].id) {
|
|
pr_debug("%s: report id %d set at index %d", __func__
|
|
, id, i);
|
|
shdr_dev->debug_info[i].id = id;
|
|
break;
|
|
} else if (shdr_dev->debug_info[i].id == id)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static u8 atvr_get_debug_report_idx(struct shdr_device *shdr_dev, u8 id)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < MAX_DEBUG_REPORTS; i++) {
|
|
if (shdr_dev->debug_info[i].id) {
|
|
if (shdr_dev->debug_info[i].id == id) {
|
|
pr_debug("%s: report id %d index is %d", __func__
|
|
, id, i);
|
|
return i;
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
return MAX_DEBUG_REPORTS;
|
|
}
|
|
|
|
static void atvr_process_debug_info(struct hid_debug_data *debug_info, u8 seq,
|
|
u16 fw_time_diff, u8 id)
|
|
{
|
|
/* Check Seq Number */
|
|
ktime_t current_time = ktime_get();
|
|
s64 time_diff;
|
|
u16 packet_delay;
|
|
|
|
time_diff = ktime_ms_delta(current_time,
|
|
debug_info->time);
|
|
packet_delay = abs(time_diff - fw_time_diff);
|
|
|
|
pr_debug("%s:report->id = 0x%x,seq %d fw_diff %d, drv_diff %d, latency %d",
|
|
__func__, id, seq, fw_time_diff,
|
|
(unsigned int)time_diff, packet_delay);
|
|
|
|
if ((u8)(debug_info->seq_num + 1) != seq)
|
|
pr_warn("%s: id:%d seq num missed Prev %d curr %d",
|
|
__func__, id,
|
|
debug_info->seq_num, seq);
|
|
|
|
/* Ignore first packet time diff */
|
|
if (debug_info->time.tv64 &&
|
|
time_diff < MAX_TIME_BETWEEN_PACKETS &&
|
|
packet_delay > MAX_PACKET_DIFF_TOLERANCE)
|
|
pr_warn("%s: id:%d Packet delay:%d ms at seq %d host diff :%lli ms fw diff %d",
|
|
__func__, id, packet_delay, seq,
|
|
time_diff, fw_time_diff);
|
|
debug_info->seq_num = seq;
|
|
debug_info->time = current_time;
|
|
}
|
|
|
|
static int atvr_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|
u8 *data, int size)
|
|
{
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
struct snd_card *shdr_card = shdr_dev->shdr_card;
|
|
struct snd_atvr *atvr_snd;
|
|
void *debug_info;
|
|
u8 idx = atvr_get_debug_report_idx(shdr_dev, report->id);
|
|
/*first byte is seq num and next 2 bytes time diff */
|
|
|
|
if (shdr_card == NULL)
|
|
return 0;
|
|
|
|
/* debug info is present and Min size check */
|
|
if (idx < MAX_DEBUG_REPORTS && size > 4) {
|
|
/*
|
|
* out of last 3 bytes,
|
|
* 1st byte is seq num
|
|
* next 2 bytes is time diff
|
|
*/
|
|
u8 seq = data[size - 3];
|
|
u16 fw_time_diff = data[size - 2] | (data[size - 1] << 8);
|
|
|
|
atvr_process_debug_info(&shdr_dev->debug_info[idx], seq,
|
|
fw_time_diff, data[0]);
|
|
}
|
|
|
|
atvr_snd = shdr_card->private_data;
|
|
|
|
#ifdef DEBUG_HID_RAW_INPUT
|
|
pr_info("%s: report->id = 0x%x, size = %d\n",
|
|
__func__, report->id, size);
|
|
if (size <= 22) {
|
|
u32 i;
|
|
for (i = 0; i < size; i++)
|
|
pr_info("data[%d] = 0x%02x\n", i, data[i]);
|
|
}
|
|
#endif
|
|
|
|
/* WAR for CSR issue */
|
|
if (atvr_jarvis_break_events(hdev, report, data, size))
|
|
return 1;
|
|
|
|
if (report->id == ADPCM_AUDIO_REPORT_ID) {
|
|
/* send the data, minus the report-id in data[0], to the
|
|
* alsa audio decoder driver for ADPCM
|
|
*/
|
|
#if (DEBUG_AUDIO_RECEPTION == 1)
|
|
if (atvr_snd->packet_counter == 0)
|
|
snd_atvr_log("first ADPCM packet received\n");
|
|
#endif
|
|
audio_dec(hdev, &data[1], PACKET_TYPE_ADPCM, size - 1);
|
|
/* we've handled the event */
|
|
return 1;
|
|
} else if (report->id == MSBC_AUDIO1_REPORT_ID) {
|
|
/* first do special case check if there is any
|
|
* keyCode active in this report. if so, we
|
|
* generate the same keyCode but on report 2, which
|
|
* is where normal keys are reported. the keycode
|
|
* is being sent in the audio packet to save packets
|
|
* and over the air bandwidth.
|
|
*/
|
|
if (data[2] & KEYCODE_PRESENT_IN_AUDIO_PACKET_FLAG) {
|
|
u8 key_data[3];
|
|
key_data[0] = INPUT_REPORT_ID;
|
|
key_data[1] = data[1]; /* low byte */
|
|
key_data[2] = data[2]; /* high byte */
|
|
key_data[2] &= ~KEYCODE_PRESENT_IN_AUDIO_PACKET_FLAG;
|
|
hid_report_raw_event(hdev, 0, key_data,
|
|
sizeof(key_data), 0);
|
|
#ifdef DEBUG_HID_RAW_INPUT
|
|
pr_info("%s: generated hid keycode 0x%02x%02x\n",
|
|
__func__, key_data[2], key_data[1]);
|
|
#endif
|
|
}
|
|
|
|
/* send the audio part to the alsa audio decoder for mSBC */
|
|
#if (DEBUG_AUDIO_RECEPTION == 1)
|
|
if (atvr_snd->packet_counter == 0)
|
|
snd_atvr_log("first MSBC packet received\n");
|
|
#endif
|
|
/* strip the one byte report id and two byte keycode field */
|
|
audio_dec(hdev, &data[1 + 2], PACKET_TYPE_MSBC, size - 1 - 2);
|
|
/* we've handled the event */
|
|
return 1;
|
|
} else if ((report->id == MSBC_AUDIO2_REPORT_ID) ||
|
|
(report->id == MSBC_AUDIO3_REPORT_ID)) {
|
|
/* strip the one byte report id */
|
|
audio_dec(hdev, &data[1], PACKET_TYPE_MSBC, size - 1);
|
|
/* we've handled the event */
|
|
return 1;
|
|
} else if (report->id == INPUT_EVT_INTR_DATA_ID) {
|
|
/*Debug infor sent by device*/
|
|
debug_info = kmalloc(sizeof(u8) * size + 1, GFP_ATOMIC);
|
|
if (!debug_info) {
|
|
pr_err("%s, Kmalloc failed!", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
*((char *)(debug_info + size)) = '\0';
|
|
memcpy(debug_info, data, size);
|
|
pr_debug("Report ID 10: PID: %d, report %s", hdev->product,
|
|
(char *)debug_info);
|
|
kfree(debug_info);
|
|
} else if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE &&
|
|
(report->id == SENSOR_REPORT_ID ||
|
|
report->id == SENSOR_REPORT_ID_SYN ||
|
|
report->id == SENSOR_REPORT_ID_COMBINED) &&
|
|
shdr_dev->snsr_fns && shdr_dev->snsr_fns->recv) {
|
|
shdr_dev->snsr_fns->recv(shdr_dev->st, data, size);
|
|
/* TODO: ret check */
|
|
if (report->id == SENSOR_REPORT_ID_COMBINED) {
|
|
data[0] = JAR_BUTTON_REPORT_ID;
|
|
hid_report_raw_event(hdev, 0, data,
|
|
TS_BUTTON_REPORT_SIZE, 0);
|
|
}
|
|
/* we've handled the event */
|
|
return 1;
|
|
}
|
|
/* let the event through for regular input processing */
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t atvr_show_hid_miss_war_timeout(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct hid_device *hdev =
|
|
container_of(dev, struct hid_device, dev);
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
|
|
return sprintf(buf, "%d\n", shdr_dev->hid_miss_war_timeout);
|
|
}
|
|
|
|
static ssize_t atvr_store_hid_miss_war_timeout(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct hid_device *hdev =
|
|
container_of(dev, struct hid_device, dev);
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
int val;
|
|
|
|
if (!kstrtoint(buf, 0, &val)) {
|
|
mutex_lock(&shdr_dev->hid_miss_war_lock);
|
|
shdr_dev->hid_miss_war_timeout = val;
|
|
mutex_unlock(&shdr_dev->hid_miss_war_lock);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR,
|
|
atvr_show_hid_miss_war_timeout, atvr_store_hid_miss_war_timeout);
|
|
|
|
static void atvr_snsr_probe(struct work_struct *work)
|
|
{
|
|
struct shdr_device *shdr_dev =
|
|
container_of(work, struct shdr_device, snsr_probe_work);
|
|
struct tsfw_icm20628_state *st = NULL;
|
|
|
|
if (shdr_dev->snsr_fns && shdr_dev->snsr_fns->probe)
|
|
shdr_dev->snsr_fns->probe(shdr_dev->hdev, &st);
|
|
/* TODO: ret check */
|
|
shdr_dev->st = st;
|
|
}
|
|
|
|
static int atvr_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
struct snd_atvr *atvr_snd;
|
|
int ret, i;
|
|
struct shdr_device *shdr_dev;
|
|
struct snd_card *shdr_card;
|
|
|
|
shdr_dev = kzalloc(sizeof(*shdr_dev), GFP_KERNEL);
|
|
if (shdr_dev == NULL) {
|
|
hid_err(hdev, "can't alloc descriptor\n");
|
|
return -ENOMEM;
|
|
}
|
|
/* since vendor/product id filter doesn't work yet, because
|
|
* Bluedroid is unable to get the vendor/product id, we
|
|
* have to filter on name
|
|
*/
|
|
pr_info("%s: name = %s, vendor_id = %d, product_id = %d, num %d\n",
|
|
__func__, hdev->name, hdev->vendor, hdev->product, num_remotes);
|
|
|
|
pr_info("%s: Found target remote %s\n", __func__, hdev->name);
|
|
hid_set_drvdata(hdev, shdr_dev);
|
|
/* set seq num to 255 since first packet comes with seq number 0 */
|
|
for (i = 0; i < MAX_DEBUG_REPORTS; i++)
|
|
shdr_dev->debug_info[i].seq_num = 0xFF;
|
|
|
|
ret = hid_parse(hdev);
|
|
if (ret) {
|
|
hid_err(hdev, "hid parse failed\n");
|
|
goto err_parse;
|
|
}
|
|
|
|
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
|
if (ret) {
|
|
hid_err(hdev, "hw start failed\n");
|
|
goto err_start;
|
|
}
|
|
|
|
/*
|
|
* Lazy-creation of the soundcard, and enable the wired headset
|
|
* only then to avoid race conditions on subsequent connections.
|
|
* AudioService.java delays enabling the output
|
|
*/
|
|
mutex_lock(&snd_cards_lock);
|
|
ret = atvr_snd_initialize(hdev, &shdr_card);
|
|
if (ret)
|
|
goto err_stop;
|
|
|
|
/*
|
|
* hdev pointer is not guaranteed to be the same thus following
|
|
* stuff has to be updated every time.
|
|
*/
|
|
shdr_dev->shdr_card = shdr_card;
|
|
shdr_dev->hdev = hdev;
|
|
atvr_snd = shdr_card->private_data;
|
|
atvr_snd->hdev = hdev;
|
|
snd_card_set_dev(shdr_card, &hdev->dev);
|
|
|
|
silence_counter = 0;
|
|
pr_info("%s: remotes count %d->%d\n", __func__,
|
|
num_remotes, num_remotes+1);
|
|
num_remotes++;
|
|
|
|
mutex_unlock(&snd_cards_lock);
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_PEPPER ||
|
|
hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY) {
|
|
shdr_dev->hid_miss_war_timeout = -1;
|
|
mutex_init(&shdr_dev->hid_miss_war_lock);
|
|
INIT_DELAYED_WORK(&shdr_dev->hid_miss_war_work,
|
|
atvr_pepper_button_release);
|
|
|
|
ret = device_create_file(&hdev->dev,
|
|
&dev_attr_timeout);
|
|
if (ret) {
|
|
hid_err(hdev,
|
|
"cannot create sysfs timeout attribute\n");
|
|
goto err_stop;
|
|
}
|
|
ret = kobject_uevent(&hdev->dev.kobj, KOBJ_CHANGE);
|
|
}
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE)
|
|
shdr_dev->snsr_fns = tsfw_icm20628_fns();
|
|
|
|
INIT_WORK(&shdr_dev->snsr_probe_work, atvr_snsr_probe);
|
|
schedule_work(&shdr_dev->snsr_probe_work);
|
|
|
|
return 0;
|
|
err_stop:
|
|
hid_hw_stop(hdev);
|
|
mutex_unlock(&snd_cards_lock);
|
|
err_start:
|
|
err_parse:
|
|
kfree(shdr_dev);
|
|
return ret;
|
|
}
|
|
|
|
static void atvr_remove(struct hid_device *hdev)
|
|
{
|
|
unsigned long flags;
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
struct snd_card *shdr_card = shdr_dev->shdr_card;
|
|
struct snd_atvr *atvr_snd;
|
|
|
|
if (shdr_card == NULL)
|
|
return;
|
|
|
|
cancel_work_sync(&shdr_dev->snsr_probe_work);
|
|
|
|
if (shdr_dev->snsr_fns && shdr_dev->snsr_fns->remove)
|
|
shdr_dev->snsr_fns->remove(shdr_dev->st);
|
|
/* TODO: ret check */
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_PEPPER ||
|
|
hdev->product == USB_DEVICE_ID_NVIDIA_FRIDAY) {
|
|
cancel_delayed_work_sync(&shdr_dev->hid_miss_war_work);
|
|
device_remove_file(&hdev->dev, &dev_attr_timeout);
|
|
}
|
|
|
|
if (hdev->product == USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE && hdev->uniq) {
|
|
if (shdr_dev->last_ljsx == 0 || shdr_dev->last_ljsx == 0xffff ||
|
|
shdr_dev->last_ljsy == 0 || shdr_dev->last_ljsy == 0xffff ||
|
|
shdr_dev->last_rjsx == 0 || shdr_dev->last_rjsx == 0xffff ||
|
|
shdr_dev->last_rjsy == 0 || shdr_dev->last_rjsy == 0xffff) {
|
|
atvr_ts_joystick_missreport_stats_inc(hdev);
|
|
}
|
|
}
|
|
|
|
mutex_lock(&snd_cards_lock);
|
|
atvr_snd = shdr_card->private_data;
|
|
|
|
spin_lock_irqsave(&atvr_snd->s_substream_lock, flags);
|
|
atvr_snd->substream_state |= ATVR_REMOVE;
|
|
spin_unlock_irqrestore(&atvr_snd->s_substream_lock, flags);
|
|
|
|
mutex_lock(&atvr_snd->hdev_lock);
|
|
atvr_snd->hdev = NULL;
|
|
mutex_unlock(&atvr_snd->hdev_lock);
|
|
|
|
hid_set_drvdata(hdev, NULL);
|
|
hid_hw_stop(hdev);
|
|
pr_info("%s: hdev->name = %s removed, num %d->%d\n",
|
|
__func__, hdev->name, num_remotes, num_remotes - 1);
|
|
num_remotes--;
|
|
|
|
cards_in_use[atvr_snd->card_index] = false;
|
|
snd_atvr_dealloc_audio_buffs(atvr_snd);
|
|
mutex_destroy(&atvr_snd->hdev_lock);
|
|
snd_card_disconnect(shdr_card);
|
|
snd_card_free_when_closed(shdr_card);
|
|
mutex_destroy(&shdr_dev->hid_miss_war_lock);
|
|
kfree(shdr_dev);
|
|
mutex_unlock(&snd_cards_lock);
|
|
}
|
|
|
|
static int atvr_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
|
struct hid_field *field, struct hid_usage *usage,
|
|
unsigned long **bit, int *max)
|
|
{
|
|
int a = field->logical_minimum;
|
|
int b = field->logical_maximum;
|
|
int fuzz;
|
|
int flat;
|
|
struct shdr_device *shdr_dev = hid_get_drvdata(hdev);
|
|
|
|
if ((usage->type == EV_ABS) && (field->application == HID_GD_GAMEPAD
|
|
|| field->application == HID_GD_JOYSTICK)) {
|
|
switch (usage->hid) {
|
|
case HID_GD_X:
|
|
case HID_GD_Y:
|
|
case HID_GD_RX:
|
|
case HID_GD_RY:
|
|
fuzz = JOYSTICK_FUZZ;
|
|
flat = JOYSTICK_FLAT;
|
|
break;
|
|
case HID_GD_Z:
|
|
case HID_GD_RZ:
|
|
case 0x200c4: /* For ABS_GAS */
|
|
case 0x200c5: /* For ABS_BRAKE */
|
|
fuzz = TRIGGER_FUZZ;
|
|
flat = TRIGGER_FLAT;
|
|
break;
|
|
default: return 0;/*Use generic mapping for HatX, HatY*/
|
|
}
|
|
set_bit(usage->type, hi->input->evbit);
|
|
set_bit(usage->code, *bit);
|
|
input_set_abs_params(hi->input, usage->code, a, b, fuzz, flat);
|
|
input_abs_set_res(hi->input, usage->code,
|
|
hidinput_calc_abs_res(field, usage->code));
|
|
return -1;
|
|
} else if (usage->hid == HID_GEN_DESK_DEBUG)
|
|
atvr_set_hid_debug_report_idx(shdr_dev, field->report->id);
|
|
return 0;
|
|
}
|
|
|
|
static const struct hid_device_id atvr_devices[] = {
|
|
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_JARVIS)},
|
|
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_PEPPER)},
|
|
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_FRIDAY)},
|
|
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE)},
|
|
{HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_STORMCASTER)},
|
|
{HID_USB_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE)},
|
|
{HID_USB_DEVICE(USB_VENDOR_ID_NVIDIA,
|
|
USB_DEVICE_ID_NVIDIA_STORMCASTER)},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(hid, atvr_devices);
|
|
|
|
static struct hid_driver atvr_driver = {
|
|
.name = "Jarvis",
|
|
.id_table = atvr_devices,
|
|
.input_mapped = atvr_input_mapped,
|
|
.raw_event = atvr_raw_event,
|
|
.probe = atvr_probe,
|
|
.remove = atvr_remove,
|
|
};
|
|
|
|
static int hid_miss_stats;
|
|
static struct mutex stats_lock;
|
|
|
|
static void atvr_hid_miss_stats_inc(void)
|
|
{
|
|
mutex_lock(&stats_lock);
|
|
hid_miss_stats++;
|
|
mutex_unlock(&stats_lock);
|
|
}
|
|
|
|
static ssize_t hid_miss_stats_show(struct device_driver *driver, char *buf)
|
|
{
|
|
int stats;
|
|
|
|
mutex_lock(&stats_lock);
|
|
stats = hid_miss_stats;
|
|
mutex_unlock(&stats_lock);
|
|
|
|
return sprintf(buf, "%d", stats);
|
|
}
|
|
|
|
static ssize_t hid_miss_stats_store(struct device_driver *driver,
|
|
const char *buf, size_t count)
|
|
{
|
|
int val;
|
|
|
|
if (!kstrtoint(buf, 0, &val) && val == 0) {
|
|
mutex_lock(&stats_lock);
|
|
hid_miss_stats = 0;
|
|
mutex_unlock(&stats_lock);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
|
static DRIVER_ATTR_RW(hid_miss_stats);
|
|
#else
|
|
static DRIVER_ATTR(hid_miss_stats, S_IRUGO | S_IWUSR,
|
|
hid_miss_stats_show, hid_miss_stats_store);
|
|
#endif
|
|
|
|
struct ts_joystick_missreport_stat {
|
|
char uniq[17];
|
|
int count;
|
|
struct list_head list;
|
|
};
|
|
|
|
static LIST_HEAD(ts_joystick_stats);
|
|
|
|
static void atvr_ts_joystick_missreport_stats_inc(struct hid_device *hdev)
|
|
{
|
|
struct ts_joystick_missreport_stat *stat;
|
|
|
|
mutex_lock(&stats_lock);
|
|
list_for_each_entry(stat, &ts_joystick_stats, list) {
|
|
if (!strcmp(stat->uniq, hdev->uniq)) {
|
|
stat->count++;
|
|
mutex_unlock(&stats_lock);
|
|
return;
|
|
}
|
|
}
|
|
|
|
stat = kzalloc(sizeof(*stat), GFP_KERNEL);
|
|
if (stat) {
|
|
strcpy(stat->uniq, hdev->uniq);
|
|
stat->count++;
|
|
list_add_tail(&stat->list, &ts_joystick_stats);
|
|
}
|
|
mutex_unlock(&stats_lock);
|
|
}
|
|
|
|
static ssize_t atvr_show_ts_joystick_stats(struct device_driver *driver,
|
|
char *buf)
|
|
{
|
|
struct ts_joystick_missreport_stat *stat;
|
|
int count = 0;
|
|
|
|
mutex_lock(&stats_lock);
|
|
list_for_each_entry(stat, &ts_joystick_stats, list) {
|
|
count += sprintf(buf + count, "%s,%d\n",
|
|
stat->uniq, stat->count);
|
|
}
|
|
mutex_unlock(&stats_lock);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t atvr_store_ts_joystick_stats(struct device_driver *driver,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct ts_joystick_missreport_stat *stat;
|
|
int val;
|
|
|
|
if (!kstrtoint(buf, 0, &val) && val == 0) {
|
|
mutex_lock(&stats_lock);
|
|
list_for_each_entry(stat, &ts_joystick_stats, list) {
|
|
stat->count = 0;
|
|
}
|
|
mutex_unlock(&stats_lock);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static DRIVER_ATTR(ts_joystick_stats, 0644,
|
|
atvr_show_ts_joystick_stats, atvr_store_ts_joystick_stats);
|
|
|
|
static int atvr_init(void)
|
|
{
|
|
int ret;
|
|
|
|
mutex_init(&snd_cards_lock);
|
|
ret = hid_register_driver(&atvr_driver);
|
|
if (ret) {
|
|
pr_err("%s: can't register SHIELD Remote driver\n",
|
|
__func__);
|
|
goto err_hid_register;
|
|
}
|
|
|
|
mutex_init(&stats_lock);
|
|
ret = driver_create_file(&atvr_driver.driver,
|
|
&driver_attr_hid_miss_stats);
|
|
if (ret) {
|
|
pr_err("%s: failed to create driver sysfs node\n", __func__);
|
|
goto err_attr_hid_miss_stats;
|
|
}
|
|
|
|
ret = driver_create_file(&atvr_driver.driver,
|
|
&driver_attr_ts_joystick_stats);
|
|
if (ret) {
|
|
pr_err("%s: failed to create driver sysfs node\n", __func__);
|
|
goto err_attr_ts_joystick_stats;
|
|
}
|
|
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
pcm_dev_node.minor = MISC_DYNAMIC_MINOR;
|
|
pcm_dev_node.name = "snd_atvr_pcm";
|
|
pcm_dev_node.fops = &pcm_fops;
|
|
ret = misc_register(&pcm_dev_node);
|
|
if (ret)
|
|
pr_err("%s: failed to create pcm misc device %d\n",
|
|
__func__, ret);
|
|
else
|
|
pr_info("%s: succeeded creating misc device %s\n",
|
|
__func__, pcm_dev_node.name);
|
|
|
|
adpcm_dev_node.minor = MISC_DYNAMIC_MINOR;
|
|
adpcm_dev_node.name = "snd_atvr_adpcm";
|
|
adpcm_dev_node.fops = &adpcm_fops;
|
|
ret = misc_register(&adpcm_dev_node);
|
|
if (ret)
|
|
pr_err("%s: failed to create adpcm misc device %d\n",
|
|
__func__, ret);
|
|
else
|
|
pr_info("%s: succeeded creating misc device %s\n",
|
|
__func__, adpcm_dev_node.name);
|
|
|
|
msbc_dev_node.minor = MISC_DYNAMIC_MINOR;
|
|
msbc_dev_node.name = "snd_atvr_msbc";
|
|
msbc_dev_node.fops = &msbc_fops;
|
|
ret = misc_register(&msbc_dev_node);
|
|
if (ret)
|
|
pr_err("%s: failed to create mSBC misc device %d\n",
|
|
__func__, ret);
|
|
else
|
|
pr_info("%s: succeeded creating misc device %s\n",
|
|
__func__, msbc_dev_node.name);
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
err_attr_ts_joystick_stats:
|
|
driver_remove_file(&atvr_driver.driver, &driver_attr_hid_miss_stats);
|
|
err_attr_hid_miss_stats:
|
|
hid_unregister_driver(&atvr_driver);
|
|
mutex_destroy(&stats_lock);
|
|
err_hid_register:
|
|
mutex_destroy(&snd_cards_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void atvr_exit(void)
|
|
{
|
|
#if (DEBUG_WITH_MISC_DEVICE == 1)
|
|
misc_deregister(&msbc_dev_node);
|
|
misc_deregister(&adpcm_dev_node);
|
|
misc_deregister(&pcm_dev_node);
|
|
#endif
|
|
|
|
driver_remove_file(&atvr_driver.driver, &driver_attr_hid_miss_stats);
|
|
driver_remove_file(&atvr_driver.driver, &driver_attr_ts_joystick_stats);
|
|
hid_unregister_driver(&atvr_driver);
|
|
mutex_destroy(&snd_cards_lock);
|
|
mutex_destroy(&stats_lock);
|
|
}
|
|
|
|
module_init(atvr_init);
|
|
module_exit(atvr_exit);
|
|
MODULE_LICENSE("GPL");
|