audio: dsp: Import elliptic

From branch: redwood-s-oss

Change-Id: I797a2a9e9bbde8f9bf750b7429977747c31bd8a6
This commit is contained in:
Giovanni Ricca 2023-01-29 02:35:27 +05:30
parent 257a07db14
commit 17ca3415dc
No known key found for this signature in database
18 changed files with 4421 additions and 0 deletions

458
techpack/audio/dsp/apr_elliptic.c Executable file
View File

@ -0,0 +1,458 @@
/**
* Elliptic Labs
*/
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
#include <sound/asound.h>
#include <sound/soc.h>
#include <sound/control.h>
#include "../asoc/msm-pcm-routing-v2.h"
#include <dsp/q6audio-v2.h>
#include <dsp/q6common.h>
#include <dsp/apr_audio-v2.h>
#include <dsp/apr_elliptic.h>
#include <elliptic/elliptic_mixer_controls.h>
#include <elliptic/elliptic_data_io.h>
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
enum {
HALL_SLIDER_UP = 4,
HALL_SLIDER_DOWN = 5,
HALL_SLIDING = 6,
};
enum driver_sensor_type {
DRIVER_SENSOR_HALL = 35,
};
struct driver_sensor_event {
enum driver_sensor_type type;
union {
int32_t event;
int32_t reserved[2];
};
};
static int afe_set_parameter(int port,
int param_id,
int module_id,
struct afe_ultrasound_set_params_t *prot_config,
uint32_t length)
{
struct afe_port_cmd_set_param_v2 *set_param_v2 = NULL;
uint32_t set_param_v2_size = sizeof(struct afe_port_cmd_set_param_v2);
struct afe_port_cmd_set_param_v3 *set_param_v3 = NULL;
uint32_t set_param_v3_size = sizeof(struct afe_port_cmd_set_param_v3);
struct param_hdr_v3 param_hdr = {0};
u16 port_id = 0;
int index = 0;
u8 *packed_param_data = NULL;
int packed_data_size = sizeof(union param_hdrs) + length;
int ret = 0;
pr_debug("[ELUS]: inside %s\n", __func__);
port_id = q6audio_get_port_id(port);
ret = q6audio_validate_port(port_id);
if (ret < 0) {
pr_err("%s: Not a valid port id = 0x%x ret %d\n", __func__,
port_id, ret);
return -EINVAL;
}
index = q6audio_get_port_index(port);
param_hdr.module_id = module_id;
param_hdr.instance_id = INSTANCE_ID_0;
param_hdr.param_id = param_id;
param_hdr.param_size = length;
pr_debug("[ELUS]: param_size %d\n", length);
packed_param_data = kzalloc(packed_data_size, GFP_KERNEL);
if (packed_param_data == NULL)
return -ENOMEM;
ret = q6common_pack_pp_params(packed_param_data, &param_hdr, (u8 *)prot_config,
&packed_data_size);
if (ret) {
pr_err("%s: Failed to pack param header and data, error %d\n",
__func__, ret);
goto fail_cmd;
}
if (q6common_is_instance_id_supported()) {
set_param_v3_size += packed_data_size;
set_param_v3 = kzalloc(set_param_v3_size, GFP_KERNEL);
if (set_param_v3 == NULL) {
ret = -ENOMEM;
goto fail_cmd;
}
set_param_v3->apr_hdr.hdr_field =
APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
set_param_v3->apr_hdr.pkt_size = sizeof(struct afe_port_cmd_set_param_v3) +
packed_data_size;
set_param_v3->apr_hdr.src_port = 0;
set_param_v3->apr_hdr.dest_port = 0;
set_param_v3->apr_hdr.token = index;
set_param_v3->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V3;
set_param_v3->port_id = port_id;
set_param_v3->payload_size = packed_data_size;
memcpy(&set_param_v3->param_data, packed_param_data,
packed_data_size);
mutex_lock(elus_afe.ptr_afe_apr_lock);
atomic_set(elus_afe.ptr_state, 1);
atomic_set(elus_afe.ptr_status, 0);
ret = apr_send_pkt(*elus_afe.ptr_apr, (uint32_t *) set_param_v3);
} else {
set_param_v2_size += packed_data_size;
set_param_v2 = kzalloc(set_param_v2_size, GFP_KERNEL);
if (set_param_v2 == NULL) {
ret = -ENOMEM;
goto fail_cmd;
}
set_param_v2->apr_hdr.hdr_field =
APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
set_param_v2->apr_hdr.pkt_size = sizeof(struct afe_port_cmd_set_param_v2) +
packed_data_size;
set_param_v2->apr_hdr.src_port = 0;
set_param_v2->apr_hdr.dest_port = 0;
set_param_v2->apr_hdr.token = index;
set_param_v2->apr_hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
set_param_v2->port_id = port_id;
set_param_v2->payload_size = packed_data_size;
memcpy(&set_param_v2->param_data, packed_param_data,
packed_data_size);
mutex_lock(elus_afe.ptr_afe_apr_lock);
atomic_set(elus_afe.ptr_state, 1);
atomic_set(elus_afe.ptr_status, 0);
ret = apr_send_pkt(*elus_afe.ptr_apr, (uint32_t *) set_param_v2);
}
if (ret < 0) {
pr_err("%s: Setting param for port %d param[0x%x]failed\n",
__func__, port, param_id);
goto fail_cmd_lock;
}
ret = wait_event_timeout(elus_afe.ptr_wait[index],
(atomic_read(elus_afe.ptr_state) == 0),
msecs_to_jiffies(elus_afe.timeout_ms));
if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
ret = -EINVAL;
goto fail_cmd_lock;
}
if (atomic_read(elus_afe.ptr_status) != 0) {
pr_err("%s: set param cmd failed\n", __func__);
ret = -EINVAL;
goto fail_cmd_lock;
}
ret = 0;
fail_cmd_lock:
mutex_unlock(elus_afe.ptr_afe_apr_lock);
fail_cmd:
pr_debug("%s param_id %x status %d\n", __func__, param_id, ret);
kfree(set_param_v2);
kfree(set_param_v3);
kfree(packed_param_data);
return ret;
}
int32_t ultrasound_apr_set_parameter(int32_t port_id, uint32_t param_id,
u8 *user_params, int32_t length) {
int32_t ret = 0;
uint32_t module_id = 0;
if (port_id == ELLIPTIC_PORT_ID)
module_id = ELLIPTIC_ULTRASOUND_MODULE_TX;
else
module_id = ELLIPTIC_ULTRASOUND_MODULE_RX;
ret = afe_set_parameter(port_id,
param_id, module_id,
(struct afe_ultrasound_set_params_t *)user_params,
length);
return ret;
}
static int32_t process_version_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_VERSION_INFO_SIZE) {
pr_debug("[ELUS]: elliptic_version copied to local AP cache");
data_block =
elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_VERSION_INFO);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_VERSION_INFO_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_branch_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_BRANCH_INFO_SIZE) {
pr_debug("[ELUS]: elliptic_branch copied to local AP cache");
data_block =
elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_BRANCH_INFO);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_BRANCH_INFO_MAX_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_tag_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_TAG_INFO_SIZE) {
pr_debug("[ELUS]: elliptic_tag copied to local AP cache");
data_block =
elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_TAG_INFO);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_TAG_INFO_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_calibration_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_CALIBRATION_DATA_SIZE) {
pr_debug("[ELUS]: calibration_data copied to local AP cache");
data_block = elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_CALIBRATION_DATA);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_CALIBRATION_DATA_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
elliptic_set_calibration_data((u8 *)&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_calibration_v2_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_CALIBRATION_V2_DATA_SIZE) {
pr_debug("[ELUS]: calibration_data copied to local AP cache");
data_block = elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_CALIBRATION_V2_DATA);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_CALIBRATION_V2_DATA_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
elliptic_set_calibration_data((u8 *)&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_ml_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_ML_DATA_SIZE) {
pr_debug("[ELUS]: ml_data copied to local AP cache");
data_block = elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_ML_DATA);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_ML_DATA_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_diagnostics_msg(uint32_t *payload, uint32_t payload_size)
{
struct elliptic_shared_data_block *data_block = NULL;
size_t copy_size = 0;
int32_t ret = -1;
pr_err("[ELUS]: %s() size:%d\n", __func__, payload_size);
if (payload_size >= ELLIPTIC_DIAGNOSTICS_DATA_SIZE) {
pr_debug("[ELUS]: diagnostics_data copied to local AP cache");
data_block = elliptic_get_shared_obj(
ELLIPTIC_OBJ_ID_DIAGNOSTICS_DATA);
copy_size = min_t(size_t, data_block->size,
(size_t)ELLIPTIC_DIAGNOSTICS_DATA_SIZE);
memcpy((u8 *)data_block->buffer,
&payload[3], copy_size);
ret = (int32_t)copy_size;
}
return ret;
}
static int32_t process_sensorhub_msg(uint32_t *payload, uint32_t payload_size)
{
int32_t ret = 0;
pr_err("[ELUS]: %s, paramId:%u, size:%d\n",
__func__, payload[1], payload_size);
return ret;
}
int32_t elliptic_process_apr_payload(uint32_t *payload)
{
uint32_t payload_size = 0;
int32_t ret = -1;
if (payload[0] == ELLIPTIC_ULTRASOUND_MODULE_TX) {
/* payload format
* payload[0] = Module ID
* payload[1] = Param ID
* payload[2] = LSB - payload size
* MSB - reserved(TBD)
* payload[3] = US data payload starts from here
*/
payload_size = payload[2] & 0xFFFF;
switch (payload[1]) {
case ELLIPTIC_ULTRASOUND_PARAM_ID_ENGINE_VERSION:
ret = process_version_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_BUILD_BRANCH:
ret = process_branch_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_TAG:
ret = process_tag_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_CALIBRATION_DATA:
ret = process_calibration_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_CALIBRATION_V2_DATA:
ret = process_calibration_v2_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_ML_DATA:
ret = process_ml_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_DIAGNOSTICS_DATA:
ret = process_diagnostics_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_SENSORHUB:
ret = process_sensorhub_msg(payload, payload_size);
break;
case ELLIPTIC_ULTRASOUND_PARAM_ID_ENGINE_DATA:
ret = elliptic_data_push(
ELLIPTIC_ALL_DEVICES,
(const char *)&payload[3],
(size_t)payload_size,
ELLIPTIC_DATA_PUSH_FROM_KERNEL);
if (ret != 0) {
pr_err("[ELUS] : failed to push apr payload to elliptic device");
return ret;
}
ret = payload_size;
break;
default:
{
pr_err("[ELUS] : elliptic_process_apr_payload, Illegal paramId:%u", payload[1]);
}
break;
}
} else {
pr_debug("[ELUS]: Invalid Ultrasound Module ID %d\n",
payload[0]);
}
return ret;
}
int elliptic_set_hall_state(int state)
{
struct driver_sensor_event dse;
int ret = -1;
dse.type = DRIVER_SENSOR_HALL;
switch (state) {
case 0:
dse.event = HALL_SLIDER_UP;
break;
case 1:
dse.event = HALL_SLIDER_DOWN;
break;
case 2:
dse.event = HALL_SLIDING;
break;
default:
pr_err("%s Invalid HALL state:%d\n", __func__, state);
return ret;
}
ret = afe_set_parameter(ELLIPTIC_PORT_ID,
2, ELLIPTIC_ULTRASOUND_MODULE_TX,
(struct afe_ultrasound_set_params_t *)&dse,
sizeof(dse));
return ret;
}
EXPORT_SYMBOL(elliptic_set_hall_state);

View File

@ -0,0 +1,28 @@
ccflags-y := -I$(src) -Wall -Werror
IO_MODULE := msm
# EXTRA_CFLAGS += -DDEBUG
# Need to add include paths:
LINUXINCLUDE += \
-I$(srctree)techpack/audio/include/dsp
-I$(srctree)techpack/audio/include/uapi
-I$(srctree)techpack/aduio/include
obj-y += elliptic.o
obj-y += elliptic_mixer_controls.o
obj-y += io_modules/$(IO_MODULE)/elliptic_data_$(IO_MODULE)_io.o
obj-y += io_modules/userspace/elliptic_data_userspace_io.o
obj-y += io_modules/userspace/elliptic_data_userspace_ctrl.o
obj-y += elliptic_sysfs.o
# elliptic_driver-y := elliptic.o
# elliptic_driver-y += io_modules/$(IO_MODULE)/elliptic_data_io.o
# elliptic_driver-y += mixer_controls/$(MIXER_MODULE)/elliptic_mixer_controls.o
# elliptic_driver-y += elliptic_sysfs.o
# .PHONY: clean
# all:
# make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
# clean:
# rm *.o *.ko

View File

@ -0,0 +1,808 @@
/**
* Copyright Elliptic Labs
*
*/
/* #define DEBUG */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
/* includes the file structure, that is, file open read close */
#include <linux/fs.h>
/* include the character device, makes cdev avilable */
#include <linux/cdev.h>
#include <linux/semaphore.h>
/* includes copy_user vice versa */
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/pm_wakeup.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/kobject.h>
#include <elliptic/elliptic_sysfs.h>
#include <elliptic/elliptic_device.h>
#include <elliptic/elliptic_data_io.h>
#include <elliptic/elliptic_mixer_controls.h>
#include <dsp/apr_elliptic.h>
/* Alternative mechanism to load calibration data.
* Read calibration data during driver initialization
* and send message to the DSP
*
* #define ELLIPTIC_LOAD_CALIBRATION_DATA_FROM_FILESYSTEM 1
*/
#ifdef ELLIPTIC_LOAD_CALIBRATION_DATA_FROM_FILESYSTEM
#include <linux/syscalls.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#endif
static struct elliptic_device *elliptic_devices;
/* Global variable for the device class*/
struct class *elliptic_class;
typedef uint32_t el_fifo_size_t;
/* Major number provided by the kernel*/
static dev_t elliptic_major;
static struct wakeup_source *wake_source;
void elliptic_data_cancel(struct elliptic_data *elliptic_data)
{
atomic_set(&elliptic_data->abort_io, 1);
wake_up_interruptible(&elliptic_data->fifo_isr_not_empty);
}
void elliptic_data_reset_debug_counters(struct elliptic_data *elliptic_data)
{
elliptic_data->isr_fifo_discard = 0;
}
void elliptic_data_print_debug_counters(struct elliptic_data *elliptic_data)
{
if (elliptic_data->isr_fifo_discard > 0) {
EL_PRINT_E("isr fifo discarded %u frames",
elliptic_data->isr_fifo_discard);
}
if (elliptic_data->userspace_read_total !=
elliptic_data->isr_write_total) {
EL_PRINT_I("user space reads / isr writes : %u / %u",
elliptic_data->userspace_read_total,
elliptic_data->isr_write_total);
}
EL_PRINT_I("total isr fifo discarded frame count : %u",
elliptic_data->isr_fifo_discard_total);
}
void elliptic_data_update_debug_counters(struct elliptic_data
*elliptic_data)
{
elliptic_data->isr_fifo_discard_total +=
elliptic_data->isr_fifo_discard;
}
/* spin lock for isr must be held prior to calling */
static void elliptic_data_flush_isr_fifo(struct elliptic_data
*elliptic_data)
{
kfifo_reset(&elliptic_data->fifo_isr);
}
/* spin lock for isr must be held prior to calling */
static void elliptic_data_isr_fifo_pop(struct elliptic_data
*elliptic_data, size_t size)
{
unsigned int fifo_result;
static uint8_t temp_buffer[ELLIPTIC_MSG_BUF_SIZE];
if (size > ELLIPTIC_MSG_BUF_SIZE)
EL_PRINT_E("pop size %zu too large", size);
fifo_result = kfifo_out(&elliptic_data->fifo_isr,
temp_buffer, size);
if (size != fifo_result)
EL_PRINT_E("failed to pop element");
}
int elliptic_notify_gain_change_msg(int component_id, int gaindb)
{
int32_t msg[3] = {ESCPT_COMPONENT_GAIN_CHANGE, component_id, gaindb};
return elliptic_data_write(
ELLIPTIC_ULTRASOUND_SET_PARAMS,
(const char *)msg, sizeof(msg));
}
static int major;
/* inode refers to the actual file on disk */
static int device_open(struct inode *inode, struct file *filp)
{
unsigned int major;
unsigned int minor;
struct elliptic_device *dev;
struct elliptic_data *elliptic_data;
unsigned long flags;
major = imajor(inode);
minor = iminor(inode);
if (major != elliptic_major || minor < 0
|| minor >= ELLIPTIC_NUM_DEVICES) {
EL_PRINT_W("no device found with minor=%d and major=%d",
major, minor);
return -ENODEV; /* No such device */
}
dev = NULL;
dev = &elliptic_devices[minor];
filp->private_data = dev;
if (inode->i_cdev != &dev->cdev) {
EL_PRINT_W("dev pointer mismatch");
return -ENODEV; /* No such device */
}
if (down_interruptible(&dev->sem) != 0) {
EL_PRINT_E("the device has been opened, unable to open lock");
return -EINVAL;
}
elliptic_data = &dev->el_data;
spin_lock_irqsave(&elliptic_data->fifo_isr_spinlock, flags);
elliptic_data_flush_isr_fifo(elliptic_data);
spin_unlock_irqrestore(&elliptic_data->fifo_isr_spinlock, flags);
atomic_set(&elliptic_data->abort_io, 0);
elliptic_data_reset_debug_counters(elliptic_data);
EL_PRINT_I("Opened device elliptic%u", minor);
dev->opened = 1;
return 0;
}
int elliptic_data_initialize(struct elliptic_data
*elliptic_data, size_t queue_size,
unsigned int wakeup_timeout, int id)
{
int is_power_of_two;
is_power_of_two = (queue_size != 0) && !(queue_size & (queue_size - 1));
if (is_power_of_two != 1) {
EL_PRINT_E("non power of 2 fifo size");
return -EINVAL;
}
if (kfifo_alloc(&elliptic_data->fifo_isr,
queue_size, GFP_KERNEL) != 0) {
EL_PRINT_E("failed to allocate fifo isr");
return -EINVAL;
}
atomic_set(&elliptic_data->abort_io, 0);
spin_lock_init(&elliptic_data->fifo_isr_spinlock);
elliptic_data->wakeup_timeout = wakeup_timeout;
mutex_init(&elliptic_data->user_buffer_lock);
init_waitqueue_head(&elliptic_data->fifo_isr_not_empty);
return 0;
}
int elliptic_data_cleanup(struct elliptic_data *elliptic_data)
{
spin_unlock(&elliptic_data->fifo_isr_spinlock);
kfifo_free(&elliptic_data->fifo_isr);
return 0;
}
size_t elliptic_data_pop(struct elliptic_data
*elliptic_data, char __user *user_buffer, size_t buffer_size)
{
int result;
unsigned long num_copied;
unsigned int fifo_result;
unsigned long flags;
if (buffer_size < ELLIPTIC_MSG_BUF_SIZE) {
EL_PRINT_E("buffer_size : %lu smaller than %lu",
buffer_size, (size_t)ELLIPTIC_MSG_BUF_SIZE);
return 0;
}
result = wait_event_interruptible(elliptic_data->fifo_isr_not_empty,
(kfifo_is_empty(&elliptic_data->fifo_isr) == 0)
|| (atomic_read(&elliptic_data->abort_io) == 1));
if (atomic_read(&elliptic_data->abort_io) == 1) {
atomic_set(&elliptic_data->abort_io, 0);
EL_PRINT_D("pop cancelled");
return 0;
}
if (result == 0) {
spin_lock_irqsave(&elliptic_data->fifo_isr_spinlock, flags);
fifo_result = kfifo_out(&elliptic_data->fifo_isr,
elliptic_data->isr_swap_buffer, ELLIPTIC_MSG_BUF_SIZE);
spin_unlock_irqrestore(&elliptic_data->fifo_isr_spinlock,
flags);
if (fifo_result == 0) {
EL_PRINT_E("failed to copy: fifo isr -> swap buffer %u",
fifo_result);
return 0;
}
mutex_lock(&elliptic_data->user_buffer_lock);
num_copied = copy_to_user(user_buffer,
elliptic_data->isr_swap_buffer,
ELLIPTIC_MSG_BUF_SIZE);
mutex_unlock(&elliptic_data->user_buffer_lock);
if (num_copied != 0) {
EL_PRINT_E("failed copy to user");
return 0;
}
++elliptic_data->userspace_read_total;
} else {
if (-ERESTARTSYS == result)
EL_PRINT_I("wait interrupted");
else
EL_PRINT_E("wait error = %d", result);
return 0;
}
return (size_t)ELLIPTIC_MSG_BUF_SIZE;
}
/* push data to specific device or all devices */
int elliptic_data_push(int deviceid,
const char *buffer,
size_t buffer_size,
elliptic_data_push_t data_source)
{
size_t available_space;
size_t space_required;
size_t zeros_to_pad;
unsigned int copied_from_user;
int copy_from_user_result;
int err;
int i;
int i_max;
unsigned long flags;
struct elliptic_device *device;
struct elliptic_data *elliptic_data;
unsigned int fifo_result;
static uint8_t zero_pad_buffer[ELLIPTIC_MSG_BUF_SIZE];
err = 0;
fifo_result = 0;
copied_from_user = 0;
copy_from_user_result = 0;
if (buffer_size > ELLIPTIC_MSG_BUF_SIZE)
return -EINVAL;
zeros_to_pad = ELLIPTIC_MSG_BUF_SIZE - buffer_size;
i = 0;
i_max = ELLIPTIC_NUM_DEVICES;
if (deviceid != ELLIPTIC_ALL_DEVICES) {
/* Copy to specific device */
i = deviceid;
i_max = i + 1;
}
for (; i < i_max; ++i) {
device = &elliptic_devices[i];
elliptic_data = &device->el_data;
if ((!device->opened))
continue;
available_space = kfifo_avail(&elliptic_data->fifo_isr);
space_required = ELLIPTIC_MSG_BUF_SIZE;
spin_lock_irqsave(&elliptic_data->fifo_isr_spinlock, flags);
if (available_space < space_required) {
++elliptic_data->isr_fifo_discard;
elliptic_data_isr_fifo_pop(elliptic_data,
ELLIPTIC_MSG_BUF_SIZE);
}
if (data_source == ELLIPTIC_DATA_PUSH_FROM_KERNEL) {
fifo_result = kfifo_in(&elliptic_data->fifo_isr,
buffer, buffer_size);
if (fifo_result == 0) {
spin_unlock_irqrestore(
&elliptic_data->fifo_isr_spinlock,
flags);
continue;
}
} else if (data_source == ELLIPTIC_DATA_PUSH_FROM_USERSPACE) {
copy_from_user_result = kfifo_from_user(
&elliptic_data->fifo_isr, buffer,
buffer_size, &copied_from_user);
if (-EFAULT == copy_from_user_result) {
spin_unlock_irqrestore(
&elliptic_data->fifo_isr_spinlock,
flags);
continue;
}
}
if (zeros_to_pad > 0) {
fifo_result = kfifo_in(
&elliptic_data->fifo_isr, zero_pad_buffer,
zeros_to_pad);
if (fifo_result == 0) {
elliptic_data_isr_fifo_pop(elliptic_data,
buffer_size);
spin_unlock_irqrestore(
&elliptic_data->fifo_isr_spinlock,
flags);
++elliptic_data->isr_fifo_discard;
continue;
}
}
++elliptic_data->isr_write_total;
spin_unlock_irqrestore(
&elliptic_data->fifo_isr_spinlock, flags);
wake_up_interruptible(&elliptic_data->fifo_isr_not_empty);
__pm_wakeup_event(wake_source, elliptic_data->wakeup_timeout);
}
return err;
}
int elliptic_open_port(int portid)
{
return elliptic_io_open_port(portid);
}
int elliptic_close_port(int portid)
{
return elliptic_io_close_port(portid);
}
int32_t elliptic_data_write(uint32_t message_id,
const char *data, size_t data_size)
{
int32_t err_dsp;
/* int32_t err_us; */
err_dsp = 0;
err_dsp = elliptic_data_io_write(message_id, data, data_size);
if (err_dsp)
EL_PRINT_E("Failed write to DSP");
return err_dsp;
/*
* err_us = 0;
* err_us = elliptic_userspace_ctrl_write(message_id, data, data_size);
* if(err_us){
* EL_PRINT_E("Failed write to user space");
*}
*
*return (err_dsp | err_us);
*/
}
/**
*
* @return Number of bytes read.
*/
static ssize_t device_read(struct file *fp, char __user *buff,
size_t length, loff_t *ppos)
{
ssize_t bytes_read = 0;
struct elliptic_device *elliptic_device;
struct elliptic_data *elliptic_data;
elliptic_device = (struct elliptic_device *)fp->private_data;
elliptic_data = (struct elliptic_data *)&elliptic_device->el_data;
bytes_read = elliptic_data_pop(elliptic_data, buff, length);
return bytes_read;
}
/**
*
* @return number of bytes actually written
*/
static ssize_t device_write(struct file *fp, const char *buff,
size_t length, loff_t *ppos)
{
ssize_t ret_val;
ret_val = 0;
if ((buff != NULL) && (length != 0))
ret_val = elliptic_data_io_write(ELLIPTIC_ULTRASOUND_SET_PARAMS,
buff, length);
return ret_val >= 0 ? (ssize_t)length : 0;
}
static long device_ioctl(struct file *fp, unsigned int number,
unsigned long param)
{
struct elliptic_device *device;
struct elliptic_data *elliptic_data;
int err;
unsigned int mirror_tag, mirror_payload_size;
unsigned char *data_ptr;
device = (struct elliptic_device *)(fp->private_data);
elliptic_data = &device->el_data;
switch (number) {
case IOCTL_ELLIPTIC_DATA_IO_CANCEL:
EL_PRINT_D("IOCTL_ELLIPTIC_CANCEL_READ %ld",
param);
elliptic_data_cancel(elliptic_data);
break;
case IOCTL_ELLIPTIC_DATA_IO_MIRROR:
data_ptr = (unsigned char *)param;
mirror_tag = *(unsigned int *)data_ptr;
mirror_payload_size = *((unsigned int *)data_ptr + 1);
if ((mirror_tag == MIRROR_TAG) &&
(mirror_payload_size != 0) &&
(mirror_payload_size <=
(ELLIPTIC_SET_PARAMS_SIZE * 4))) {
err = elliptic_data_io_write(
ELLIPTIC_ULTRASOUND_SET_PARAMS,
(data_ptr + 8), mirror_payload_size);
if (err != 0) {
EL_PRINT_E("elliptic_data_io_write failed");
return err;
}
} else {
EL_PRINT_E("TAG or Length is not valid");
}
break;
default:
EL_PRINT_W("UNKNOWN IOCTL number=%d", number);
break;
}
return 0;
}
static unsigned int device_poll(struct file *file,
struct poll_table_struct *poll_table)
{
unsigned int mask;
struct elliptic_device *device;
struct elliptic_data *elliptic_data;
mask = 0;
device = (struct elliptic_device *)file->private_data;
elliptic_data = (struct elliptic_data *)&device->el_data;
poll_wait(file, &elliptic_data->fifo_isr_not_empty, poll_table);
if (!kfifo_is_empty(&elliptic_data->fifo_isr))
mask = POLLIN | POLLRDNORM;
return mask;
}
static int device_close(struct inode *inode, struct file *filp)
{
struct elliptic_device *device;
struct elliptic_data *elliptic_data;
unsigned int minor;
device = filp->private_data;
elliptic_data = &device->el_data;
minor = iminor(inode);
if (device == NULL) {
EL_PRINT_E("device not found");
return -ENODEV;
}
device->opened = 0;
elliptic_data_update_debug_counters(elliptic_data);
elliptic_data_print_debug_counters(elliptic_data);
elliptic_data_cancel(elliptic_data);
up(&device->sem);
EL_PRINT_I("Closed device elliptic%u", minor);
return 0;
}
/* defines the file operations provided by the driver */
static const struct file_operations elliptic_fops = {
.owner = THIS_MODULE, /* prevents unloading when operations are in use*/
.open = device_open, /*to open the device*/
.write = device_write, /*to write to the device*/
.read = device_read, /*to read the device*/
.poll = device_poll,
.unlocked_ioctl = device_ioctl, /* IOCTL calls */
.release = device_close, /*to close the device*/
};
static int elliptic_device_initialize(struct elliptic_device
*elliptic_device, int minor, struct class *class)
{
int err;
dev_t device_number;
struct device *device;
BUG_ON(elliptic_device == NULL || class == NULL);
err = 0;
device = NULL;
device_number = MKDEV(elliptic_major, minor);
/* Memory is to be allocated when the device is opened the first time */
sema_init(&elliptic_device->sem, 1);
cdev_init(&elliptic_device->cdev, &elliptic_fops);
elliptic_device->cdev.owner = THIS_MODULE;
err = cdev_add(&elliptic_device->cdev, device_number, 1);
if (err) {
EL_PRINT_E("error %d while trying to add %s%d",
err, ELLIPTIC_DEVICENAME, minor);
return err;
}
device = device_create(class, NULL, device_number,
NULL, ELLIPTIC_DEVICENAME "%d", minor);
if (IS_ERR(device)) {
err = PTR_ERR(device);
EL_PRINT_E("error %d while trying to create %s%d",
err, ELLIPTIC_DEVICENAME, minor);
cdev_del(&elliptic_device->cdev);
return err;
}
if (err) {
EL_PRINT_E("failed device initialize");
return err;
}
return 0;
}
static void elliptic_device_cleanup(struct elliptic_device *dev, int minor,
struct class *class)
{
BUG_ON(dev == NULL || class == NULL);
device_destroy(class, MKDEV(elliptic_major, minor));
cdev_del(&dev->cdev);
up(&dev->sem);
}
static void elliptic_driver_cleanup(int devices_to_destroy)
{
int i;
if (elliptic_devices) {
elliptic_data_io_cleanup();
for (i = 0; i < devices_to_destroy; ++i) {
elliptic_data_cleanup(&elliptic_devices[i].el_data);
elliptic_device_cleanup(
&elliptic_devices[i], i, elliptic_class);
}
kfree(elliptic_devices);
}
if (elliptic_class)
class_destroy(elliptic_class);
unregister_chrdev_region(
MKDEV(elliptic_major, 0), ELLIPTIC_NUM_DEVICES);
}
#ifdef ELLIPTIC_LOAD_CALIBRATION_DATA_FROM_FILESYSTEM
#define ELLIPTIC_CALIBRATION_MAX_DATA_SIZE (ELLIPTIC_CALIBRATION_V2_DATA_SIZE + ELLIPTIC_CALIBRATION_DATA_SIZE)
static unsigned char calibration_data[ELLIPTIC_CALIBRATION_MAX_DATA_SIZE];
static char *calibration_filename = "/persist/audio/elliptic_calibration";
/* function to load the calibration from a file (if possible) */
static size_t load_calibration_data(char *filename)
{
size_t ret = 0;
int fd;
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
fd = sys_open(filename, O_RDONLY, 0);
if (fd >= 0) {
size_t bytes_read = sys_read(fd, calibration_data, ELLIPTIC_CALIBRATION_MAX_DATA_SIZE);
if (bytes_read == ELLIPTIC_CALIBRATION_DATA_SIZE ||
bytes_read == ELLIPTIC_CALIBRATION_V2_DATA_SIZE) {
ret = bytes_read;
}
sys_close(fd);
}
set_fs(old_fs);
return ret;
}
static int32_t elliptic_send_calibration_to_engine(size_t calib_data_size)
{
elliptic_set_calibration_data(calibration_data, calib_data_size);
return elliptic_data_write(
ELLIPTIC_ULTRASOUND_SET_PARAMS,
(const char *)calibration_data, calib_data_size);
}
#endif
int __init elliptic_driver_init(void)
{
int err;
int i;
int devices_to_destroy;
dev_t device_number = MKDEV(major, 0);
if (major) {
err = register_chrdev_region(device_number, ELLIPTIC_NUM_DEVICES,
ELLIPTIC_DEVICENAME);
} else {
err = alloc_chrdev_region(&device_number, 0, ELLIPTIC_NUM_DEVICES,
ELLIPTIC_DEVICENAME);
major = MAJOR(device_number);
}
devices_to_destroy = 0;
if (err < 0) {
EL_PRINT_E("Failed to allocate cdev region");
return err;
}
elliptic_major = MAJOR(device_number);
elliptic_class = class_create(THIS_MODULE, "chardev");
if (elliptic_class == NULL) {
EL_PRINT_E("Class creation failed");
goto fail;
}
elliptic_devices = (struct elliptic_device *)
kzalloc(sizeof(struct elliptic_device) * ELLIPTIC_NUM_DEVICES,
GFP_KERNEL);
if (elliptic_devices == NULL) {
err = -ENOMEM;
EL_PRINT_E("elliptic_devices creation failed");
goto fail;
}
for (i = 0; i < ELLIPTIC_NUM_DEVICES; ++i) {
if (elliptic_device_initialize(&elliptic_devices[i], i,
elliptic_class)) {
devices_to_destroy = i;
EL_PRINT_E("elliptic device initialize failed %d", i);
goto fail;
}
if (elliptic_data_initialize(&elliptic_devices[i].el_data,
ELLIPTIC_DATA_FIFO_SIZE, ELLIPTIC_WAKEUP_TIMEOUT, i)) {
EL_PRINT_E("elliptic data initialize failed failed %d", i);
goto fail;
}
}
if (elliptic_initialize_sysfs())
goto fail;
if (elliptic_data_io_initialize())
goto fail;
if (elliptic_userspace_io_driver_init())
goto fail;
if (elliptic_userspace_ctrl_driver_init())
goto fail;
wake_source = wakeup_source_register(NULL, "elliptic_wake_source");
if (wake_source == NULL) {
EL_PRINT_E("wake_source register failed");
goto fail;
}
#ifdef ELLIPTIC_LOAD_CALIBRATION_DATA_FROM_FILESYSTEM
/* Code to send calibration to engine */
{
size_t calib_data_size = load_calibration_data(calibration_filename);
if (calib_data_size > 0)
elliptic_send_calibration_to_engine(calib_data_size);
}
#endif
EL_PRINT_I("elliptic_class creation name %s", elliptic_class->name);
return 0;
fail:
elliptic_driver_cleanup(devices_to_destroy);
return err;
}
void elliptic_driver_exit(void)
{
wakeup_source_unregister(wake_source);
elliptic_cleanup_sysfs();
elliptic_driver_cleanup(ELLIPTIC_NUM_DEVICES);
elliptic_userspace_io_driver_exit();
elliptic_userspace_ctrl_driver_exit();
}
MODULE_AUTHOR("Elliptic Labs");
MODULE_DESCRIPTION("Providing Interface to UPS data");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,654 @@
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <elliptic/elliptic_device.h>
#include <elliptic/elliptic_sysfs.h>
#include "elliptic_version.h"
#include <elliptic/elliptic_mixer_controls.h>
#define ELLIPTIC_DIAGNOSTICS_DATA_SECTION_COUNT 16
#define ELLIPTIC_CALIBRATION_MAX_DISPLAY_COUNT 96
#define ELLIPTIC_ML_DISPLAY_COUNT 16
#define elliptic_attr(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
#define elliptic_attr_ro(_name) \
static struct kobj_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = S_IRUGO, \
}, \
.show = _name##_show, \
}
static int kobject_create_and_add_failed;
static int sysfs_create_group_failed;
extern struct elliptic_system_configuration_parameters_cache
elliptic_system_configuration_cache;
static ssize_t calibration_store(struct kobject *dev,
struct kobj_attribute *attr, const char *buf, size_t count) {
ssize_t result;
struct elliptic_shared_data_block *calibration_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_CALIBRATION_DATA);
if (calibration_obj == NULL) {
EL_PRINT_E("calibration_obj is NULL");
return -EINVAL;
}
if (count > calibration_obj->size) {
EL_PRINT_E("write length %zu larger than buffer", count);
return 0;
}
memcpy(calibration_obj->buffer, buf, count);
result = (ssize_t)count;
return result;
}
static ssize_t calibration_v2_store(struct kobject *dev,
struct kobj_attribute *attr, const char *buf, size_t count) {
ssize_t result;
struct elliptic_shared_data_block *calibration_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_CALIBRATION_V2_DATA);
if (calibration_obj == NULL) {
EL_PRINT_E("calibration_obj is NULL");
return -EINVAL;
}
if (count > calibration_obj->size) {
EL_PRINT_E("write length %zu larger than buffer", count);
return 0;
}
memcpy(calibration_obj->buffer, buf, count);
result = (ssize_t)count;
return result;
}
static ssize_t diagnostics_store(struct kobject *dev,
struct kobj_attribute *attr, const char *buf, size_t count) {
ssize_t result;
struct elliptic_shared_data_block *diagnostics_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_DIAGNOSTICS_DATA);
if (diagnostics_obj == NULL) {
EL_PRINT_E("diagnostics_obj is NULL");
return -EINVAL;
}
if (count > diagnostics_obj->size) {
EL_PRINT_E("write length %zu larger than buffer", count);
return 0;
}
memcpy(diagnostics_obj->buffer, buf, count);
result = (ssize_t)count;
return result;
}
static ssize_t ml_store(struct kobject *dev,
struct kobj_attribute *attr, const char *buf, size_t count) {
ssize_t result;
struct elliptic_shared_data_block *ml_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_ML_DATA);
if (ml_obj == NULL) {
EL_PRINT_E("ml_obj is NULL");
return -EINVAL;
}
if (count > ml_obj->size) {
EL_PRINT_E("write length %zu larger than buffer", count);
return 0;
}
memcpy(ml_obj->buffer, buf, count);
result = (ssize_t)count;
return result;
}
static ssize_t calibration_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
ssize_t result;
int length = 0;
int i;
uint8_t *caldata;
struct elliptic_shared_data_block *calibration_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_CALIBRATION_DATA);
if (kobject_create_and_add_failed)
EL_PRINT_E("kobject_create_and_add_failed");
if (sysfs_create_group_failed)
EL_PRINT_E("sysfs_create_group_failed");
if (calibration_obj == NULL) {
EL_PRINT_E("calibration_obj is NULL");
return -EINVAL;
}
if (calibration_obj->size > PAGE_SIZE) {
EL_PRINT_E("calibration_obj->size > PAGE_SIZE");
return -EINVAL;
}
caldata = (uint8_t *)calibration_obj->buffer;
length = 0;
if (pretty) {
if (caldata[0] == 0xDE &&
caldata[1] == 0xAD) {
length += snprintf(buf + length, PAGE_SIZE - length,
"Calibration Data: not loaded");
} else {
length += snprintf(buf + length, PAGE_SIZE - length,
"Calibration Data: ");
for (i = 0; i < calibration_obj->size; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%02x ", caldata[i]);
}
} else {
for (i = 0; i < calibration_obj->size; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%02x ", caldata[i]);
}
length += snprintf(buf + length, PAGE_SIZE - length, "\n\n");
result = (ssize_t)length;
return result;
}
static ssize_t calibration_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return calibration_show_core(dev, attr, buf, 0);
}
static ssize_t calibration_v2_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
ssize_t result;
int length = 0;
int i;
uint8_t *caldata;
struct elliptic_shared_data_block *calibration_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_CALIBRATION_V2_DATA);
if (kobject_create_and_add_failed)
EL_PRINT_E("kobject_create_and_add_failed");
if (sysfs_create_group_failed)
EL_PRINT_E("sysfs_create_group_failed");
if (calibration_obj == NULL) {
EL_PRINT_E("calibration_obj is NULL");
return -EINVAL;
}
if (calibration_obj->size > PAGE_SIZE) {
EL_PRINT_E("calibration_obj->size > PAGE_SIZE");
return -EINVAL;
}
caldata = (uint8_t *)calibration_obj->buffer;
length = 0;
if (pretty) {
if (caldata[0] == 0xDE &&
caldata[1] == 0xAD) {
length += snprintf(buf + length, PAGE_SIZE - length,
"Calibration Ext Data: not loaded");
} else {
int j = (ELLIPTIC_CALIBRATION_V2_DATA_SIZE>>2) - 1;
length += snprintf(buf + length, PAGE_SIZE - length,
"Calibration Ext Data: ");
for (i = 0; i < ELLIPTIC_CALIBRATION_MAX_DISPLAY_COUNT; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%02x ", caldata[i]);
length += snprintf(buf + length, PAGE_SIZE - length,
"\nTruncated at %d",
ELLIPTIC_CALIBRATION_MAX_DISPLAY_COUNT);
length += snprintf(buf + length, PAGE_SIZE - length,
"\nmisc: %u %u %u %u %u %u %u %u\n",
caldata[j-7], caldata[j-6], caldata[j-5],
caldata[j-4], caldata[j-3], caldata[j-2],
caldata[j-1], caldata[j]);
}
} else {
for (i = 0; i < calibration_obj->size; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%02x ", caldata[i]);
}
length += snprintf(buf + length, PAGE_SIZE - length, "\n\n");
result = (ssize_t)length;
return result;
}
static ssize_t calibration_v2_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return calibration_v2_show_core(dev, attr, buf, 0);
}
static ssize_t diagnostics_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
ssize_t result;
int length = 0;
uint32_t *data32;
int i;
struct elliptic_shared_data_block *diagnostics_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_DIAGNOSTICS_DATA);
if (kobject_create_and_add_failed)
EL_PRINT_E("kobject_create_and_add_failed");
if (sysfs_create_group_failed)
EL_PRINT_E("sysfs_create_group_failed");
if (diagnostics_obj == NULL) {
EL_PRINT_E("diagnostics_obj is NULL");
return -EINVAL;
}
if (diagnostics_obj->size > PAGE_SIZE) {
EL_PRINT_E("diagnostics_obj->size > PAGE_SIZE");
return -EINVAL;
}
length = 0;
data32 = (uint32_t *)diagnostics_obj->buffer;
if (pretty) {
length += snprintf(buf + length, PAGE_SIZE - length,
"Diagnostics:\n counters:\n");
for (i = 0; i < ELLIPTIC_DIAGNOSTICS_DATA_SECTION_COUNT; i++)
length += snprintf(buf + length, PAGE_SIZE - length, " %u %u %u %u\n",
data32[4*i], data32[4*i+1], data32[4*i+2], data32[4*i+3]);
} else {
for (i = 0; i < (diagnostics_obj->size >> 4); ++i)
length += snprintf(buf + length, PAGE_SIZE - length, " %u %u %u %u\n",
data32[4*i], data32[4*i+1], data32[4*i+2], data32[4*i+3]);
}
length += snprintf(buf + length, PAGE_SIZE - length, "\n\n");
result = (ssize_t)length;
return result;
}
static ssize_t diagnostics_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return diagnostics_show_core(dev, attr, buf, 0);
}
static ssize_t ml_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
ssize_t result;
int length = 0;
int i;
uint32_t *mldata;
struct elliptic_shared_data_block *ml_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_ML_DATA);
if (kobject_create_and_add_failed)
EL_PRINT_E("kobject_create_and_add_failed");
if (sysfs_create_group_failed)
EL_PRINT_E("sysfs_create_group_failed");
if (ml_obj == NULL) {
EL_PRINT_E("ml_obj is NULL");
return -EINVAL;
}
if (ml_obj->size > PAGE_SIZE) {
EL_PRINT_E("ml_obj->size > PAGE_SIZE");
return -EINVAL;
}
mldata = (uint32_t *)ml_obj->buffer;
length = 0;
if (pretty) {
if (mldata[0] == 0x0 &&
mldata[1] == 0x0) {
length += snprintf(buf + length, PAGE_SIZE - length,
"ML Data: not loaded");
} else {
length += snprintf(buf + length, PAGE_SIZE - length,
"ML Data: ");
for (i = 0; i < ELLIPTIC_ML_DISPLAY_COUNT; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%08x ", mldata[i]);
length += snprintf(buf + length, PAGE_SIZE - length,
"\nTruncated at %d",
ELLIPTIC_ML_DISPLAY_COUNT);
}
} else {
int values = ml_obj->size >> 2;
for (i = 0; i < values; ++i)
length += snprintf(buf + length, PAGE_SIZE - length,
"0x%08x ", mldata[i]);
}
length += snprintf(buf + length, PAGE_SIZE - length, "\n\n");
result = (ssize_t)length;
return result;
}
static ssize_t ml_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return ml_show_core(dev, attr, buf, 0);
}
static ssize_t version_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
ssize_t result;
struct elliptic_engine_version_info *version_info;
int length;
struct elliptic_shared_data_block *version_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_VERSION_INFO);
if (kobject_create_and_add_failed)
EL_PRINT_E("kobject_create_and_add_failed");
if (sysfs_create_group_failed)
EL_PRINT_E("sysfs_create_group_failed");
if (version_obj == NULL) {
EL_PRINT_E("version_obj is NULL");
return -EINVAL;
}
if (version_obj->size > PAGE_SIZE) {
EL_PRINT_E("version_obj->size > PAGE_SIZE");
return -EINVAL;
}
version_info = (struct elliptic_engine_version_info *)
version_obj->buffer;
if (pretty) {
if (version_info->major == 0xDE &&
version_info->minor == 0xAD) {
length = snprintf(buf, PAGE_SIZE, "Version: unknown\n");
} else {
length = snprintf(buf, PAGE_SIZE, "Version: %d.%d.%d.%d\n",
version_info->major, version_info->minor,
version_info->build, version_info->revision);
}
} else {
length = snprintf(buf, PAGE_SIZE, "%d.%d.%d.%d\n",
version_info->major, version_info->minor,
version_info->build, version_info->revision);
}
result = (ssize_t)length;
return result;
}
static ssize_t version_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return version_show_core(dev, attr, buf, 0);
}
static ssize_t branch_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
int length = 0;
struct elliptic_shared_data_block *branch_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_BRANCH_INFO);
if (branch_obj == NULL) {
EL_PRINT_E("branch_obj not found");
return 0;
}
if (branch_obj->size > PAGE_SIZE) {
EL_PRINT_E("branch_obj->size > PAGE_SIZE");
return -EINVAL;
}
if (pretty) {
length = snprintf(buf, PAGE_SIZE - 1, "Branch: %s\n",
(const char *)(branch_obj->buffer));
} else {
length = snprintf(buf, PAGE_SIZE - 1, "%s\n",
(const char *)(branch_obj->buffer));
}
return (ssize_t)length;
}
static ssize_t branch_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return branch_show_core(dev, attr, buf, 0);
}
static ssize_t tag_show_core(struct kobject *dev,
struct kobj_attribute *attr, char *buf, int pretty)
{
int length = 0;
struct elliptic_shared_data_block *tag_obj =
elliptic_get_shared_obj(ELLIPTIC_OBJ_ID_TAG_INFO);
if (tag_obj == NULL) {
EL_PRINT_E("tag_obj not found");
return 0;
}
if (tag_obj->size > PAGE_SIZE) {
EL_PRINT_E("tag_obj->size > PAGE_SIZE");
return -EINVAL;
}
if (pretty) {
length = snprintf(buf, PAGE_SIZE - 1, "Tag: %s\n",
(const char *)(tag_obj->buffer));
} else {
length = snprintf(buf, PAGE_SIZE - 1, "%s\n",
(const char *)(tag_obj->buffer));
}
return (ssize_t)length;
}
static ssize_t tag_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
return tag_show_core(dev, attr, buf, 0);
}
static ssize_t cache_show(char *buf, int pretty)
{
struct elliptic_system_configuration_parameters_cache *cache =
&elliptic_system_configuration_cache;
int length = 0;
length = snprintf(buf, PAGE_SIZE - 1, "Cache:\n");
length += snprintf(buf + length, PAGE_SIZE - 1, " mi:%d\n",
cache->microphone_index);
length += snprintf(buf + length, PAGE_SIZE - 1, " om:%d\n",
cache->operation_mode);
length += snprintf(buf + length, PAGE_SIZE - 1, " omf:%d\n",
cache->operation_mode_flags);
length += snprintf(buf + length, PAGE_SIZE - 1, " cs:%d\n",
cache->calibration_state);
length += snprintf(buf + length, PAGE_SIZE - 1, " cp:%d\n",
cache->calibration_profile);
length += snprintf(buf + length, PAGE_SIZE - 1, " ug:%d\n",
cache->ultrasound_gain);
length += snprintf(buf + length, PAGE_SIZE - 1, " ll:%d\n",
cache->log_level);
length += snprintf(buf + length, PAGE_SIZE - 1, " es:%d\n",
cache->engine_suspend);
return (ssize_t)length;
}
static ssize_t opmode_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
int length = 0;
ssize_t result;
struct elliptic_system_configuration_parameters_cache *cache =
&elliptic_system_configuration_cache;
length += snprintf(buf + length, PAGE_SIZE - 1, "%d\n",
cache->operation_mode);
result = (ssize_t)length;
return result;
}
static ssize_t opmode_flags_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
int length = 0;
ssize_t result;
struct elliptic_system_configuration_parameters_cache *cache =
&elliptic_system_configuration_cache;
length += snprintf(buf + length, PAGE_SIZE - 1, "%d\n",
cache->operation_mode_flags);
result = (ssize_t)length;
return result;
}
static ssize_t driver_version_show(char *buf)
{
int length = 0;
length = snprintf(buf, PAGE_SIZE, "Driver version: %s-%s (%s)\n",
build_name, build_number, build_source_version);
return (ssize_t)length;
}
static ssize_t state_show(struct kobject *dev,
struct kobj_attribute *attr, char *buf)
{
int length = 0;
length += driver_version_show(buf + length);
length += version_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += branch_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += tag_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += calibration_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += calibration_v2_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += diagnostics_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += ml_show_core(dev, attr, buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
length += cache_show(buf + length, 1);
if (length > PAGE_SIZE)
return (ssize_t)0;
return (ssize_t)length;
}
elliptic_attr(calibration);
elliptic_attr(calibration_v2);
elliptic_attr(diagnostics);
elliptic_attr(ml);
elliptic_attr_ro(version);
elliptic_attr_ro(branch);
elliptic_attr_ro(state);
elliptic_attr_ro(tag);
elliptic_attr_ro(opmode);
elliptic_attr_ro(opmode_flags);
static struct attribute *elliptic_attrs[] = {
&calibration_attr.attr,
&version_attr.attr,
&branch_attr.attr,
&calibration_v2_attr.attr,
&diagnostics_attr.attr,
&state_attr.attr,
&tag_attr.attr,
&ml_attr.attr,
&opmode_attr.attr,
&opmode_flags_attr.attr,
NULL,
};
static struct attribute_group elliptic_attr_group = {
.name = ELLIPTIC_SYSFS_ENGINE_FOLDER,
.attrs = elliptic_attrs,
};
static struct kobject *elliptic_sysfs_kobj;
int elliptic_initialize_sysfs(void)
{
int err;
elliptic_sysfs_kobj = kobject_create_and_add(ELLIPTIC_SYSFS_ROOT_FOLDER,
kernel_kobj->parent);
if (!elliptic_sysfs_kobj) {
kobject_create_and_add_failed = 1;
EL_PRINT_E("failed to create kobj");
return -ENOMEM;
}
err = sysfs_create_group(elliptic_sysfs_kobj, &elliptic_attr_group);
if (err) {
sysfs_create_group_failed = 1;
EL_PRINT_E("failed to create sysfs group");
kobject_put(elliptic_sysfs_kobj);
return -ENOMEM;
}
return 0;
}
void elliptic_cleanup_sysfs(void)
{
kobject_put(elliptic_sysfs_kobj);
}

View File

@ -0,0 +1,8 @@
#ifndef ELLIPTIC_VERSION_H
#define ELLIPTIC_VERSION_H
#define build_name "Elliptic.LinuxKernelDriver.VendorDLKM.master"
#define build_number "20181016.1"
#define build_source_version "e5e26691554bba9b1438ec3d7e0cc55dfb296bb8"
#endif //ELLIPTIC_VERSION_H

View File

@ -0,0 +1,2 @@
# TODO: add config parameter for which io module to build
#obj-y += userspace/

View File

@ -0,0 +1,108 @@
/**
* Copyright Elliptic Labs
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
/* includes the file structure, that is, file open read close */
#include <linux/fs.h>
/* include the character device, makes cdev avilable */
#include <linux/cdev.h>
#include <linux/semaphore.h>
/* includes copy_user vice versa */
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <elliptic/elliptic_data_io.h>
#include <elliptic/elliptic_device.h>
#include <dsp/apr_elliptic.h>
#include <dsp/q6afe-v2.h>
#define IO_PING_PONG_BUFFER_SIZE 512
#define AFE_MSM_RX_PSEUDOPORT_ID 0x8001
#define AFE_MSM_TX_PSEUDOPORT_ID 0x8002
struct elliptic_msm_io_device {
};
/* static struct elliptic_msm_io_device io_device;*/
int elliptic_data_io_initialize(void)
{
return 0;
}
int elliptic_data_io_cleanup(void)
{
return 0;
}
int elliptic_io_open_port(int portid)
{
if (portid == ULTRASOUND_RX_PORT_ID)
return afe_start_pseudo_port(AFE_MSM_RX_PSEUDOPORT_ID);
else
return afe_start_pseudo_port(AFE_MSM_TX_PSEUDOPORT_ID);
}
int elliptic_io_close_port(int portid)
{
if (portid == ULTRASOUND_RX_PORT_ID)
return afe_stop_pseudo_port(AFE_MSM_RX_PSEUDOPORT_ID);
else
return afe_stop_pseudo_port(AFE_MSM_TX_PSEUDOPORT_ID);
}
int32_t elliptic_data_io_write(uint32_t message_id, const char *data,
size_t data_size)
{
int32_t result = 0;
/* msm_pcm_routing_acquire_lock(); */
result = ultrasound_apr_set_parameter(ELLIPTIC_PORT_ID,
message_id, (u8 *)data,
(int32_t)data_size);
/* msm_pcm_routing_release_lock();*/
return result;
}
int32_t elliptic_data_io_transact(uint32_t message_id, const char *data,
size_t data_size, char *output_data, size_t output_data_size)
{
pr_err("%s : unimplemented\n", __func__);
return -EINVAL;
}

View File

@ -0,0 +1,2 @@
# TODO: add config parameter for which io module to build
#obj-y += elliptic_data_io.o

View File

@ -0,0 +1,271 @@
/**
* Copyright Elliptic Labs
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
/* includes the file structure, that is, file open read close */
#include <linux/fs.h>
/* include the character device, makes cdev avilable */
#include <linux/cdev.h>
#include <linux/semaphore.h>
/* includes copy_user vice versa */
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <elliptic/elliptic_data_io.h>
#include <elliptic/elliptic_device.h>
static dev_t elliptic_userspace_ctrl_major;
#define USERSPACE_CTRL_IO_DEVICE_NAME "elliptic_us_ctrl_io"
struct elliptic_userspace_ctrl_device {
struct cdev cdev;
struct semaphore sem;
int ping_pong_idx;
size_t ping_pong_buffer_size[2];
uint8_t ping_pong_buffer[2][ELLIPTIC_MSG_BUF_SIZE];
wait_queue_head_t data_available;
struct mutex data_lock;
atomic_t data_state;
};
static struct elliptic_userspace_ctrl_device ctrl_device;
static uint8_t *get_ping_buffer(struct elliptic_userspace_ctrl_device *dev,
/*out parameter*/ size_t *data_size)
{
if (data_size != NULL)
*data_size = dev->ping_pong_buffer_size[dev->ping_pong_idx];
return dev->ping_pong_buffer[dev->ping_pong_idx];
}
static uint8_t *get_pong_buffer(struct elliptic_userspace_ctrl_device *dev,
/*out parameter*/ size_t *data_size)
{
if (data_size != NULL)
*data_size = dev->ping_pong_buffer_size[1 - dev->ping_pong_idx];
return dev->ping_pong_buffer[1 - dev->ping_pong_idx];
}
static void set_pong_buffer_size(struct elliptic_userspace_ctrl_device *dev,
size_t data_size)
{
dev->ping_pong_buffer_size[1 - dev->ping_pong_idx] = data_size;
}
static void swap_ping_pong(struct elliptic_userspace_ctrl_device *dev)
{
dev->ping_pong_idx = 1 - dev->ping_pong_idx;
}
static int device_open(struct inode *inode, struct file *filp)
{
if (inode->i_cdev != &ctrl_device.cdev) {
pr_warn("elliptic : dev pointer mismatch\n");
return -ENODEV; /* No such device */
}
if (down_interruptible(&ctrl_device.sem) != 0)
return -EEXIST;
EL_PRINT_I("Opened device %s", USERSPACE_CTRL_IO_DEVICE_NAME);
return 0;
}
static ssize_t device_read(struct file *fp, char __user *buff,
size_t user_buf_length, loff_t *ppos)
{
size_t bytes_read;
unsigned long copy_result;
uint8_t *ping_buffer;
int result;
if (user_buf_length < ELLIPTIC_MSG_BUF_SIZE)
EL_PRINT_E("user space buffer user_buf_length too small : %zu",
user_buf_length);
bytes_read = 0;
copy_result = 0;
ping_buffer = NULL;
result = wait_event_interruptible(ctrl_device.data_available,
atomic_read(&ctrl_device.data_state) != 0);
if (result == 0) {
const int state = atomic_read(&ctrl_device.data_state);
if (state > 0) {
result = mutex_lock_interruptible(
&ctrl_device.data_lock);
if (result == 0) {
swap_ping_pong(&ctrl_device);
ping_buffer = get_ping_buffer(
&ctrl_device, &bytes_read);
if (bytes_read > user_buf_length) {
EL_PRINT_E(
"ping buffer size %zu larger than user buffer",
bytes_read);
goto fail;
}
copy_result = copy_to_user(buff, ping_buffer,
bytes_read);
if (copy_result > 0) {
EL_PRINT_E("Failed copy to user");
goto fail;
}
atomic_set(&ctrl_device.data_state, 0);
mutex_unlock(&ctrl_device.data_lock);
} else if (result == -EINTR) {
EL_PRINT_E("lock interrupted");
} else {
EL_PRINT_E("lock error = %d", result);
}
} else {
EL_PRINT_W("state = %d", state);
atomic_set(&ctrl_device.data_state, 0);
}
} else if (result == -ERESTARTSYS) {
EL_PRINT_E("interrupted");
} else {
EL_PRINT_E("wait_event error = %d", result);
}
return bytes_read;
fail:
atomic_set(&ctrl_device.data_state, 0);
mutex_unlock(&ctrl_device.data_lock);
return 0;
}
static int device_close(struct inode *inode, struct file *filp)
{
up(&ctrl_device.sem);
EL_PRINT_I("Closed device %s", USERSPACE_CTRL_IO_DEVICE_NAME);
return 0;
}
static const struct file_operations
elliptic_userspace_ctrl_fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.release = device_close,
};
int elliptic_userspace_ctrl_driver_init(void)
{
struct device *device;
dev_t device_number;
int err;
err = alloc_chrdev_region(
&device_number, 0, 1, USERSPACE_CTRL_IO_DEVICE_NAME);
if (err < 0) {
pr_err("failed to allocate chrdev region\n");
return err;
}
elliptic_userspace_ctrl_major = MAJOR(device_number);
device_number = MKDEV(elliptic_userspace_ctrl_major, 0);
device = device_create(
elliptic_class, NULL, device_number,
NULL, USERSPACE_CTRL_IO_DEVICE_NAME);
if (IS_ERR(device)) {
unregister_chrdev(
elliptic_userspace_ctrl_major,
USERSPACE_CTRL_IO_DEVICE_NAME);
EL_PRINT_E("Failed to create the device\n");
return PTR_ERR(device);
}
cdev_init(&ctrl_device.cdev, &elliptic_userspace_ctrl_fops);
ctrl_device.cdev.owner = THIS_MODULE;
err = cdev_add(&ctrl_device.cdev, device_number, 1);
if (err) {
EL_PRINT_W("error %d while trying to add %s%d",
err, ELLIPTIC_DEVICENAME, 0);
return err;
}
sema_init(&ctrl_device.sem, 1);
mutex_init(&ctrl_device.data_lock);
init_waitqueue_head(&ctrl_device.data_available);
return 0;
}
void elliptic_userspace_ctrl_driver_exit(void)
{
BUG_ON(elliptic_class == NULL);
device_destroy(elliptic_class, MKDEV(elliptic_userspace_ctrl_major, 0));
cdev_del(&ctrl_device.cdev);
unregister_chrdev(elliptic_userspace_ctrl_major,
USERSPACE_CTRL_IO_DEVICE_NAME);
up(&ctrl_device.sem);
}
int32_t elliptic_userspace_ctrl_write(uint32_t message_id,
const char *data, size_t data_size){
uint8_t *pong_buffer;
if (data_size > ELLIPTIC_MSG_BUF_SIZE) {
EL_PRINT_E("data size : %zu larger than buf size : %zu",
data_size, (size_t)ELLIPTIC_MSG_BUF_SIZE);
return -EINVAL;
}
mutex_lock(&ctrl_device.data_lock);
pong_buffer = get_pong_buffer(&ctrl_device, NULL);
set_pong_buffer_size(&ctrl_device, data_size);
memcpy(pong_buffer, data, data_size);
wake_up_interruptible(&ctrl_device.data_available);
atomic_set(&ctrl_device.data_state, 1);
mutex_unlock(&ctrl_device.data_lock);
return 0;
}

View File

@ -0,0 +1,149 @@
/**
* Copyright Elliptic Labs
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
/* includes the file structure, that is, file open read close */
#include <linux/fs.h>
/* include the character device, makes cdev avilable */
#include <linux/cdev.h>
#include <linux/semaphore.h>
/* includes copy_user vice versa */
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <elliptic/elliptic_data_io.h>
#include <elliptic/elliptic_device.h>
static dev_t elliptic_userspace_major;
#define USERSPACE_IO_DEVICE_NAME "elliptic_us_io"
struct elliptic_userspace_device {
struct cdev cdev;
struct semaphore sem;
};
static struct elliptic_userspace_device io_device;
static int device_open(struct inode *inode, struct file *filp)
{
if (inode->i_cdev != &io_device.cdev) {
pr_warn("elliptic : dev pointer mismatch\n");
return -ENODEV; /* No such device */
}
if (down_interruptible(&io_device.sem) != 0)
return -EEXIST;
EL_PRINT_I("Opened device %s", USERSPACE_IO_DEVICE_NAME);
return 0;
}
static ssize_t device_write(struct file *fp, const char __user *buff,
size_t length, loff_t *ppos)
{
int push_result;
push_result = elliptic_data_push(
ELLIPTIC_ALL_DEVICES, buff, length, ELLIPTIC_DATA_PUSH_FROM_USERSPACE);
return push_result == 0 ? (ssize_t)length : (ssize_t)(-1);
}
static int device_close(struct inode *inode, struct file *filp)
{
up(&io_device.sem);
EL_PRINT_I("Closed device %s", USERSPACE_IO_DEVICE_NAME);
return 0;
}
static const struct file_operations
elliptic_userspace_fops = {
.owner = THIS_MODULE,
.open = device_open,
.write = device_write,
.release = device_close,
};
int elliptic_userspace_io_driver_init(void)
{
struct device *device;
dev_t device_number;
int err;
err = alloc_chrdev_region(
&device_number, 0, 1, USERSPACE_IO_DEVICE_NAME);
if (err < 0) {
pr_err("failed to allocate chrdev region\n");
return err;
}
elliptic_userspace_major = MAJOR(device_number);
device_number = MKDEV(elliptic_userspace_major, 0);
device = device_create(
elliptic_class, NULL, device_number,
NULL, USERSPACE_IO_DEVICE_NAME);
if (IS_ERR(device)) {
unregister_chrdev(
elliptic_userspace_major, USERSPACE_IO_DEVICE_NAME);
pr_err("Failed to create the device\n");
return PTR_ERR(device);
}
cdev_init(&io_device.cdev, &elliptic_userspace_fops);
io_device.cdev.owner = THIS_MODULE;
err = cdev_add(&io_device.cdev, device_number, 1);
if (err) {
EL_PRINT_W("error %d while trying to add %s%d",
err, ELLIPTIC_DEVICENAME, 0);
return err;
}
sema_init(&io_device.sem, 1);
return 0;
}
void elliptic_userspace_io_driver_exit(void)
{
BUG_ON(elliptic_class == NULL);
device_destroy(elliptic_class, MKDEV(elliptic_userspace_major, 0));
cdev_del(&io_device.cdev);
unregister_chrdev(elliptic_userspace_major, USERSPACE_IO_DEVICE_NAME);
up(&io_device.sem);
}

View File

@ -0,0 +1,2 @@
# TODO: add config parameter for which io module to build
#obj-y += elliptic_data_io.o

View File

@ -0,0 +1,123 @@
/**
* Copyright Elliptic Labs
*
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <elliptic/elliptic_data_io.h>
#include <elliptic/elliptic_device.h>
#define USE_IRQ 11
static struct task_struct *simulating_task;
static atomic_t cancel;
struct elliptic_data_io_state {
};
#define BUFFER_SIZE 128
static int32_t output_buffer[BUFFER_SIZE];
irqreturn_t irq_handler(int irq, void *dev_id)
{
int result;
result = elliptic_data_push(ELLIPTIC_ALL_DEVICES,
(const char *)output_buffer, BUFFER_SIZE * sizeof(int32_t));
return 0;
}
static void fill_buffer(int32_t *buffer, size_t len, int32_t value)
{
size_t i;
for (i = 0; i < len; ++i)
buffer[i] = value;
}
int simulating_thread(void *context)
{
static int32_t count;
int result;
count = 0;
msleep(100);
pr_debug("%s\n", __func__);
while (atomic_read(&cancel) == 0) {
if (kthread_should_stop())
do_exit(0);
fill_buffer(output_buffer, BUFFER_SIZE, count);
++count;
if (result != 0) {
pr_warn("failed to push data\n");
}
asm("int $0x3B"); /* Corresponding to irq 11 */
msleep(0);
}
return 0;
}
int32_t elliptic_data_io_write(uint32_t message_id, const char *data,
size_t data_size) {
return 0;
}
int32_t elliptic_data_io_transact(uint32_t message_id, const char *data,
size_t data_size, char *output_data, size_t output_data_size) {
return 0;
}
void elliptic_data_io_cancel(struct elliptic_data *elliptic_data)
{
atomic_set(&elliptic_data->abort_io, 1);
wake_up_interruptible(&elliptic_data->fifo_isr_not_empty);
}
int elliptic_data_io_initialize(void)
{
pr_debug("%s\n", __func__);
atomic_set(&cancel, 0);
simulating_task = kthread_run(&simulating_thread, NULL,
"el_simulating_thread");
if (request_irq(USE_IRQ, irq_handler, IRQF_SHARED, "my_device",
(void *)(irq_handler))) {
pr_debug("my_device: cannot register IRQ ");
return -EPERM;
}
return 0;
}
int elliptic_data_io_cleanup(void)
{
free_irq(USE_IRQ, (void *)(irq_handler));
kthread_stop(simulating_task);
atomic_set(&cancel, 1);
msleep(200);
return 0;
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <linux/types.h>
#include <dsp/apr_audio-v2.h>
#include <elliptic/elliptic_data_io.h>
#include <linux/delay.h>
#define ELLIPTIC_SET_PARAMS_SIZE 114
#define ELLIPTIC_ULTRASOUND_MODULE_TX 0x0F010201
#define ELLIPTIC_ULTRASOUND_MODULE_RX 0x0FF10202
#define ULTRASOUND_OPCODE 0x0FF10204
/* This need to be updated for all platforms */
#define ELLIPTIC_PORT_ID AFE_PORT_ID_TX_CODEC_DMA_TX_4
/** Sequence of Elliptic Labs Ultrasound module parameters */
struct afe_ultrasound_set_params_t {
uint32_t payload[ELLIPTIC_SET_PARAMS_SIZE];
} __packed;
/** Sequence of Elliptic Labs Ultrasound module parameters */
/** Elliptic APR public */
int32_t ultrasound_apr_set_parameter(int32_t port_id, uint32_t param_id,
u8 *user_params, int32_t length);
int32_t elliptic_process_apr_payload(uint32_t *payload);
int elliptic_notify_gain_change_msg(int component_id, int gaindb);
typedef struct afe_ultrasound_state {
atomic_t us_apr_state;
void **ptr_apr;
atomic_t *ptr_status;
atomic_t *ptr_state;
wait_queue_head_t *ptr_wait;
struct mutex *ptr_afe_apr_lock;
int timeout_ms;
} afe_ultrasound_state_t;
extern afe_ultrasound_state_t elus_afe;

View File

@ -0,0 +1,155 @@
/**
* Copyright Elliptic Labs 2015-2016
*
*/
#pragma once
#include <linux/types.h>
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#define ELLIPTIC_DATA_IO_AP_TO_DSP 0
#define ELLIPTIC_DATA_IO_DSP_TO_AP 1
#define ELLIPTIC_DATA_IO_READ_OK 0
#define ELLIPTIC_DATA_IO_READ_BUSY 1
#define ELLIPTIC_DATA_IO_READ_CANCEL 2
#define ELLIPTIC_MSG_BUF_SIZE 512
/* wake source timeout in ms*/
#define ELLIPTIC_WAKEUP_TIMEOUT 250
#define ELLIPTIC_DATA_FIFO_SIZE (PAGE_SIZE)
#define ULTRASOUND_RX_PORT_ID 0
#define ULTRASOUND_TX_PORT_ID 1
/* Elliptic Labs UltraSound Module */
#define ELLIPTIC_ULTRASOUND_DISABLE 0
#define ELLIPTIC_ULTRASOUND_ENABLE 1
#define ELLIPTIC_ULTRASOUND_SET_PARAMS 2
#define ELLIPTIC_ULTRASOUND_GET_PARAMS 3
#define ELLIPTIC_ULTRASOUND_RAMP_DOWN 4
/** Param ID definition */
#define ELLIPTIC_ULTRASOUND_PARAM_ID_ENGINE_DATA 3
#define ELLIPTIC_ULTRASOUND_PARAM_ID_CALIBRATION_DATA 11
#define ELLIPTIC_ULTRASOUND_PARAM_ID_ENGINE_VERSION 12
#define ELLIPTIC_ULTRASOUND_PARAM_ID_BUILD_BRANCH 14
#define ELLIPTIC_ULTRASOUND_PARAM_ID_CALIBRATION_V2_DATA 15
#define ELLIPTIC_ULTRASOUND_PARAM_ID_SENSORHUB 16
#define ELLIPTIC_ULTRASOUND_PARAM_ID_DIAGNOSTICS_DATA 17
#define ELLIPTIC_ULTRASOUND_PARAM_ID_TAG 18
#define ELLIPTIC_ULTRASOUND_PARAM_ID_ML_DATA 19
#define ELLIPTIC_DATA_READ_BUSY 0
#define ELLIPTIC_DATA_READ_OK 1
#define ELLIPTIC_DATA_READ_CANCEL 2
#define ELLIPTIC_ALL_DEVICES -1
#define ELLIPTIC_DEVICE_0 0
#define ELLIPTIC_DEVICE_1 1
enum elliptic_message_id {
ELLIPTIC_MESSAGE_PAYLOAD, /* Input to AP*/
ELLIPTIC_MESSAGE_RAW, /* Output from AP*/
ELLIPTIC_MESSAGE_CALIBRATION,
ELLIPTIC_MESSAGE_CALIBRATION_V2,
ELLIPTIC_MESSAGE_DIAGNOSTICS,
ELLIPTIC_MAX_MESSAGE_IDS
};
typedef enum {
ELLIPTIC_DATA_PUSH_FROM_KERNEL,
ELLIPTIC_DATA_PUSH_FROM_USERSPACE
} elliptic_data_push_t;
struct elliptic_data {
/* wake lock timeout */
unsigned int wakeup_timeout;
/* members for top half interrupt handling */
struct kfifo fifo_isr;
spinlock_t fifo_isr_spinlock;
wait_queue_head_t fifo_isr_not_empty;
struct mutex user_buffer_lock;
/* buffer to swap data from isr fifo to userspace */
uint8_t isr_swap_buffer[ELLIPTIC_MSG_BUF_SIZE];
atomic_t abort_io;
/* debug counters, reset between open/close */
uint32_t isr_fifo_discard;
/* debug counters, persistent */
uint32_t isr_fifo_discard_total;
uint32_t userspace_read_total;
uint32_t isr_write_total;
};
/* Elliptic IO module API (implemented by IO module)*/
int elliptic_data_io_initialize(void);
int elliptic_data_io_cleanup(void);
int32_t elliptic_data_io_write(uint32_t message_id, const char *data,
size_t data_size);
int32_t elliptic_data_io_transact(uint32_t message_id, const char *data,
size_t data_size, char *output_data, size_t output_data_size);
/* Elliptic driver API (implemented by main driver)*/
int elliptic_data_initialize(struct elliptic_data *,
size_t max_queue_size, unsigned int wakeup_timeout, int id);
int elliptic_data_cleanup(struct elliptic_data *);
void elliptic_data_reset_debug_counters(struct elliptic_data *);
void elliptic_data_update_debug_counters(struct elliptic_data *);
void elliptic_data_print_debug_counters(struct elliptic_data *);
/* Called from elliptic device read */
size_t elliptic_data_pop(struct elliptic_data *,
char __user *buffer, size_t buffer_size);
/* Used for cancelling a blocking read */
void elliptic_data_cancel(struct elliptic_data *);
/* Called from IO module*/
int elliptic_data_push(int deviceid, const char *buffer, size_t buffer_size, elliptic_data_push_t);
/* Writes to io module and user space control */
int32_t elliptic_data_write(uint32_t message_id,
const char *data, size_t data_size);
/* Opens port */
int elliptic_open_port(int portid);
/* Closes port */
int elliptic_close_port(int portid);
/* Opens port */
int elliptic_io_open_port(int portid);
/* Closes port */
int elliptic_io_close_port(int portid);
/* Create device node for userspace io driver*/
int elliptic_userspace_io_driver_init(void);
void elliptic_userspace_io_driver_exit(void);
/* Create device node for userspace io driver*/
int elliptic_userspace_ctrl_driver_init(void);
void elliptic_userspace_ctrl_driver_exit(void);
int32_t elliptic_userspace_ctrl_write(uint32_t message_id,
const char *data, size_t data_size);

View File

@ -0,0 +1,55 @@
/**
* Copyright Elliptic Labs
*
*/
#pragma once
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/semaphore.h>
#include <elliptic/elliptic_data_io.h>
#define ELLIPTIC_DEVICENAME "elliptic"
#define ELLIPTIC_NUM_DEVICES 2
#define IOCTL_ELLIPTIC_APP 197
#define MIRROR_TAG 0x3D0A4842
#define IOCTL_ELLIPTIC_DATA_IO_CANCEL \
_IO(IOCTL_ELLIPTIC_APP, 2)
#define IOCTL_ELLIPTIC_ACTIVATE_ENGINE \
_IOW(IOCTL_ELLIPTIC_APP, 3, int)
#define IOCTL_ELLIPTIC_SET_RAMP_DOWN \
_IO(IOCTL_ELLIPTIC_APP, 4)
#define IOCTL_ELLIPTIC_SYSTEM_CONFIGURATION \
_IOW(IOCTL_ELLIPTIC_APP, 5, int)
#define IOCTL_ELLIPTIC_DATA_IO_MIRROR \
_IOW(IOCTL_ELLIPTIC_APP, 117, unsigned char *)
struct elliptic_device {
int opened;
struct cdev cdev;
struct semaphore sem;
struct device *device;
struct elliptic_data el_data;
};
extern struct class *elliptic_class;
#define EL_PRINT_E(string, arg...) \
pr_err("[ELUS] : (%s) : " string "\n", __func__, ##arg)
#define EL_PRINT_W(string, arg...) \
pr_warn("[ELUS] : (%s) : " string "\n", __func__, ##arg)
#define EL_PRINT_I(string, arg...) \
pr_info("[ELUS] : (%s) : " string "\n", __func__, ##arg)
#define EL_PRINT_D(string, arg...) \
pr_debug("[ELUS] : (%s) : " string "\n", __func__, ##arg)

View File

@ -0,0 +1,190 @@
#pragma once
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <linux/types.h>
#define ELLIPTIC_OBJ_ID_CALIBRATION_DATA 1
#define ELLIPTIC_OBJ_ID_VERSION_INFO 2
#define ELLIPTIC_OBJ_ID_BRANCH_INFO 3
#define ELLIPTIC_OBJ_ID_CALIBRATION_V2_DATA 4
#define ELLIPTIC_OBJ_ID_DIAGNOSTICS_DATA 5
#define ELLIPTIC_OBJ_ID_TAG_INFO 6
#define ELLIPTIC_OBJ_ID_ML_DATA 7
#define ELLIPTIC_SYSTEM_CONFIGURATION_SIZE 96
#define ELLIPTIC_CALIBRATION_DATA_SIZE 64
#define ELLIPTIC_CALIBRATION_V2_DATA_SIZE 448
#define ELLIPTIC_DIAGNOSTICS_DATA_SIZE 448
#define ELLIPTIC_DIAGNOSTICS_U32_DATA_VALUES (ELLIPTIC_DIAGNOSTICS_DATA_SIZE>>2)
#define ELLIPTIC_SENSOR_DATA_SIZE 68
#define ELLIPTIC_SENSOR_U32_DATA_VALUES (ELLIPTIC_SENSOR_DATA_SIZE>>2)
#define ELLIPTIC_VERSION_INFO_SIZE 16
#define ELLIPTIC_BRANCH_INFO_SIZE 32
#define ELLIPTIC_BRANCH_INFO_MAX_SIZE 128
#define ELLIPTIC_TAG_INFO_SIZE 32
#define ELLIPTIC_ML_DATA_SIZE 432
#define ELLIPTIC_ULTRASOUND_DISABLE 0
#define ELLIPTIC_ULTRASOUND_ENABLE 1
#define ELLIPTIC_ULTRASOUND_SET_PARAMS 2
#define ELLIPTIC_ULTRASOUND_GET_PARAMS 3
#define ELLIPTIC_ULTRASOUND_RAMP_DOWN 4
/** register */
#define ELLIPTIC_CALIBRATION 1
/** bits */
#define ELLIPTIC_CALIBRATION_STATE 0
#define ELLIPTIC_CALIBRATION_PROFILE 1
#define ELLIPTIC_ULTRASOUND_GAIN 2
/** custom settings */
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_COUNT 16
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_MAX_VALUE 0x7FFFFFFF
/** register */
#define ELLIPTIC_SYSTEM_CONFIGURATION 0
/** bits */
#define ELLIPTIC_SYSTEM_CONFIGURATION_LATENCY 0
#define ELLIPTIC_SYSTEM_CONFIGURATION_SENSITIVITY 1
#define ELLIPTIC_SYSTEM_CONFIGURATION_SPEAKER_SCALING 2
#define ELLIPTIC_SYSTEM_CONFIGURATION_MICROPHONE_INDEX 3
#define ELLIPTIC_SYSTEM_CONFIGURATION_OPERATION_MODE 4
#define ELLIPTIC_SYSTEM_CONFIGURATION_OPERATION_MODE_FLAGS 5
#define ELLIPTIC_SYSTEM_CONFIGURATION_LOG_LEVEL 6
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_0 7
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_1 8
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_2 9
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_3 10
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_4 11
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_5 12
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_6 13
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_7 14
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_8 15
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_9 16
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_10 17
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_11 18
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_12 19
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_13 20
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_14 21
#define ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_15 22
#define ELLIPTIC_SYSTEM_CONFIGURATION_SUSPEND 23
#define ELLIPTIC_SYSTEM_CONFIGURATION_INPUT_ENABLED 24
#define ELLIPTIC_SYSTEM_CONFIGURATION_OUTPUT_ENABLED 25
#define ELLIPTIC_SYSTEM_CONFIGURATION_EXTERNAL_EVENT 26
#define ELLIPTIC_SYSTEM_CONFIGURATION_CALIBRATION_METHOD 27
#define ELLIPTIC_SYSTEM_CONFIGURATION_DEBUG_MODE 28
#define ELLIPTIC_SYSTEM_CONFIGURATION_NUMBER_OF_RUNS 29
#define ELLIPTIC_SYSTEM_CONFIGURATION_CONTEXT 30
#define ELLIPTIC_SYSTEM_CONFIGURATION_CAPTURE 31
#define ELLIPTIC_SYSTEM_CONFIGURATION_INPUT_CHANNELS 32
#define ELLIPTIC_SYSTEM_CONFIGURATION_REPORT_NONE 33
#define ELLIPTIC_SYSTEM_CONFIGURATION_RE_SEND 34
#define ELLIPTIC_SYSTEM_CONFIGURATION_MAX_CONTEXT_VALUE 0x7FFFFFFF
struct elliptic_engine_version_info {
uint32_t major;
uint32_t minor;
uint32_t build;
uint32_t revision;
};
struct elliptic_shared_data_block {
uint32_t object_id;
size_t size;
void *buffer;
};
struct elliptic_shared_data_block *elliptic_get_shared_obj(uint32_t
object_id);
extern unsigned int elliptic_add_component_controls(void *platform);
void elliptic_set_calibration_data(uint8_t *calib_data, size_t size);
enum elliptic_system_configuration_parameter_type {
ESCPT_SPEAKER_SCALING = 1,
ESCPT_CHANNEL_SENSITIVITY,
ESCPT_LATENCY,
ESCPT_MICROPHONE_INDEX,
ESCPT_OPERATION_MODE,
ESCPT_OPERATION_MODE_FLAGS,
ESCPT_COMPONENT_GAIN_CHANGE,
ESCPT_CALIBRATION_STATE,
ESCPT_ENGINE_VERSION,
ESCPT_CALIBRATION_PROFILE,
ESCPT_ULTRASOUND_GAIN,
ESCPT_LOG_LEVEL,
ESCPT_BUILD_BRANCH,
ESCPT_FSELECTION,
ESCPT_ENGINE_DIAGNOSTICS,
ESCPT_ENGINE_CUSTOM_SETTING_0,
ESCPT_ENGINE_CUSTOM_SETTING_1,
ESCPT_ENGINE_CUSTOM_SETTING_2,
ESCPT_ENGINE_CUSTOM_SETTING_3,
ESCPT_ENGINE_CUSTOM_SETTING_4,
ESCPT_ENGINE_CUSTOM_SETTING_5,
ESCPT_ENGINE_CUSTOM_SETTING_6,
ESCPT_ENGINE_CUSTOM_SETTING_7,
ESCPT_ENGINE_CUSTOM_SETTING_8,
ESCPT_ENGINE_CUSTOM_SETTING_9,
ESCPT_ENGINE_CUSTOM_SETTING_10,
ESCPT_ENGINE_CUSTOM_SETTING_11,
ESCPT_ENGINE_CUSTOM_SETTING_12,
ESCPT_ENGINE_CUSTOM_SETTING_13,
ESCPT_ENGINE_CUSTOM_SETTING_14,
ESCPT_ENGINE_CUSTOM_SETTING_15,
ESCPT_SUSPEND, // 32
ESCPT_INPUT_ENABLED,
ESCPT_OUTPUT_ENABLED,
ESCPT_EXTERNAL_EVENT,
ESCPT_ENGINE_TAG, //36
ESCPT_CALIBRATION_METHOD,
ESCPT_DEBUG_MODE,
ESCPT_NUMBER_OF_RUNS,
ESCPT_CONTEXT,
ESCPT_CAPTURE,
ESCPT_INPUT_CHANNELS,
ESCPT_RE_SEND=255,
};
struct elliptic_system_configuration_parameters_cache {
int32_t speaker_scaling[2];
int32_t sensitivity;
int32_t latency;
int32_t microphone_index;
int32_t operation_mode;
int32_t operation_mode_flags;
int32_t component_gain_change;
int32_t calibration_state;
int32_t engine_version;
int32_t calibration_profile;
int32_t ultrasound_gain;
int32_t log_level;
int32_t custom_settings[ELLIPTIC_SYSTEM_CONFIGURATION_CUSTOM_SETTING_COUNT];
int32_t engine_suspend;
int32_t input_enabled;
int32_t output_enabled;
int32_t external_event;
int32_t calibration_method;
int32_t debug_mode;
int32_t number_of_runs;
int32_t context;
int32_t capture;
int32_t input_channels;
int32_t re_send;
};
int elliptic_trigger_version_msg(void);
int elliptic_trigger_branch_msg(void);
int elliptic_trigger_tag_msg(void);
int elliptic_trigger_diagnostics_msg(void);

View File

@ -0,0 +1,15 @@
#pragma once
#define ELLIPTIC_SYSFS_ENGINE_FOLDER "engine"
#define ELLIPTIC_SYSFS_ROOT_FOLDER "elliptic"
#define ELLIPTIC_SYSFS_CALIBRATION_FILENAME "calibration"
#define ELLIPTIC_SYSFS_VERSION_FILENAME "version"
#define ELLIPTIC_SYSFS_CALIBRATION_V2_FILENAME "calibration_v2"
#define ELLIPTIC_SYSFS_STATE_FILENAME "state"
#define ELLIPTIC_SYSFS_TAG_FILENAME "tag"
#define ELLIPTIC_SYSFS_OPMODE_FILENAME "opmode"
#define ELLIPTIC_SYSFS_OPMODE_FLAGS_FILENAME "opmode_flags"
int elliptic_initialize_sysfs(void);
void elliptic_cleanup_sysfs(void);