driver: 内部構造を変更

This commit is contained in:
nns779
2020-10-25 18:08:04 +09:00
parent 160b89e656
commit 73a76f15c1
31 changed files with 4614 additions and 4536 deletions

View File

@@ -5,7 +5,6 @@ DEBUG := 0
dMAX_DEVICE := 0 dMAX_DEVICE := 0
dPSB_DEBUG := 0 dPSB_DEBUG := 0
dITEDTV_BUS_USE_WORKQUEUE := 0 dITEDTV_BUS_USE_WORKQUEUE := 0
dRINGBUFFER_USE_SPINLOCK := 0
ccflags-y := -I$(M)/../include ccflags-y := -I$(M)/../include
@@ -13,8 +12,8 @@ ifneq ($(DEBUG),0)
ccflags-y += -DDEBUG -g ccflags-y += -DDEBUG -g
endif endif
ifneq ($(dMAX_DEVICE),0) ifneq ($(dPX4_USB_MAX_DEVICE),0)
ccflags-y += -DMAX_DEVICE=$(dMAX_DEVICE) ccflags-y += -DMAX_DEVICE=$(dPX4_USB_MAX_DEVICE)
endif endif
ifneq ($(dPSB_DEBUG),0) ifneq ($(dPSB_DEBUG),0)
ccflags-y += -DPSB_DEBUG ccflags-y += -DPSB_DEBUG
@@ -22,9 +21,6 @@ endif
ifneq ($(dITEDTV_BUS_USE_WORKQUEUE),0) ifneq ($(dITEDTV_BUS_USE_WORKQUEUE),0)
ccflags-y += -DITEDTV_BUS_USE_WORKQUEUE ccflags-y += -DITEDTV_BUS_USE_WORKQUEUE
endif endif
ifneq ($(dRINGBUFFER_USE_SPINLOCK),0)
ccflags-y += -DRINGBUFFER_USE_SPINLOCK
endif
obj-m := px4_drv.o obj-m := px4_drv.o
px4_drv-y := driver.o module_param.o px4.o isdb2056.o it930x.o itedtv_bus.o tc90522.o rt710.o r850.o ringbuffer.o px4_drv-y := driver_module.o ptx_chrdev.o px4_usb.o px4_usb_params.o px4_device.o px4_device_params.o px4_mldev.o isdb2056_device.o it930x.o itedtv_bus.o tc90522.o r850.o rt710.o ringbuffer.o

View File

@@ -1,71 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Module initiator of the driver (driver.c)
*
* Copyright (c) 2019 nns779
*/
#include "print_format.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "driver.h"
#include "revision.h"
#include "firmware.h"
#include "px4.h"
#include "isdb2056.h"
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR("nns779");
MODULE_DESCRIPTION("Unofficial Linux driver for PLEX PX-W3U4/Q3U4/W3PE4/Q3PE4 ISDB-T/S receivers");
MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE(FIRMWARE_FILENAME);
static int driver_module_init(void)
{
int ret = 0;
pr_info(KBUILD_MODNAME
#ifdef DRIVER_VERSION
" version " DRIVER_VERSION
#endif
#ifdef REVISION_NUMBER
#if defined(DRIVER_VERSION)
","
#endif
" rev: " REVISION_NUMBER
#endif
#ifdef COMMIT_HASH
#if defined(PX4_DRIVER_VERSION) || defined(REVISION_NUMBER)
","
#endif
" commit: " COMMIT_HASH
#endif
#ifdef REVISION_NAME
" @ " REVISION_NAME
#endif
"\n");
ret = px4_register();
if (ret)
goto exit;
ret = isdb2056_register();
if (ret)
goto exit;
exit:
return ret;
}
static void driver_module_exit(void)
{
px4_unregister();
isdb2056_unregister();
}
module_init(driver_module_init);
module_exit(driver_module_exit);

View File

@@ -1,8 +0,0 @@
// driver.h
#ifndef __DRIVER_H__
#define __DRIVER_H__
#define DRIVER_VERSION "0.2.1"
#endif

61
driver/driver_module.c Normal file
View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Module initiator of the driver (driver_module.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "print_format.h"
#include "driver_module.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "revision.h"
#include "px4_usb.h"
#include "firmware.h"
int init_module(void)
{
int ret = 0;
pr_info(KBUILD_MODNAME
#ifdef PX4_DRV_VERSION
" version " PX4_DRV_VERSION
#endif
#ifdef REVISION_NUMBER
#if defined(PX4_DRV_VERSION)
","
#endif
" rev: " REVISION_NUMBER
#endif
#ifdef COMMIT_HASH
#if defined(PX4_DRV_VERSION) || defined(REVISION_NUMBER)
","
#endif
" commit: " COMMIT_HASH
#endif
#ifdef REVISION_NAME
" @ " REVISION_NAME
#endif
"\n");
ret = px4_usb_register();
if (ret)
return ret;
return 0;
}
void cleanup_module(void)
{
px4_usb_unregister();
}
MODULE_VERSION(PX4_DRV_VERSION);
MODULE_AUTHOR("nns779");
MODULE_DESCRIPTION("Unofficial Linux driver for PLEX PX-W3U4/Q3U4/W3PE4/Q3PE4 ISDB-T/S receivers");
MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE(IT930X_FIRMWARE_FILENAME);

8
driver/driver_module.h Normal file
View File

@@ -0,0 +1,8 @@
// module.h
#ifndef __DRIVER_MODULE_H__
#define __DRIVER_MODULE_H__
#define PX4_DRV_VERSION "0.4.0"
#endif

View File

@@ -3,8 +3,6 @@
#ifndef __FIRMWARE_H__ #ifndef __FIRMWARE_H__
#define __FIRMWARE_H__ #define __FIRMWARE_H__
#if !defined(FIRMWARE_FILENAME) #define IT930X_FIRMWARE_FILENAME "it930x-firmware.bin"
#define FIRMWARE_FILENAME "it930x-firmware.bin"
#endif
#endif #endif

View File

@@ -26,12 +26,19 @@ struct i2c_comm_request {
}; };
struct i2c_comm_master { struct i2c_comm_master {
int (*request) (void *i2c_priv, int (*gate_ctrl)(void *i2c_priv, bool open);
int (*request)(void *i2c_priv,
const struct i2c_comm_request *req, const struct i2c_comm_request *req,
int num); int num);
void *priv; void *priv;
}; };
static inline int i2c_comm_master_gate_ctrl(const struct i2c_comm_master *m,
bool open)
{
return ((m && m ->gate_ctrl) ? m->gate_ctrl(m->priv, open) : -EFAULT);
}
static inline int i2c_comm_master_request(const struct i2c_comm_master *m, static inline int i2c_comm_master_request(const struct i2c_comm_master *m,
const struct i2c_comm_request *req, const struct i2c_comm_request *req,
int num) int num)

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
// isdb2056.h
#ifndef __ISDB2056_H__
#define __ISDB2056_H__
int isdb2056_register(void);
void isdb2056_unregister(void);
#endif

1060
driver/isdb2056_device.c Normal file

File diff suppressed because it is too large Load Diff

46
driver/isdb2056_device.h Normal file
View File

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PTX driver definitions for Digibest ISDB2056 device (isdb2056_device.h)
*
* Copyright (c) 2018-2020 nns779
*/
#ifndef __ISDB2056_DEVICE_H__
#define __ISDB2056_DEVICE_H__
#include <linux/atomic.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include "ptx_chrdev.h"
#include "it930x.h"
#include "tc90522.h"
#include "r850.h"
#include "rt710.h"
#define ISDB2056_CHRDEV_NUM 1
struct isdb2056_chrdev {
struct ptx_chrdev *chrdev;
struct tc90522_demod tc90522_t;
struct tc90522_demod tc90522_s;
struct r850_tuner r850;
struct rt710_tuner rt710;
};
struct isdb2056_device {
struct kref kref;
atomic_t available;
struct device *dev;
struct ptx_chrdev_group *chrdev_group;
struct isdb2056_chrdev chrdev2056;
struct it930x_bridge it930x;
void *stream_ctx;
};
int isdb2056_device_init(struct isdb2056_device *isdb2056, struct device *dev,
struct ptx_chrdev_context *chrdev_ctx);
void isdb2056_device_term(struct isdb2056_device *isdb2056);
#endif

View File

@@ -568,6 +568,7 @@ int it930x_init(struct it930x_bridge *it930x)
priv->i2c[i].it930x = it930x; priv->i2c[i].it930x = it930x;
priv->i2c[i].bus = i + 1; priv->i2c[i].bus = i + 1;
it930x->i2c_master[i].gate_ctrl = NULL;
it930x->i2c_master[i].request = it930x_i2c_master_request; it930x->i2c_master[i].request = it930x_i2c_master_request;
it930x->i2c_master[i].priv = &priv->i2c[i]; it930x->i2c_master[i].priv = &priv->i2c[i];
} }
@@ -597,6 +598,7 @@ int it930x_term(struct it930x_bridge *it930x)
/* clear the i2c operator */ /* clear the i2c operator */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
it930x->i2c_master[i].gate_ctrl = NULL;
it930x->i2c_master[i].request = NULL; it930x->i2c_master[i].request = NULL;
it930x->i2c_master[i].priv = NULL; it930x->i2c_master[i].priv = NULL;
} }

View File

@@ -1,45 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Module parameter definitions (module_param.c)
*
* Copyright (c) 2019 nns779
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "module_param.h"
unsigned int xfer_packets = 816;
unsigned int urb_max_packets = 816;
unsigned int max_urbs = 6;
unsigned int tsdev_max_packets = 2048;
int psb_purge_timeout = 2000;
bool no_dma = false;
bool disable_multi_device_power_control = false;
bool s_tuner_no_sleep = false;
module_param(xfer_packets, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(xfer_packets,
"Number of transfer packets from the device. (default: 816)");
module_param(urb_max_packets, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(urb_max_packets,
"Maximum number of TS packets per URB. (default: 816)");
module_param(max_urbs, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(max_urbs, "Maximum number of URBs. (default: 6)");
module_param(tsdev_max_packets, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(tsdev_max_packets,
"Maximum number of TS packets buffering in tsdev. (default: 2048)");
module_param(psb_purge_timeout, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param(no_dma, bool, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param(disable_multi_device_power_control, bool,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param(s_tuner_no_sleep, bool, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

View File

@@ -1,17 +0,0 @@
// module_param.h
#ifndef __MODULE_PARAM_H__
#define __MODULE_PARAM_H__
#include <linux/types.h>
extern unsigned int xfer_packets;
extern unsigned int urb_max_packets;
extern unsigned int max_urbs;
extern unsigned int tsdev_max_packets;
extern int psb_purge_timeout;
extern bool no_dma;
extern bool disable_multi_device_power_control;
extern bool s_tuner_no_sleep;
#endif

963
driver/ptx_chrdev.c Normal file
View File

@@ -0,0 +1,963 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Character device operator for PTX devices (ptx_chrdev.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "print_format.h"
#include "ptx_chrdev.h"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
static LIST_HEAD(ctx_list);
static DEFINE_MUTEX(ctx_list_lock);
static bool __ptx_chrdev_search_context(unsigned int major,
struct ptx_chrdev_context **chrdev_ctx);
static bool __ptx_chrdev_context_search_group(struct ptx_chrdev_context *chrdev_ctx,
unsigned int minor,
struct ptx_chrdev_group **chrdev_group);
static void ptx_chrdev_group_release(struct kref *kref);
static void ptx_chrdev_context_release(struct kref *kref);
static int ptx_chrdev_open(struct inode *inode, struct file *file)
{
int ret = 0;
unsigned int major, minor;
struct ptx_chrdev_context *ctx;
struct ptx_chrdev_group *group;
struct ptx_chrdev *chrdev = NULL;
major = imajor(inode);
minor = iminor(inode);
mutex_lock(&ctx_list_lock);
if (!__ptx_chrdev_search_context(major, &ctx)) {
mutex_unlock(&ctx_list_lock);
ret = -ENOENT;
goto fail;
}
kref_get(&ctx->kref);
mutex_lock(&ctx->lock);
mutex_unlock(&ctx_list_lock);
if (!__ptx_chrdev_context_search_group(ctx, minor, &group)) {
mutex_unlock(&ctx->lock);
ret = -ENOENT;
goto fail_ctx;
}
kref_get(&group->kref);
mutex_lock(&group->lock);
mutex_unlock(&ctx->lock);
if (!atomic_read(&group->available)) {
mutex_unlock(&group->lock);
ret = -ENOENT;
goto fail_group;
}
chrdev = &group->chrdev[minor - group->minor_base];
ret = (atomic_cmpxchg(&chrdev->open, 0, 1)) ? -EALREADY : 0;
if (ret) {
mutex_unlock(&group->lock);
goto fail_group;
}
mutex_lock(&chrdev->lock);
mutex_unlock(&group->lock);
if (chrdev->ops && chrdev->ops->open)
ret = chrdev->ops->open(chrdev);
if (!ret)
file->private_data = chrdev;
mutex_unlock(&chrdev->lock);
if (ret)
goto fail_chrdev;
return 0;
fail_chrdev:
atomic_dec_return(&chrdev->open);
fail_group:
kref_put(&group->kref, ptx_chrdev_group_release);
fail_ctx:
kref_put(&ctx->kref, ptx_chrdev_context_release);
fail:
return ret;
}
static ssize_t ptx_chrdev_read(struct file *file,
char __user *buf, size_t count, loff_t *ppos)
{
int ret = 0;
struct ptx_chrdev *chrdev = file->private_data;
struct ptx_chrdev_group *group = chrdev->parent;
u8 __user *p = buf;
size_t remain = count;
if (!atomic_read_acquire(&group->available))
return -EIO;
ringbuffer_ready_read(chrdev->ringbuf);
while (remain) {
size_t len;
if (wait_event_interruptible(chrdev->ringbuf_wait,
ringbuffer_is_readable(chrdev->ringbuf) ||
!ringbuffer_is_running(chrdev->ringbuf) ||
!atomic_read(&group->available))) {
if (remain == count)
ret = -EINTR;
break;
}
len = remain;
ret = ringbuffer_read_user(chrdev->ringbuf, p, &len);
if (ret || !len)
break;
p += len;
remain -= len;
}
return (ret < 0) ? ret : (count - remain);
}
static int ptx_chrdev_release(struct inode *inode, struct file *file)
{
int ret = 0;
struct ptx_chrdev *chrdev = file->private_data;
mutex_lock(&chrdev->lock);
if (chrdev->streaming) {
if (chrdev->ops && chrdev->ops->set_capture)
chrdev->ops->set_capture(chrdev, false);
ringbuffer_stop(chrdev->ringbuf);
wake_up(&chrdev->ringbuf_wait);
chrdev->streaming = false;
}
if (chrdev->ops && chrdev->ops->release)
ret = chrdev->ops->release(chrdev);
mutex_unlock(&chrdev->lock);
atomic_dec_return(&chrdev->open);
kref_put(&chrdev->parent->kref, ptx_chrdev_group_release);
kref_put(&chrdev->parent->parent->kref, ptx_chrdev_context_release);
return ret;
}
static long ptx_chrdev_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct ptx_chrdev *chrdev = file->private_data;
struct ptx_chrdev_group *group = chrdev->parent;
if (!atomic_read_acquire(&group->available))
return -EIO;
mutex_lock(&chrdev->lock);
switch (cmd) {
case PTX_SET_CHANNEL:
{
struct ptx_freq freq;
enum ptx_system_type system;
if (!chrdev->ops || !chrdev->ops->tune) {
ret = -ENOSYS;
break;
}
if (copy_from_user(&freq, (void *)arg, sizeof(freq))) {
ret = -EFAULT;
break;
}
system = chrdev->params.system;
switch (chrdev->params.system) {
case PTX_ISDB_S_SYSTEM:
if (freq.freq_no < 0) {
ret = -EINVAL;
break;
} else if (freq.freq_no < 12) {
// BS
if (freq.slot >= 8) {
ret = -EINVAL;
break;
}
chrdev->params.freq = 1049480 + (38360 * freq.freq_no);
} else if (freq.freq_no < 24) {
// CS
chrdev->params.freq = 1613000 + (40000 * (freq.freq_no - 12));
} else {
ret = -EINVAL;
break;
}
chrdev->params.bandwidth = 0;
chrdev->params.stream_id = freq.slot;
break;
case PTX_ISDB_T_SYSTEM:
if ((freq.freq_no >= 3 && freq.freq_no <= 12) ||
(freq.freq_no >= 22 && freq.freq_no <= 62)) {
// CATV C13-C22ch, C23-63ch
chrdev->params.freq = 93143 + freq.freq_no * 6000 + freq.slot/* addfreq */;
if (freq.freq_no == 12)
chrdev->params.freq += 2000;
} else if (freq.freq_no >= 63 && freq.freq_no <= 102) {
// UHF 13-52ch
chrdev->params.freq = 95143 + freq.freq_no * 6000 + freq.slot/* addfreq */;
} else {
// Unknown channel
ret = -EINVAL;
break;
}
chrdev->params.bandwidth = 6;
chrdev->params.stream_id = 0;
break;
case PTX_UNSPECIFIED_SYSTEM:
if (chrdev->system_cap & PTX_ISDB_S_SYSTEM) {
if (freq.freq_no < 0) {
ret = -EINVAL;
break;
} else if (freq.freq_no < 12) {
// BS
if (freq.slot >= 8) {
ret = -EINVAL;
break;
}
chrdev->params.freq = 1049480 + (38360 * freq.freq_no);
} else if (freq.freq_no < 24) {
// CS
chrdev->params.freq = 1613000 + (40000 * (freq.freq_no - 12));
} else {
ret = -EINVAL;
break;
}
chrdev->params.bandwidth = 0;
chrdev->params.stream_id = freq.slot;
chrdev->params.system = PTX_ISDB_S_SYSTEM;
} else if (chrdev->system_cap & PTX_ISDB_T_SYSTEM) {
if (freq.freq_no >= 63 && freq.freq_no <= 102) {
// UHF 13-52ch
chrdev->params.freq = 95143 + freq.freq_no * 6000 + freq.slot/* addfreq */;
chrdev->params.bandwidth = 6;
chrdev->params.stream_id = 0;
chrdev->params.system = PTX_ISDB_T_SYSTEM;
} else {
ret = -EINVAL;
break;
}
} else {
ret = -EINVAL;
break;
}
break;
default:
ret = -ENOSYS;
break;
}
if (ret)
break;
ret = chrdev->ops->tune(chrdev, &chrdev->params);
if (ret) {
chrdev->params.system = system;
break;
}
chrdev->current_system = chrdev->params.system;
chrdev->params.system = system;
if (chrdev->ops->check_lock) {
int i;
bool locked = false;
i = 300;
while (i--) {
ret = chrdev->ops->check_lock(chrdev, &locked);
if (!ret && locked)
break;
msleep(10);
}
if (!locked) {
ret = -EAGAIN;
break;
}
}
if (chrdev->current_system == PTX_ISDB_S_SYSTEM &&
chrdev->ops->set_stream_id)
ret = chrdev->ops->set_stream_id(chrdev,
chrdev->params.stream_id);
break;
}
case PTX_START_STREAMING:
if (chrdev->streaming) {
ret = -EALREADY;
break;
}
chrdev->ringbuf_write_size = 0;
if (chrdev->ops && chrdev->ops->set_capture)
ret = chrdev->ops->set_capture(chrdev, true);
else
ret = -ENOSYS;
if (!ret) {
ringbuffer_reset(chrdev->ringbuf);
ringbuffer_start(chrdev->ringbuf);
chrdev->streaming = true;
}
break;
case PTX_STOP_STREAMING:
if (!chrdev->streaming) {
ret = -EALREADY;
break;
}
if (chrdev->ops && chrdev->ops->set_capture)
ret = chrdev->ops->set_capture(chrdev, false);
else
ret = -ENOSYS;
if (!ret) {
ringbuffer_stop(chrdev->ringbuf);
wake_up(&chrdev->ringbuf_wait);
chrdev->streaming = false;
}
break;
case PTX_GET_CNR:
{
u32 cn = 0;
if (chrdev->ops && chrdev->ops->read_cnr_raw)
ret = chrdev->ops->read_cnr_raw(chrdev, &cn);
else
ret = -ENOSYS;
if (ret)
break;
if (copy_to_user((void *)arg, &cn, sizeof(cn)))
ret = -EFAULT;
break;
}
case PTX_ENABLE_LNB_POWER:
if (chrdev->ops && chrdev->ops->set_lnb_voltage)
ret = chrdev->ops->set_lnb_voltage(chrdev, (int)arg);
else
ret = -ENOSYS;
break;
case PTX_DISABLE_LNB_POWER:
if (chrdev->ops && chrdev->ops->set_lnb_voltage)
ret = chrdev->ops->set_lnb_voltage(chrdev, 0);
else
ret = -ENOSYS;
break;
case PTX_SET_SYSTEM_MODE:
{
enum ptx_system_type mode = (enum ptx_system_type)arg;
switch (mode) {
case PTX_UNSPECIFIED_SYSTEM:
case PTX_ISDB_T_SYSTEM:
case PTX_ISDB_S_SYSTEM:
if (chrdev->system_cap & mode)
chrdev->params.system = mode;
else
ret = -EINVAL;
break;
default:
ret = -EINVAL;
break;
}
break;
}
#if 0
case PTXT_GET_INFO:
break;
case PTXT_GET_PARAMS:
break;
case PTXT_SET_PARAMS:
break;
case PTXT_CLEAR_PARAMS:
break;
case PTXT_TUNE:
break;
case PTXT_SET_LNB_VOLTAGE:
break;
case PTXT_SET_CAPTURE:
break;
case PTXT_READ_STATS:
break;
#endif
default:
ret = -ENOSYS;
break;
}
mutex_unlock(&chrdev->lock);
return ret;
}
static struct file_operations ptx_chrdev_fops = {
.owner = THIS_MODULE,
.open = ptx_chrdev_open,
.read = ptx_chrdev_read,
.release = ptx_chrdev_release,
.unlocked_ioctl = ptx_chrdev_unlocked_ioctl
};
static bool __ptx_chrdev_search_context(unsigned int major,
struct ptx_chrdev_context **chrdev_ctx)
{
struct ptx_chrdev_context *ctx;
*chrdev_ctx = NULL;
list_for_each_entry(ctx, &ctx_list, list) {
if (MAJOR(ctx->dev_base) != major)
continue;
*chrdev_ctx = ctx;
break;
}
return (*chrdev_ctx) ? true : false;
}
int ptx_chrdev_context_create(const char *name, const char *devname,
unsigned int total_num,
struct ptx_chrdev_context **chrdev_ctx)
{
int ret = 0;
struct ptx_chrdev_context *ctx;
if (!name || !devname || !total_num || !chrdev_ctx)
return -EINVAL;
ctx = kzalloc(sizeof(*ctx) + (sizeof(*ctx->minor_table) * total_num),
GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mutex_init(&ctx->lock);
strlcpy(ctx->devname, devname, sizeof(ctx->devname));
INIT_LIST_HEAD(&ctx->group_list);
ctx->class = class_create(THIS_MODULE, name);
if (IS_ERR(ctx->class)) {
pr_err("ptx_chrdev_context_create: class_create(\"%s\") failed.\n",
name);
kfree(ctx);
return PTR_ERR(ctx->class);
}
ret = alloc_chrdev_region(&ctx->dev_base, 0, total_num, name);
if (ret < 0) {
pr_err("ptx_chrdev_context_create: alloc_chrdev_region(\"%s\") failed.\n",
name);
class_destroy(ctx->class);
kfree(ctx);
return ret;
}
kref_init(&ctx->kref);
ctx->last_id = 0;
ctx->minor_num = total_num;
ctx->minor_table = ((u8 *)ctx) + sizeof(*ctx);
mutex_lock(&ctx_list_lock);
list_add_tail(&ctx->list, &ctx_list);
mutex_unlock(&ctx_list_lock);
*chrdev_ctx = ctx;
return 0;
}
static void ptx_chrdev_context_release(struct kref *kref)
{
struct ptx_chrdev_context *ctx = container_of(kref,
struct ptx_chrdev_context,
kref);
pr_debug("ptx_chrdev_context_release\n");
unregister_chrdev_region(ctx->dev_base, ctx->minor_num);
class_destroy(ctx->class);
mutex_destroy(&ctx->lock);
kfree(ctx);
return;
}
void ptx_chrdev_context_destroy(struct ptx_chrdev_context *chrdev_ctx)
{
struct ptx_chrdev_group *group, *tmp_group;
mutex_lock(&ctx_list_lock);
list_del(&chrdev_ctx->list);
mutex_unlock(&ctx_list_lock);
mutex_lock(&chrdev_ctx->lock);
list_for_each_entry_safe(group, tmp_group,
&chrdev_ctx->group_list, list) {
ptx_chrdev_group_destroy(group);
}
mutex_unlock(&chrdev_ctx->lock);
kref_put(&chrdev_ctx->kref, ptx_chrdev_context_release);
return;
}
static bool __ptx_chrdev_context_search_group(struct ptx_chrdev_context *chrdev_ctx,
unsigned int minor,
struct ptx_chrdev_group **chrdev_group)
{
struct ptx_chrdev_group *group;
*chrdev_group = NULL;
list_for_each_entry(group, &chrdev_ctx->group_list, list) {
if (group->minor_base > minor ||
minor >= (group->minor_base + group->chrdev_num))
continue;
*chrdev_group = group;
break;
}
return (*chrdev_group) ? true : false;
}
static int __ptx_chrdev_context_search_minor(struct ptx_chrdev_context *chrdev_ctx,
unsigned int num, u8 state,
unsigned int *base)
{
unsigned int i, j;
if (num > chrdev_ctx->minor_num)
return -EINVAL;
for (i = 0; i < (chrdev_ctx->minor_num - num + 1); i++) {
if (chrdev_ctx->minor_table[i] != state)
continue;
for (j = 1; j < num; j++) {
if (chrdev_ctx->minor_table[i + j] != state)
break;
}
if (j < num) {
i += j;
continue;
}
// found
*base = i;
return 0;
}
return -EBUSY;
}
static int __ptx_chrdev_context_check_minor_status(struct ptx_chrdev_context *chrdev_ctx,
unsigned int base,
unsigned int num,
u8 state,bool *res)
{
unsigned int i;
if ((base + num) > chrdev_ctx->minor_num)
return -EINVAL;
*res = true;
for (i = 0; i < num; i++) {
if (chrdev_ctx->minor_table[base + i] != state) {
*res = false;
break;
}
}
return 0;
}
static int __ptx_chrdev_context_set_minor_status(struct ptx_chrdev_context *chrdev_ctx,
unsigned int base,
unsigned int num,
u8 state)
{
unsigned int i;
if ((base + num) > chrdev_ctx->minor_num)
return -EINVAL;
for (i = 0; i < num; i++)
chrdev_ctx->minor_table[base + i] = state;
return 0;
}
int ptx_chrdev_context_reserve(struct ptx_chrdev_context *chrdev_ctx,
unsigned int num, unsigned int *minor_base)
{
int ret = 0;
unsigned int base;
mutex_lock(&chrdev_ctx->lock);
ret = __ptx_chrdev_context_search_minor(chrdev_ctx, num,
PTX_CHRDEV_MINOR_FREE, &base);
if (ret)
goto exit;
__ptx_chrdev_context_set_minor_status(chrdev_ctx, base, num,
PTX_CHRDEV_MINOR_RESERVED);
*minor_base = MINOR(chrdev_ctx->dev_base) + base;
exit:
mutex_unlock(&chrdev_ctx->lock);
return ret;
}
int ptx_chrdev_context_add_group(struct ptx_chrdev_context *chrdev_ctx,
struct device *dev,
const struct ptx_chrdev_group_config *config,
struct ptx_chrdev_group **chrdev_group)
{
int ret = 0;
unsigned int i, num, base;
struct ptx_chrdev_group *group = NULL;
if (!chrdev_ctx || !dev || !config)
return -EINVAL;
num = config->chrdev_num;
kref_get(&chrdev_ctx->kref);
mutex_lock(&chrdev_ctx->lock);
if (config->reserved) {
bool res;
base = config->minor_base - MINOR(chrdev_ctx->dev_base);
ret = __ptx_chrdev_context_check_minor_status(chrdev_ctx,
base, num,
PTX_CHRDEV_MINOR_RESERVED,
&res);
if (!ret && !res)
ret = -EINVAL;
} else {
ret = __ptx_chrdev_context_search_minor(chrdev_ctx, num,
PTX_CHRDEV_MINOR_FREE,
&base);
if (ret)
dev_err(dev,
"ptx_chrdev_context_add: no enough minor number%s.\n",
(num == 1) ? "" : "s");
else
__ptx_chrdev_context_set_minor_status(chrdev_ctx,
base, num,
PTX_CHRDEV_MINOR_RESERVED);
}
if (ret)
goto fail;
__ptx_chrdev_context_set_minor_status(chrdev_ctx,
base, num,
PTX_CHRDEV_MINOR_IN_USE);
group = kzalloc(sizeof(*group) + (sizeof(group->chrdev[0]) * (num - 1)),
GFP_KERNEL);
if (!group) {
ret = -ENOMEM;
goto fail_group;
}
mutex_init(&group->lock);
atomic_set(&group->available, 1);
group->parent = chrdev_ctx;
group->dev = dev;
group->minor_base = MINOR(chrdev_ctx->dev_base) + base;
group->chrdev_num = 0;
for (i = 0; i < num; i++) {
struct ptx_chrdev *chrdev = &group->chrdev[i];
const struct ptx_chrdev_config *chrdev_config = &config->chrdev_config[i];
mutex_init(&chrdev->lock);
atomic_set(&chrdev->open, 0);
chrdev->system_cap = chrdev_config->system_cap;
chrdev->current_system = PTX_UNSPECIFIED_SYSTEM;
chrdev->ops = chrdev_config->ops;
chrdev->parent = group;
memset(&chrdev->params, 0, sizeof(chrdev->params));
chrdev->streaming = false;
init_waitqueue_head(&chrdev->ringbuf_wait);
chrdev->ringbuf_threshold_size = chrdev_config->ringbuf_size / 8;
chrdev->ringbuf_write_size = 0;
chrdev->priv = chrdev_config->priv;
ret = ringbuffer_create(&chrdev->ringbuf);
if (ret) {
mutex_destroy(&chrdev->lock);
dev_err(dev,
"ptx_chrdev_context_add: ringbuffer_create() failed. (ret: %d)\n",
ret);
break;
}
ret = ringbuffer_alloc(chrdev->ringbuf,
chrdev_config->ringbuf_size);
if (ret) {
ringbuffer_destroy(chrdev->ringbuf);
mutex_destroy(&chrdev->lock);
dev_err(dev,
"ptx_chrdev_context_add: ringbuffer_alloc(%zu) failed. (ret: %d)\n",
chrdev_config->ringbuf_size, ret);
break;
}
if (chrdev->ops->init) {
ret = chrdev->ops->init(chrdev);
if (ret) {
ringbuffer_destroy(chrdev->ringbuf);
mutex_destroy(&chrdev->lock);
dev_err(dev,
"ptx_chrdev_context_add: chrdev->ops->init(%u) failed. (ret: %d)\n",
i, ret);
break;
}
}
chrdev->id = i;
group->chrdev_num++;
}
if (ret)
goto fail_chrdev;
cdev_init(&group->cdev, &ptx_chrdev_fops);
group->cdev.owner = THIS_MODULE;
ret = cdev_add(&group->cdev,
MKDEV(MAJOR(chrdev_ctx->dev_base), group->minor_base),
num);
if (ret < 0) {
dev_err(dev,
"ptx_chrdev_context_add: cdev_add() failed. (ret: %d)\n",
ret);
goto fail_chrdev;
}
for (i = 0; i < num; i++) {
dev_info(dev, "/dev/%s%u\n", chrdev_ctx->devname, base + i);
device_create(chrdev_ctx->class, dev,
MKDEV(MAJOR(chrdev_ctx->dev_base),
group->minor_base + i),
NULL, "%s%u", chrdev_ctx->devname, base + i);
}
kref_init(&group->kref);
group->id = chrdev_ctx->last_id++;
list_add_tail(&group->list, &chrdev_ctx->group_list);
mutex_unlock(&chrdev_ctx->lock);
if (chrdev_group)
*chrdev_group = group;
return 0;
fail_chrdev:
if (group) {
for (i = 0; i < group->chrdev_num; i++) {
struct ptx_chrdev *chrdev = &group->chrdev[i];
if (chrdev->ops->term)
chrdev->ops->term(chrdev);
ringbuffer_destroy(chrdev->ringbuf);
mutex_destroy(&chrdev->lock);
}
mutex_destroy(&group->lock);
kfree(group);
}
fail_group:
__ptx_chrdev_context_set_minor_status(chrdev_ctx,
base, num,
(config->reserved) ? PTX_CHRDEV_MINOR_RESERVED : PTX_CHRDEV_MINOR_FREE);
fail:
mutex_unlock(&chrdev_ctx->lock);
kref_put(&chrdev_ctx->kref, ptx_chrdev_context_release);
return ret;
}
int ptx_chrdev_context_remove_group(struct ptx_chrdev_context *chrdev_ctx,
unsigned int minor_base)
{
int ret = 0;
struct ptx_chrdev_group *group;
mutex_lock(&chrdev_ctx->lock);
if (!__ptx_chrdev_context_search_group(chrdev_ctx,
minor_base, &group)) {
// not found
ret = -ENOENT;
goto exit;
}
ptx_chrdev_group_destroy(group);
exit:
mutex_unlock(&chrdev_ctx->lock);
return ret;
}
static void ptx_chrdev_group_release(struct kref *kref)
{
struct ptx_chrdev_group *group = container_of(kref,
struct ptx_chrdev_group,
kref);
struct ptx_chrdev_context *ctx = group->parent;
unsigned int i, minor_base, num;
dev_dbg(group->dev, "ptx_chrdev_group_release\n");
minor_base = group->minor_base;
num = group->chrdev_num;
for (i = 0; i < num; i++) {
struct ptx_chrdev *chrdev = &group->chrdev[i];
if (chrdev->ops->term)
chrdev->ops->term(chrdev);
ringbuffer_destroy(chrdev->ringbuf);
mutex_destroy(&chrdev->lock);
}
mutex_destroy(&group->lock);
kfree(group);
mutex_lock(&ctx->lock);
__ptx_chrdev_context_set_minor_status(ctx,
minor_base - MINOR(ctx->dev_base),
num,
PTX_CHRDEV_MINOR_FREE);
mutex_unlock(&ctx->lock);
kref_put(&ctx->kref, ptx_chrdev_context_release);
return;
}
void ptx_chrdev_group_destroy(struct ptx_chrdev_group *chrdev_group)
{
struct ptx_chrdev_context *ctx = chrdev_group->parent;
unsigned int i;
dev_dbg(chrdev_group->dev,
"ptx_chrdev_group_destroy: kref count: %u\n",
kref_read(&chrdev_group->kref));
mutex_lock(&ctx->lock);
list_del(&chrdev_group->list);
mutex_unlock(&ctx->lock);
mutex_lock(&chrdev_group->lock);
atomic_xchg(&chrdev_group->available, 0);
for (i = 0; i < chrdev_group->chrdev_num; i++) {
wake_up(&chrdev_group->chrdev[i].ringbuf_wait);
device_destroy(ctx->class,
MKDEV(MAJOR(ctx->dev_base),
chrdev_group->minor_base + i));
}
cdev_del(&chrdev_group->cdev);
mutex_unlock(&chrdev_group->lock);
kref_put(&chrdev_group->kref, ptx_chrdev_group_release);
return;
}
int ptx_chrdev_put_stream(struct ptx_chrdev *chrdev, void *buf, size_t len)
{
int ret = 0;
ret = ringbuffer_write_atomic(chrdev->ringbuf, buf, &len);
if (ret && ret != -EOVERFLOW)
return ret;
chrdev->ringbuf_write_size += len;
if (chrdev->ringbuf_write_size >= chrdev->ringbuf_threshold_size) {
wake_up(&chrdev->ringbuf_wait);
chrdev->ringbuf_write_size -= chrdev->ringbuf_threshold_size;
}
return 0;
}

124
driver/ptx_chrdev.h Normal file
View File

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Character device operator definitions for PTX devices (ptx_chrdev.h)
*
* Copyright (c) 2018-2020 nns779
*/
#ifndef __PTX_CHRDEV__
#define __PTX_CHRDEV__
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include "ptx_ioctl.h"
#include "ringbuffer.h"
struct ptx_tune_params {
enum ptx_system_type system;
u32 freq;
u32 bandwidth;
u16 stream_id;
};
struct ptx_chrdev;
struct ptx_chrdev_group;
struct ptx_chrdev_context;
struct ptx_chrdev_operations {
int (*init)(struct ptx_chrdev *chrdev);
int (*term)(struct ptx_chrdev *chrdev);
int (*open)(struct ptx_chrdev *chrdev);
int (*release)(struct ptx_chrdev *chrdev);
int (*tune)(struct ptx_chrdev *chrdev, struct ptx_tune_params *params);
int (*check_lock)(struct ptx_chrdev *chrdev, bool *locked);
int (*set_stream_id)(struct ptx_chrdev *chrdev, u16 stream_id);
int (*set_lnb_voltage)(struct ptx_chrdev *chrdev, int voltage);
int (*set_capture)(struct ptx_chrdev *chrdev, bool status);
int (*read_signal_strength)(struct ptx_chrdev *chrdev, u32 *value);
int (*read_cnr)(struct ptx_chrdev *chrdev, u32 *value);
int (*read_cnr_raw)(struct ptx_chrdev *chrdev, u32 *value);
};
struct ptx_chrdev_config {
enum ptx_system_type system_cap;
const struct ptx_chrdev_operations *ops;
size_t ringbuf_size;
void *priv;
};
struct ptx_chrdev_group_config {
bool reserved;
unsigned int minor_base;
unsigned int chrdev_num;
struct ptx_chrdev_config *chrdev_config;
};
struct ptx_chrdev {
struct mutex lock;
unsigned int id;
atomic_t open;
char name[64];
enum ptx_system_type system_cap;
enum ptx_system_type current_system;
const struct ptx_chrdev_operations *ops;
struct ptx_chrdev_group *parent;
struct ptx_tune_params params;
bool streaming;
struct ringbuffer *ringbuf;
wait_queue_head_t ringbuf_wait;
size_t ringbuf_threshold_size;
size_t ringbuf_write_size;
void *priv;
};
struct ptx_chrdev_group {
struct list_head list;
struct mutex lock;
struct kref kref;
unsigned int id;
atomic_t available;
struct ptx_chrdev_context *parent;
struct device *dev;
struct cdev cdev;
unsigned int minor_base;
unsigned int chrdev_num;
struct ptx_chrdev chrdev[1];
};
#define PTX_CHRDEV_MINOR_FREE 0
#define PTX_CHRDEV_MINOR_RESERVED 1
#define PTX_CHRDEV_MINOR_IN_USE 2
struct ptx_chrdev_context {
struct list_head list;
struct mutex lock;
struct kref kref;
char devname[64];
struct class *class;
dev_t dev_base;
unsigned int last_id;
unsigned int minor_num;
u8 *minor_table;
struct list_head group_list;
};
int ptx_chrdev_context_create(const char *name, const char *devname,
unsigned int total_num,
struct ptx_chrdev_context **chrdev_ctx);
void ptx_chrdev_context_destroy(struct ptx_chrdev_context *chrdev_ctx);
int ptx_chrdev_context_add_group(struct ptx_chrdev_context *chrdev_ctx,
struct device *dev,
const struct ptx_chrdev_group_config *config,
struct ptx_chrdev_group **chrdev_group);
int ptx_chrdev_context_remove_group(struct ptx_chrdev_context *chrdev_ctx,
unsigned int minor_base);
void ptx_chrdev_group_destroy(struct ptx_chrdev_group *chrdev_group);
int ptx_chrdev_put_stream(struct ptx_chrdev *chrdev, void *buf, size_t len);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
// px4.h
#ifndef __PX4_H__
#define __PX4_H__
int px4_register(void);
void px4_unregister(void);
#endif

1377
driver/px4_device.c Normal file

File diff suppressed because it is too large Load Diff

65
driver/px4_device.h Normal file
View File

@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PTX driver definitions for PLEX PX4/PX5 series devices (px4_device.h)
*
* Copyright (c) 2018-2020 nns779
*/
#ifndef __PX4_DEVICE_H__
#define __PX4_DEVICE_H__
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include "px4_mldev.h"
#include "ptx_chrdev.h"
#include "it930x.h"
#include "tc90522.h"
#include "r850.h"
#include "rt710.h"
#define PX4_CHRDEV_NUM 4
struct px4_device;
struct px4_chrdev {
struct ptx_chrdev *chrdev;
struct px4_device *parent;
bool lnb_power;
struct tc90522_demod tc90522;
union {
struct r850_tuner r850;
struct rt710_tuner rt710;
} tuner;
};
struct px4_serial_number {
unsigned long long serial_number;
unsigned int dev_id;
};
struct px4_device {
struct mutex lock;
struct kref kref;
atomic_t available;
struct device *dev;
struct px4_serial_number serial;
struct px4_mldev *mldev;
unsigned int open_count;
unsigned int lnb_power_count;
unsigned int streaming_count;
struct ptx_chrdev_group *chrdev_group;
struct px4_chrdev chrdev4[PX4_CHRDEV_NUM];
struct it930x_bridge it930x;
void *stream_ctx;
};
int px4_device_init(struct px4_device *px4, struct device *dev,
const char *dev_serial, bool use_mldev,
struct ptx_chrdev_context *chrdev_ctx);
void px4_device_term(struct px4_device *px4);
#endif

View File

@@ -0,0 +1,33 @@
// SPDX-Licence-Identifier: GPL-2.0-only
/*
* Module parameter definitions (px4_device_params.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "px4_device_params.h"
#include <linux/kernel.h>
#include <linux/module.h>
struct px4_device_param_set px4_device_params = {
.tsdev_max_packets = 2048,
.psb_purge_timeout = 2000,
.disable_multi_device_power_control = false,
.s_tuner_no_sleep = false
};
module_param_named(tsdev_max_packets, px4_device_params.tsdev_max_packets,
uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(tsdev_max_packets,
"Maximum number of TS packets buffering in tsdev. (default: 2048)");
module_param_named(psb_purge_timeout, px4_device_params.psb_purge_timeout,
int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param_named(disable_multi_device_power_control,
px4_device_params.disable_multi_device_power_control,
bool, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param_named(s_tuner_no_sleep, px4_device_params.s_tuner_no_sleep,
bool, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

View File

@@ -0,0 +1,17 @@
// px4_device_params.h
#ifndef __PX4_DEVICE_PARAMS_H__
#define __PX4_DEVICE_PARAMS_H__
#include <linux/types.h>
struct px4_device_param_set {
unsigned int tsdev_max_packets;
int psb_purge_timeout;
bool disable_multi_device_power_control;
bool s_tuner_no_sleep;
};
extern struct px4_device_param_set px4_device_params;
#endif

210
driver/px4_mldev.c Normal file
View File

@@ -0,0 +1,210 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PX4 multi-device power manager (px4_mldev.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "print_format.h"
#include "px4_mldev.h"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
static LIST_HEAD(px4_mldev_list);
static DEFINE_MUTEX(px4_mldev_glock);
static unsigned int __px4_mldev_get_power_count(struct px4_mldev *mldev);
bool px4_mldev_search(unsigned long long serial_number,
struct px4_mldev **mldev)
{
struct px4_mldev *m;
*mldev = NULL;
mutex_lock(&px4_mldev_glock);
list_for_each_entry(m, &px4_mldev_list, list) {
if (m->serial_number == serial_number) {
*mldev = m;
break;
}
}
mutex_unlock(&px4_mldev_glock);
return (*mldev) ? true : false;
}
int px4_mldev_alloc(struct px4_mldev **mldev, struct px4_device *px4,
int (*backend_set_power)(struct px4_device *, bool))
{
unsigned int dev_id = px4->serial.dev_id - 1;
struct px4_mldev *m;
dev_dbg(px4->dev,
"px4_mldev_alloc: serial_number: %014llu, dev_id: %u\n",
px4->serial.serial_number, px4->serial.dev_id);
if (dev_id > 1)
return -EINVAL;
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
return -ENOMEM;
kref_init(&m->kref);
mutex_init(&m->lock);
m->serial_number = px4->serial.serial_number;
m->dev[px4->serial.dev_id - 1] = px4;
m->backend_set_power = backend_set_power;
mutex_lock(&px4_mldev_glock);
list_add_tail(&m->list, &px4_mldev_list);
mutex_unlock(&px4_mldev_glock);
*mldev = m;
return 0;
}
static void px4_mldev_release(struct kref *kref)
{
struct px4_mldev *mldev = container_of(kref, struct px4_mldev, kref);
pr_debug("px4_mldev_release: serial_number: %014llu\n",
mldev->serial_number);
mutex_lock(&px4_mldev_glock);
list_del(&mldev->list);
mutex_unlock(&px4_mldev_glock);
mutex_destroy(&mldev->lock);
kfree(mldev);
return;
}
int px4_mldev_add(struct px4_mldev *mldev, struct px4_device *px4)
{
int ret = 0;
unsigned int dev_id = px4->serial.dev_id - 1;
dev_dbg(px4->dev,
"px4_mldev_add: serial_number: %014llu, dev_id: %u\n",
mldev->serial_number, dev_id + 1);
if (dev_id > 1)
return -EINVAL;
mutex_lock(&mldev->lock);
if (kref_read(&mldev->kref) >= 2) {
ret = -EINVAL;
goto exit;
}
if (mldev->dev[dev_id]) {
ret = -EALREADY;
goto exit;
}
if (__px4_mldev_get_power_count(mldev))
ret = mldev->backend_set_power(px4, true);
if (ret)
goto exit;
kref_get(&mldev->kref);
mldev->dev[dev_id] = px4;
exit:
mutex_unlock(&mldev->lock);
return ret;
}
int px4_mldev_remove(struct px4_mldev *mldev, struct px4_device *px4)
{
unsigned int dev_id = px4->serial.dev_id - 1;
dev_dbg(px4->dev,
"px4_mldev_remove: serial_number: %014llu, dev_id: %u\n",
mldev->serial_number, dev_id + 1);
if (dev_id > 1)
return -EINVAL;
mutex_lock(&mldev->lock);
if (mldev->dev[dev_id] != px4) {
mutex_unlock(&mldev->lock);
return -EINVAL;
}
if (__px4_mldev_get_power_count(mldev) && mldev->power_state[dev_id]) {
mldev->backend_set_power(px4, false);
mldev->power_state[dev_id] = false;
}
mldev->dev[dev_id] = NULL;
if (kref_put(&mldev->kref, px4_mldev_release))
return 0;
mutex_unlock(&mldev->lock);
return 0;
}
static unsigned int __px4_mldev_get_power_count(struct px4_mldev *mldev)
{
return (!!mldev->power_state[0]) + (!!mldev->power_state[1]);
}
int px4_mldev_set_power(struct px4_mldev *mldev, struct px4_device *px4,
bool state)
{
int ret = 0, i;
unsigned int dev_id = px4->serial.dev_id - 1;
dev_dbg(px4->dev,
"px4_mldev_set_power: serial_number: %014llu, dev_id: %u, state: %s\n",
mldev->serial_number, dev_id + 1, (state) ? "true" : "false");
dev_dbg(px4->dev,
"px4_mldev_set_power: power_state: %s, %s\n",
(mldev->power_state[0]) ? "true" : "false",
(mldev->power_state[1]) ? "true" : "false");
if (dev_id > 1)
return -EINVAL;
mutex_lock(&mldev->lock);
if (!mldev->dev[dev_id]) {
ret = -EINVAL;
goto exit;
}
if (mldev->power_state[dev_id] == state)
goto exit;
if (!state)
mldev->power_state[dev_id] = false;
if (!__px4_mldev_get_power_count(mldev)) {
for (i = 0; i < 2; i++) {
if (!mldev->dev[i])
continue;
ret = mldev->backend_set_power(mldev->dev[i], state);
if (ret)
break;
}
}
if (state && !ret)
mldev->power_state[dev_id] = true;
exit:
mutex_unlock(&mldev->lock);
return ret;
}

37
driver/px4_mldev.h Normal file
View File

@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PX4 multi-device power manager definitions (px4_mldev.h)
*
* Copyright (c) 2018-2020 nns779
*/
#ifndef __PX4_MLDEV_H__
#define __PX4_MLDEV_H__
#include <linux/types.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include "px4_device.h"
struct px4_mldev {
struct kref kref;
struct list_head list;
struct mutex lock;
unsigned long long serial_number;
struct px4_device *dev[2];
bool power_state[2];
int (*backend_set_power)(struct px4_device *px4, bool state);
};
bool px4_mldev_search(unsigned long long serial_number,
struct px4_mldev **mldev);
int px4_mldev_alloc(struct px4_mldev **mldev, struct px4_device *px4,
int (*backend_set_power)(struct px4_device *, bool));
int px4_mldev_add(struct px4_mldev *mldev, struct px4_device *px4);
int px4_mldev_remove(struct px4_mldev *mldev, struct px4_device *px4);
int px4_mldev_set_power(struct px4_mldev *mldev, struct px4_device *px4,
bool state);
#endif

256
driver/px4_usb.c Normal file
View File

@@ -0,0 +1,256 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PTX driver for USB devices (px4_usb.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "print_format.h"
#include "px4_usb.h"
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/usb.h>
#include "px4_usb_params.h"
#include "px4_device_params.h"
#include "ptx_chrdev.h"
#include "px4_device.h"
#include "isdb2056_device.h"
#define PX4_USB_MAX_DEVICE 16
#define PX4_USB_MAX_CHRDEV (PX4_USB_MAX_DEVICE * PX4_CHRDEV_NUM)
#define ISDB2056_USB_MAX_DEVICE 64
#define ISDB2056_USB_MAX_CHRDEV (ISDB2056_USB_MAX_DEVICE * ISDB2056_CHRDEV_NUM)
struct px4_usb_context {
struct list_head list;
enum px4_usb_device_type type;
union {
struct px4_device px4;
struct isdb2056_device isdb2056;
} ctx;
};
static DEFINE_MUTEX(px4_usb_glock);
static LIST_HEAD(px4_usb_ctx_list);
static struct ptx_chrdev_context *px4_usb_chrdev_ctx[7];
static int px4_usb_init_bridge(struct device *dev, struct usb_device *usb_dev,
struct it930x_bridge *it930x)
{
struct itedtv_bus *bus = &it930x->bus;
bus->dev = dev;
bus->type = ITEDTV_BUS_USB;
bus->usb.dev = usb_dev;
bus->usb.ctrl_timeout = 3000;
bus->usb.streaming.urb_buffer_size = 188 * px4_usb_params.urb_max_packets;
bus->usb.streaming.urb_num = px4_usb_params.max_urbs;
bus->usb.streaming.no_dma = px4_usb_params.no_dma;
it930x->dev = dev;
it930x->config.xfer_size = 188 * px4_usb_params.xfer_packets;
it930x->config.i2c_speed = 0x07;
return 0;
}
static int px4_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
int ret = 0;
struct device *dev;
struct usb_device *usb_dev;
struct px4_usb_context *ctx;
dev = &intf->dev;
usb_dev = interface_to_usbdev(intf);
if (usb_dev->speed < USB_SPEED_HIGH)
dev_warn(dev, "This device is operating as USB 1.1.\n");
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
dev_err(dev, "px4_usb_probe: kzalloc(sizeof(*ctx)) failed.\n");
return -ENOMEM;
}
switch (id->idVendor) {
case 0x0511:
{
bool px4_use_mldev = false;
switch (id->idProduct) {
case USB_PID_PX_Q3U4:
case USB_PID_PX_Q3PE4:
if (!px4_device_params.disable_multi_device_power_control)
px4_use_mldev = true;
/* fall through */
case USB_PID_PX_W3U4:
case USB_PID_PX_W3PE4:
ret = px4_usb_init_bridge(dev, usb_dev,
&ctx->ctx.px4.it930x);
if (ret)
break;
ctx->type = PX4_USB_DEVICE;
ret = px4_device_init(&ctx->ctx.px4, dev,
usb_dev->serial, px4_use_mldev,
px4_usb_chrdev_ctx[PX4_USB_DEVICE]);
break;
case USB_PID_DIGIBEST_ISDB2056:
ret = px4_usb_init_bridge(dev, usb_dev,
&ctx->ctx.isdb2056.it930x);
if (ret)
break;
ctx->type = ISDB2056_USB_DEVICE;
ret = isdb2056_device_init(&ctx->ctx.isdb2056, dev,
px4_usb_chrdev_ctx[ISDB2056_USB_DEVICE]);
break;
default:
ret = -EINVAL;
break;
}
break;
}
default:
ret = -EINVAL;
break;
}
if (ret)
goto fail;
mutex_lock(&px4_usb_glock);
list_add_tail(&ctx->list, &px4_usb_ctx_list);
mutex_unlock(&px4_usb_glock);
get_device(dev);
usb_set_intfdata(intf, ctx);
return 0;
fail:
if (ctx)
kfree(ctx);
return ret;
}
static void px4_usb_disconnect(struct usb_interface *intf)
{
struct px4_usb_context *ctx;
ctx = usb_get_intfdata(intf);
if (!ctx) {
pr_err("px4_usb_disconnect: ctx is NULL.\n");
return;
}
usb_set_intfdata(intf, NULL);
mutex_lock(&px4_usb_glock);
list_del(&ctx->list);
mutex_unlock(&px4_usb_glock);
switch (ctx->type) {
case PX4_USB_DEVICE:
px4_device_term(&ctx->ctx.px4);
break;
case ISDB2056_USB_DEVICE:
isdb2056_device_term(&ctx->ctx.isdb2056);
break;
default:
// unknown device
break;
}
put_device(&intf->dev);
kfree(ctx);
return;
}
static int px4_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
return -ENOSYS;
}
static int px4_usb_resume(struct usb_interface *intf)
{
return 0;
}
static const struct usb_device_id px4_usb_ids[] = {
{ USB_DEVICE(0x0511, USB_PID_PX_W3U4) },
{ USB_DEVICE(0x0511, USB_PID_PX_Q3U4) },
{ USB_DEVICE(0x0511, USB_PID_PX_W3PE4) },
{ USB_DEVICE(0x0511, USB_PID_PX_Q3PE4) },
{ USB_DEVICE(0x0511, USB_PID_DIGIBEST_ISDB2056) },
{ 0 }
};
MODULE_DEVICE_TABLE(usb, px4_usb_ids);
static struct usb_driver px4_usb_driver = {
.name = "px4_usb",
.probe = px4_usb_probe,
.disconnect = px4_usb_disconnect,
.suspend = px4_usb_suspend,
.resume = px4_usb_resume,
.id_table = px4_usb_ids
};
int px4_usb_register()
{
int ret = 0;
memset(&px4_usb_chrdev_ctx, 0, sizeof(px4_usb_chrdev_ctx));
ret = ptx_chrdev_context_create("px4", "px4video",
PX4_USB_MAX_CHRDEV,
&px4_usb_chrdev_ctx[PX4_USB_DEVICE]);
if (ret) {
pr_err("px4_usb_register: ptx_chrdev_context_create() failed.\n");
return ret;
}
ret = ptx_chrdev_context_create("isdb2056", "isdb2056video",
ISDB2056_USB_MAX_CHRDEV,
&px4_usb_chrdev_ctx[ISDB2056_USB_DEVICE]);
if (ret) {
pr_err("px4_usb_register: ptx_chrdev_context_create() failed.\n");
ptx_chrdev_context_destroy(px4_usb_chrdev_ctx[PX4_USB_DEVICE]);
return ret;
}
ret = usb_register(&px4_usb_driver);
if (ret) {
pr_err("px4_usb_register: usb_register() failed.\n");
ptx_chrdev_context_destroy(px4_usb_chrdev_ctx[PX4_USB_DEVICE]);
ptx_chrdev_context_destroy(px4_usb_chrdev_ctx[ISDB2056_USB_DEVICE]);
return ret;
}
return 0;
}
void px4_usb_unregister()
{
usb_deregister(&px4_usb_driver);
ptx_chrdev_context_destroy(px4_usb_chrdev_ctx[PX4_USB_DEVICE]);
ptx_chrdev_context_destroy(px4_usb_chrdev_ctx[ISDB2056_USB_DEVICE]);
}

38
driver/px4_usb.h Normal file
View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* PTX driver definitions for USB devices (px4_usb.h)
*
* Copyright (c) 2018-2020 nns779
*/
#ifndef __PX4_USB_H__
#define __PX4_USB_H__
#define USB_PID_PX_W3U4 0x083f
#define USB_PID_PX_Q3U4 0x084a
#define USB_PID_PX_W3PE4 0x023f
#define USB_PID_PX_Q3PE4 0x024a
#define USB_PID_DIGIBEST_ISDB2056 0x004b
// Not supported devices
#define USB_PID_PX_MLT5PE 0x024e
#define USB_PID_PX_MLT8PE3 0x0252
#define USB_PID_PX_MLT8PE5 0x0253
#define USB_PID_DIGIBEST_ISDBT2053 0x0046
#define USB_PID_DIGIBEST_ISDBT2057 0x004c
#define USB_PID_DIGIBEST_ISDBT6013 0x024d
enum px4_usb_device_type {
UNKNOWN_USB_DEVICE = 0,
PX4_USB_DEVICE,
PXMLT_USB_DEVICE, // not supported
ISDBT2053_USB_DEVICE, // not supported
ISDB2056_USB_DEVICE,
ISDBT2057_USB_DEVICE, // not supported
ISDBT6013_USB_DEVICE // not supported
};
int px4_usb_register(void);
void px4_usb_unregister(void);
#endif

35
driver/px4_usb_params.c Normal file
View File

@@ -0,0 +1,35 @@
// SPDX-Licence-Identifier: GPL-2.0-only
/*
* Module parameter definitions (px4_usb_params.c)
*
* Copyright (c) 2018-2020 nns779
*/
#include "px4_usb_params.h"
#include <linux/kernel.h>
#include <linux/module.h>
struct px4_usb_param_set px4_usb_params = {
.xfer_packets = 816,
.urb_max_packets = 816,
.max_urbs = 6,
.no_dma = false
};
module_param_named(xfer_packets, px4_usb_params.xfer_packets,
uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(xfer_packets,
"Number of transfer packets from the device. (default: 816)");
module_param_named(urb_max_packets, px4_usb_params.urb_max_packets,
uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(urb_max_packets,
"Maximum number of TS packets per URB. (default: 816)");
module_param_named(max_urbs, px4_usb_params.max_urbs,
uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(max_urbs, "Maximum number of URBs. (default: 6)");
module_param_named(no_dma, px4_usb_params.no_dma,
bool, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

17
driver/px4_usb_params.h Normal file
View File

@@ -0,0 +1,17 @@
// px4_usb_params.h
#ifndef __PX4_USB_PARAMS_H__
#define __PX4_USB_PARAMS_H__
#include <linux/types.h>
struct px4_usb_param_set {
unsigned int xfer_packets;
unsigned int urb_max_packets;
unsigned int max_urbs;
bool no_dma;
};
extern struct px4_usb_param_set px4_usb_params;
#endif

View File

@@ -1,365 +1,263 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Ringbuffer functions (ringbuffer.c) * Ringbuffer implementation (ringbuffer.c)
* *
* Copyright (c) 2018-2019 nns779 * Copyright (c) 2018-2020 nns779
*/ */
#include "print_format.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "ringbuffer.h" #include "ringbuffer.h"
static int _ringbuffer_init(struct ringbuffer *ringbuffer) #include <linux/slab.h>
{ #include <linux/sched.h>
#ifdef RINGBUFFER_USE_SPINLOCK #include <linux/uaccess.h>
spin_lock_init(&ringbuffer->lock);
#endif
atomic_set(&ringbuffer->avail, 0);
atomic_set(&ringbuffer->rw_cnt, 0);
atomic_set(&ringbuffer->wait_cnt, 0);
init_waitqueue_head(&ringbuffer->wait);
init_waitqueue_head(&ringbuffer->data_wait);
ringbuffer->buf = NULL;
ringbuffer->buf_size = 0;
#ifdef RINGBUFFER_USE_SPINLOCK
ringbuffer->data_size = 0;
#else
atomic_set(&ringbuffer->data_size, 0);
#endif
ringbuffer->tail_pos = 0;
ringbuffer->head_pos = 0;
ringbuffer->write_size = 0;
ringbuffer->write_threshold_size = 0;
return 0; static void __ringbuffer_free(struct ringbuffer *ringbuf);
} static void ringbuffer_lock(struct ringbuffer *ringbuf);
int ringbuffer_create(struct ringbuffer **ringbuffer) int ringbuffer_create(struct ringbuffer **ringbuf)
{ {
int ret = 0;
struct ringbuffer *p; struct ringbuffer *p;
*ringbuffer = NULL; p = kzalloc(sizeof(*p), GFP_KERNEL);
p = kzalloc(sizeof(struct ringbuffer), GFP_KERNEL);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
ret = _ringbuffer_init(p); atomic_set(&p->state, 0);
if (!ret) atomic_set(&p->rw_count, 0);
*ringbuffer = p; atomic_set(&p->wait_count, 0);
else init_waitqueue_head(&p->wait);
kfree(p); p->buf = NULL;
p->size = 0;
atomic_long_set(&p->actual_size, 0);
atomic_long_set(&p->head, 0);
atomic_long_set(&p->tail, 0);
return ret; *ringbuf = p;
return 0;
} }
int ringbuffer_destroy(struct ringbuffer *ringbuffer) int ringbuffer_destroy(struct ringbuffer *ringbuf)
{
ringbuffer_stop(ringbuf);
ringbuffer_lock(ringbuf);
__ringbuffer_free(ringbuf);
kfree(ringbuf);
return 0;
}
static void __ringbuffer_free(struct ringbuffer *ringbuf)
{
if (ringbuf->buf)
free_pages((unsigned long)ringbuf->buf,
get_order(ringbuf->size));
ringbuf->buf = NULL;
ringbuf->size = 0;
return;
}
static void __ringbuffer_reset(struct ringbuffer *ringbuf)
{
atomic_long_set(&ringbuf->actual_size, 0);
atomic_long_set(&ringbuf->head, 0);
atomic_long_set(&ringbuf->tail, 0);
return;
}
static void ringbuffer_lock(struct ringbuffer *ringbuf)
{
atomic_add_return(1, &ringbuf->wait_count);
wait_event(ringbuf->wait, !atomic_read(&ringbuf->rw_count));
return;
}
static void ringbuffer_unlock(struct ringbuffer *ringbuf)
{
if (atomic_sub_return(1, &ringbuf->wait_count))
wake_up(&ringbuf->wait);
return;
}
int ringbuffer_alloc(struct ringbuffer *ringbuf, size_t size)
{ {
int ret = 0; int ret = 0;
ret = ringbuffer_free(ringbuffer); if (atomic_read_acquire(&ringbuf->state))
if (!ret) return -EBUSY;
kfree(ringbuffer);
return ret; ringbuffer_lock(ringbuf);
}
static void _ringbuffer_free(struct ringbuffer *ringbuffer) if (ringbuf->buf && ringbuf->size != size)
{ __ringbuffer_free(ringbuf);
free_pages((unsigned long)ringbuffer->buf,
get_order(ringbuffer->buf_size));
ringbuffer->buf = NULL; ringbuf->size = 0;
ringbuffer->buf_size = 0; __ringbuffer_reset(ringbuf);
#ifdef RINGBUFFER_USE_SPINLOCK
ringbuffer->data_size = 0;
#else
atomic_set(&ringbuffer->data_size, 0);
#endif
ringbuffer->tail_pos = 0;
ringbuffer->head_pos = 0;
ringbuffer->write_size = 0;
ringbuffer->write_threshold_size = 0;
}
int ringbuffer_alloc(struct ringbuffer *ringbuffer, size_t size) if (!ringbuf->buf) {
{ ringbuf->buf = (u8 *)__get_free_pages(GFP_KERNEL,
int ret = 0; get_order(size));
if (!ringbuf->buf)
// Acquire lock
if (atomic_add_return(1, &ringbuffer->wait_cnt) != 1) {
// Someone is waiting
ret = -EAGAIN;
goto exit;
}
atomic_set(&ringbuffer->avail, 0);
wake_up(&ringbuffer->data_wait);
wait_event(ringbuffer->wait, !atomic_read(&ringbuffer->rw_cnt));
if (ringbuffer->buf) {
if (ringbuffer->buf_size == size)
goto reset;
_ringbuffer_free(ringbuffer);
}
// Allocate
ringbuffer->buf = (u8 *)__get_free_pages(GFP_KERNEL, get_order(size));
if (!ringbuffer->buf) {
ret = -ENOMEM; ret = -ENOMEM;
goto exit; else
ringbuf->size = size;
} }
ringbuffer->buf_size = size; ringbuffer_unlock(ringbuf);
ringbuffer->write_threshold_size = (size / 10);
reset:
#ifdef RINGBUFFER_USE_SPINLOCK
ringbuffer->data_size = 0;
#else
atomic_set(&ringbuffer->data_size, 0);
#endif
ringbuffer->tail_pos = 0;
ringbuffer->head_pos = 0;
ringbuffer->write_size = 0;
exit:
// Release lock
atomic_sub(1, &ringbuffer->wait_cnt);
return ret; return ret;
} }
int ringbuffer_free(struct ringbuffer *ringbuffer) int ringbuffer_free(struct ringbuffer *ringbuf)
{ {
int ret = 0; if (atomic_read_acquire(&ringbuf->state))
return -EBUSY;
// Acquire lock ringbuffer_lock(ringbuf);
if (atomic_add_return(1, &ringbuffer->wait_cnt) != 1) { __ringbuffer_reset(ringbuf);
// Someone is waiting __ringbuffer_free(ringbuf);
ret = -EAGAIN; ringbuffer_unlock(ringbuf);
goto exit;
}
atomic_set(&ringbuffer->avail, 0);
wake_up(&ringbuffer->data_wait);
wait_event(ringbuffer->wait, !atomic_read(&ringbuffer->rw_cnt));
if (!ringbuffer->buf)
goto exit;
_ringbuffer_free(ringbuffer);
exit:
// Release lock
atomic_sub(1, &ringbuffer->wait_cnt);
return ret;
}
int ringbuffer_start(struct ringbuffer *ringbuffer)
{
int ret = 0;
if (atomic_read(&ringbuffer->avail))
return 0;
// Acquire lock for read buffer pointer
if (atomic_add_return(1, &ringbuffer->wait_cnt) != 1) {
// Someone is waiting
ret = -EAGAIN;
goto exit;
}
if (ringbuffer->buf && ringbuffer->buf_size)
atomic_set(&ringbuffer->avail, 1);
exit:
// Release lock
atomic_sub(1, &ringbuffer->wait_cnt);
return ret;
}
int ringbuffer_stop(struct ringbuffer *ringbuffer)
{
atomic_set(&ringbuffer->avail, 0);
wake_up(&ringbuffer->data_wait);
return 0; return 0;
} }
int ringbuffer_write_atomic(struct ringbuffer *ringbuffer, int ringbuffer_reset(struct ringbuffer *ringbuf)
const void *data, size_t len)
{ {
#ifdef RINGBUFFER_USE_SPINLOCK if (atomic_read_acquire(&ringbuf->state))
unsigned long flags; return -EBUSY;
#endif
const u8 *p = data;
size_t buf_size, data_size, tail_pos, write_size;
if (atomic_read(&ringbuffer->avail) != 2) ringbuffer_lock(ringbuf);
return -EIO; __ringbuffer_reset(ringbuf);
ringbuffer_unlock(ringbuf);
atomic_add(1, &ringbuffer->rw_cnt); return 0;
if (atomic_read(&ringbuffer->wait_cnt)) {
atomic_sub(1, &ringbuffer->rw_cnt);
wake_up(&ringbuffer->wait);
return -EIO;
}
buf_size = ringbuffer->buf_size;
tail_pos = ringbuffer->tail_pos;
#ifdef RINGBUFFER_USE_SPINLOCK
spin_lock_irqsave(&ringbuffer->lock, flags);
data_size = ringbuffer->data_size;
spin_unlock_irqrestore(&ringbuffer->lock, flags);
#else
data_size = atomic_read(&ringbuffer->data_size);
#endif
write_size = (data_size + len <= buf_size) ? (len) : (buf_size - data_size);
if (write_size) {
size_t t = (tail_pos + write_size <= buf_size) ? (write_size) : (buf_size - tail_pos);
memcpy(ringbuffer->buf + tail_pos, p, t);
if (t < write_size) {
memcpy(ringbuffer->buf, p + t, write_size - t);
tail_pos = write_size - t;
} else {
tail_pos = (tail_pos + write_size == buf_size) ? 0 : (tail_pos + write_size);
}
ringbuffer->tail_pos = tail_pos;
#ifdef RINGBUFFER_USE_SPINLOCK
spin_lock_irqsave(&ringbuffer->lock, flags);
ringbuffer->data_size += write_size;
spin_unlock_irqrestore(&ringbuffer->lock, flags);
#else
atomic_add(write_size, &ringbuffer->data_size);
#endif
if (ringbuffer->write_size >= ringbuffer->write_threshold_size) {
if (atomic_read(&ringbuffer->rw_cnt) > 1) {
wake_up(&ringbuffer->data_wait);
ringbuffer->write_size = 0;
}
} else {
ringbuffer->write_size += write_size;
}
}
if (!atomic_sub_return(1, &ringbuffer->rw_cnt) &&
atomic_read(&ringbuffer->wait_cnt))
wake_up(&ringbuffer->wait);
return (write_size != len) ? (-ECANCELED) : (0);
} }
int ringbuffer_read_user(struct ringbuffer *ringbuffer, int ringbuffer_start(struct ringbuffer *ringbuf)
{
if (atomic_cmpxchg(&ringbuf->state, 0, 1))
return -EALREADY;
return 0;
}
int ringbuffer_stop(struct ringbuffer *ringbuf)
{
if (!atomic_xchg(&ringbuf->state, 0))
return -EALREADY;
return 0;
}
int ringbuffer_ready_read(struct ringbuffer *ringbuf)
{
if (!atomic_cmpxchg(&ringbuf->state, 1, 2))
return -EINVAL;
return 0;
}
int ringbuffer_read_user(struct ringbuffer *ringbuf,
void __user *buf, size_t *len) void __user *buf, size_t *len)
{ {
int ret = 0; u8 *p;
u8 *p = buf; size_t buf_size, actual_size, head, read_size;
size_t buf_size, l = *len, buf_pos = 0;
atomic_cmpxchg(&ringbuffer->avail, 1, 2); atomic_add_return_acquire(1, &ringbuf->rw_count);
atomic_add(1, &ringbuffer->rw_cnt); p = ringbuf->buf;
buf_size = ringbuf->size;
actual_size = atomic_long_read_acquire(&ringbuf->actual_size);
head = atomic_long_read(&ringbuf->head);
if (atomic_read(&ringbuffer->wait_cnt)) { read_size = (*len <= actual_size) ? *len : actual_size;
atomic_sub(1, &ringbuffer->rw_cnt); if (read_size) {
wake_up(&ringbuffer->wait); size_t tmp = (head + read_size <= buf_size) ? read_size : (buf_size - head);
return -EIO; unsigned long res;
}
buf_size = ringbuffer->buf_size; res = copy_to_user(buf, p + head, tmp);
while (l > buf_pos) { if (tmp < read_size) {
#ifdef RINGBUFFER_USE_SPINLOCK res = copy_to_user(((u8 *)buf) + tmp, p,
unsigned long flags; read_size - tmp);
#endif head = read_size - tmp;
size_t data_size, head_pos, read_size, t;
unsigned long r;
if (wait_event_interruptible(ringbuffer->data_wait, (
#ifdef RINGBUFFER_USE_SPINLOCK
(ringbuffer->data_size)
#else
atomic_read(&ringbuffer->data_size)
#endif
|| !atomic_read(&ringbuffer->avail)))
) {
if (!buf_pos)
ret = -EINTR;
break;
}
#ifdef RINGBUFFER_USE_SPINLOCK
spin_lock_irqsave(&ringbuffer->lock, flags);
data_size = ringbuffer->data_size;
spin_unlock_irqrestore(&ringbuffer->lock, flags);
#else
data_size = atomic_read(&ringbuffer->data_size);
#endif
if (!data_size) {
if (!buf_pos)
ret = -EIO;
break;
}
head_pos = ringbuffer->head_pos;
read_size = (l - buf_pos > data_size) ? (data_size) : (l - buf_pos);
t = (head_pos + read_size <= buf_size) ? (read_size) : (buf_size - head_pos);
r = copy_to_user(p + buf_pos, ringbuffer->buf + head_pos, t);
if (r)
pr_debug("ringbuffer_read_user: copy_to_user() 1 failed. remain: %lu\n", r);
if (t < read_size) {
r = copy_to_user(p + buf_pos + t,
ringbuffer->buf, read_size - t);
if (r)
pr_debug("ringbuffer_read_user: copy_to_user() 2 failed. remain: %lu\n", r);
head_pos = read_size - t;
} else { } else {
head_pos = (head_pos + read_size == buf_size) ? 0 : (head_pos + read_size); head = (head + read_size == buf_size) ? 0 : (head + read_size);
} }
ringbuffer->head_pos = head_pos; atomic_long_xchg(&ringbuf->head, head);
buf_pos += read_size; atomic_long_sub_return_release(read_size,
&ringbuf->actual_size);
#ifdef RINGBUFFER_USE_SPINLOCK
spin_lock_irqsave(&ringbuffer->lock, flags);
ringbuffer->data_size -= read_size;
spin_unlock_irqrestore(&ringbuffer->lock, flags);
#else
atomic_sub(read_size, &ringbuffer->data_size);
#endif
} }
*len = buf_pos; if (!atomic_sub_return(1, &ringbuf->rw_count) &&
atomic_read(&ringbuf->wait_count))
wake_up(&ringbuf->wait);
if (!atomic_sub_return(1, &ringbuffer->rw_cnt) && *len = read_size;
atomic_read(&ringbuffer->wait_cnt))
wake_up(&ringbuffer->wait); return 0;
}
int ringbuffer_write_atomic(struct ringbuffer *ringbuf,
const void *buf, size_t *len)
{
int ret = 0;
u8 *p;
size_t buf_size, actual_size, tail, write_size;
if (atomic_read(&ringbuf->state) != 2)
return -EINVAL;
atomic_add_return_acquire(1, &ringbuf->rw_count);
p = ringbuf->buf;
buf_size = ringbuf->size;
actual_size = atomic_long_read_acquire(&ringbuf->actual_size);
tail = atomic_long_read(&ringbuf->tail);
write_size = (actual_size + *len <= buf_size) ? *len : (buf_size - actual_size);
if (write_size) {
size_t tmp = (tail + write_size <= buf_size) ? write_size : (buf_size - tail);
memcpy(p + tail, buf, tmp);
if (tmp < write_size) {
memcpy(p, ((u8 *)buf) + tmp, write_size - tmp);
tail = write_size - tmp;
} else {
tail = (tail + write_size == buf_size) ? 0 : (tail + write_size);
}
atomic_long_xchg(&ringbuf->tail, tail);
atomic_long_add_return_release(write_size,
&ringbuf->actual_size);
}
if (!atomic_sub_return(1, &ringbuf->rw_count) &&
atomic_read(&ringbuf->wait_count))
wake_up(&ringbuf->wait);
if (*len != write_size)
ret = -EOVERFLOW;
*len = write_size;
return ret; return ret;
} }
bool ringbuffer_is_running(struct ringbuffer *ringbuf)
{
return !!atomic_read_acquire(&ringbuf->state);
}
bool ringbuffer_is_readable(struct ringbuffer *ringbuf)
{
return !!atomic_long_read_acquire(&ringbuf->actual_size);
}

View File

@@ -2,7 +2,7 @@
/* /*
* Ringbuffer definitions (ringbuffer.h) * Ringbuffer definitions (ringbuffer.h)
* *
* Copyright (c) 2018-2019 nns779 * Copyright (c) 2018-2020 nns779
*/ */
#ifndef __RINGBUFFER_H__ #ifndef __RINGBUFFER_H__
@@ -10,40 +10,33 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/wait.h> #include <linux/wait.h>
struct ringbuffer { struct ringbuffer {
#ifdef RINGBUFFER_USE_SPINLOCK atomic_t state;
spinlock_t lock; // for data_size atomic_t rw_count;
#endif atomic_t wait_count;
atomic_t avail;
atomic_t rw_cnt;
atomic_t wait_cnt;
wait_queue_head_t wait; wait_queue_head_t wait;
wait_queue_head_t data_wait;
u8 *buf; u8 *buf;
size_t buf_size; size_t size;
#ifdef RINGBUFFER_USE_SPINLOCK atomic_long_t actual_size;
size_t data_size; atomic_long_t head; // read
#else atomic_long_t tail; // write
atomic_t data_size;
#endif
size_t tail_pos; // write
size_t head_pos; // read
size_t write_size;
size_t write_threshold_size;
}; };
int ringbuffer_create(struct ringbuffer **ringbuffer); int ringbuffer_create(struct ringbuffer **ringbuf);
int ringbuffer_destroy(struct ringbuffer *ringbuffer); int ringbuffer_destroy(struct ringbuffer *ringbuf);
int ringbuffer_alloc(struct ringbuffer *ringbuffer, size_t size); int ringbuffer_alloc(struct ringbuffer *ringbuf, size_t size);
int ringbuffer_free(struct ringbuffer *ringbuffer); int ringbuffer_free(struct ringbuffer *ringbuf);
int ringbuffer_start(struct ringbuffer *ringbuffer); int ringbuffer_reset(struct ringbuffer *ringbuf);
int ringbuffer_stop(struct ringbuffer *ringbuffer); int ringbuffer_start(struct ringbuffer *ringbuf);
int ringbuffer_write_atomic(struct ringbuffer *ringbuffer, int ringbuffer_stop(struct ringbuffer *ringbuf);
const void *data, size_t len); int ringbuffer_ready_read(struct ringbuffer *ringbuf);
int ringbuffer_read_user(struct ringbuffer *ringbuffer, int ringbuffer_read_user(struct ringbuffer *ringbuf,
void __user *buf, size_t *len); void __user *buf, size_t *len);
int ringbuffer_write_atomic(struct ringbuffer *ringbuf,
const void *buf, size_t *len);
bool ringbuffer_is_readable(struct ringbuffer *ringbuf);
bool ringbuffer_is_running(struct ringbuffer *ringbuf);
#endif #endif

View File

@@ -366,6 +366,7 @@ int tc90522_init(struct tc90522_demod *demod)
{ {
mutex_init(&demod->priv.lock); mutex_init(&demod->priv.lock);
demod->i2c_master.gate_ctrl = NULL;
demod->i2c_master.request = tc90522_i2c_master_request; demod->i2c_master.request = tc90522_i2c_master_request;
demod->i2c_master.priv = demod; demod->i2c_master.priv = demod;
@@ -374,6 +375,7 @@ int tc90522_init(struct tc90522_demod *demod)
int tc90522_term(struct tc90522_demod *demod) int tc90522_term(struct tc90522_demod *demod)
{ {
demod->i2c_master.gate_ctrl = NULL;
demod->i2c_master.request = NULL; demod->i2c_master.request = NULL;
demod->i2c_master.priv = NULL; demod->i2c_master.priv = NULL;