mirror of
https://github.com/tsukumijima/px4_drv.git
synced 2025-07-23 12:13:06 +02:00
driver: 内部構造を変更
This commit is contained in:
@@ -5,7 +5,6 @@ DEBUG := 0
|
||||
dMAX_DEVICE := 0
|
||||
dPSB_DEBUG := 0
|
||||
dITEDTV_BUS_USE_WORKQUEUE := 0
|
||||
dRINGBUFFER_USE_SPINLOCK := 0
|
||||
|
||||
ccflags-y := -I$(M)/../include
|
||||
|
||||
@@ -13,8 +12,8 @@ ifneq ($(DEBUG),0)
|
||||
ccflags-y += -DDEBUG -g
|
||||
endif
|
||||
|
||||
ifneq ($(dMAX_DEVICE),0)
|
||||
ccflags-y += -DMAX_DEVICE=$(dMAX_DEVICE)
|
||||
ifneq ($(dPX4_USB_MAX_DEVICE),0)
|
||||
ccflags-y += -DMAX_DEVICE=$(dPX4_USB_MAX_DEVICE)
|
||||
endif
|
||||
ifneq ($(dPSB_DEBUG),0)
|
||||
ccflags-y += -DPSB_DEBUG
|
||||
@@ -22,9 +21,6 @@ endif
|
||||
ifneq ($(dITEDTV_BUS_USE_WORKQUEUE),0)
|
||||
ccflags-y += -DITEDTV_BUS_USE_WORKQUEUE
|
||||
endif
|
||||
ifneq ($(dRINGBUFFER_USE_SPINLOCK),0)
|
||||
ccflags-y += -DRINGBUFFER_USE_SPINLOCK
|
||||
endif
|
||||
|
||||
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
|
||||
|
@@ -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);
|
@@ -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
61
driver/driver_module.c
Normal 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
8
driver/driver_module.h
Normal file
@@ -0,0 +1,8 @@
|
||||
// module.h
|
||||
|
||||
#ifndef __DRIVER_MODULE_H__
|
||||
#define __DRIVER_MODULE_H__
|
||||
|
||||
#define PX4_DRV_VERSION "0.4.0"
|
||||
|
||||
#endif
|
@@ -3,8 +3,6 @@
|
||||
#ifndef __FIRMWARE_H__
|
||||
#define __FIRMWARE_H__
|
||||
|
||||
#if !defined(FIRMWARE_FILENAME)
|
||||
#define FIRMWARE_FILENAME "it930x-firmware.bin"
|
||||
#endif
|
||||
#define IT930X_FIRMWARE_FILENAME "it930x-firmware.bin"
|
||||
|
||||
#endif
|
||||
|
@@ -26,12 +26,19 @@ struct i2c_comm_request {
|
||||
};
|
||||
|
||||
struct i2c_comm_master {
|
||||
int (*gate_ctrl)(void *i2c_priv, bool open);
|
||||
int (*request)(void *i2c_priv,
|
||||
const struct i2c_comm_request *req,
|
||||
int num);
|
||||
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,
|
||||
const struct i2c_comm_request *req,
|
||||
int num)
|
||||
|
1775
driver/isdb2056.c
1775
driver/isdb2056.c
File diff suppressed because it is too large
Load Diff
@@ -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
1060
driver/isdb2056_device.c
Normal file
File diff suppressed because it is too large
Load Diff
46
driver/isdb2056_device.h
Normal file
46
driver/isdb2056_device.h
Normal 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
|
@@ -568,6 +568,7 @@ int it930x_init(struct it930x_bridge *it930x)
|
||||
priv->i2c[i].it930x = it930x;
|
||||
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].priv = &priv->i2c[i];
|
||||
}
|
||||
@@ -597,6 +598,7 @@ int it930x_term(struct it930x_bridge *it930x)
|
||||
/* clear the i2c operator */
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
it930x->i2c_master[i].gate_ctrl = NULL;
|
||||
it930x->i2c_master[i].request = NULL;
|
||||
it930x->i2c_master[i].priv = NULL;
|
||||
}
|
||||
|
@@ -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);
|
@@ -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
963
driver/ptx_chrdev.c
Normal 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
124
driver/ptx_chrdev.h
Normal 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
|
2231
driver/px4.c
2231
driver/px4.c
File diff suppressed because it is too large
Load Diff
@@ -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
1377
driver/px4_device.c
Normal file
File diff suppressed because it is too large
Load Diff
65
driver/px4_device.h
Normal file
65
driver/px4_device.h
Normal 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
|
33
driver/px4_device_params.c
Normal file
33
driver/px4_device_params.c
Normal 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);
|
17
driver/px4_device_params.h
Normal file
17
driver/px4_device_params.h
Normal 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
210
driver/px4_mldev.c
Normal 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
37
driver/px4_mldev.h
Normal 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
256
driver/px4_usb.c
Normal 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
38
driver/px4_usb.h
Normal 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
35
driver/px4_usb_params.c
Normal 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
17
driver/px4_usb_params.h
Normal 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
|
@@ -1,365 +1,263 @@
|
||||
// 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"
|
||||
|
||||
static int _ringbuffer_init(struct ringbuffer *ringbuffer)
|
||||
{
|
||||
#ifdef RINGBUFFER_USE_SPINLOCK
|
||||
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;
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
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;
|
||||
|
||||
*ringbuffer = NULL;
|
||||
|
||||
p = kzalloc(sizeof(struct ringbuffer), GFP_KERNEL);
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = _ringbuffer_init(p);
|
||||
if (!ret)
|
||||
*ringbuffer = p;
|
||||
else
|
||||
kfree(p);
|
||||
atomic_set(&p->state, 0);
|
||||
atomic_set(&p->rw_count, 0);
|
||||
atomic_set(&p->wait_count, 0);
|
||||
init_waitqueue_head(&p->wait);
|
||||
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;
|
||||
|
||||
ret = ringbuffer_free(ringbuffer);
|
||||
if (!ret)
|
||||
kfree(ringbuffer);
|
||||
if (atomic_read_acquire(&ringbuf->state))
|
||||
return -EBUSY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
ringbuffer_lock(ringbuf);
|
||||
|
||||
static void _ringbuffer_free(struct ringbuffer *ringbuffer)
|
||||
{
|
||||
free_pages((unsigned long)ringbuffer->buf,
|
||||
get_order(ringbuffer->buf_size));
|
||||
if (ringbuf->buf && ringbuf->size != size)
|
||||
__ringbuffer_free(ringbuf);
|
||||
|
||||
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;
|
||||
}
|
||||
ringbuf->size = 0;
|
||||
__ringbuffer_reset(ringbuf);
|
||||
|
||||
int ringbuffer_alloc(struct ringbuffer *ringbuffer, size_t size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// 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) {
|
||||
if (!ringbuf->buf) {
|
||||
ringbuf->buf = (u8 *)__get_free_pages(GFP_KERNEL,
|
||||
get_order(size));
|
||||
if (!ringbuf->buf)
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
else
|
||||
ringbuf->size = size;
|
||||
}
|
||||
|
||||
ringbuffer->buf_size = size;
|
||||
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);
|
||||
ringbuffer_unlock(ringbuf);
|
||||
|
||||
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
|
||||
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)
|
||||
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);
|
||||
ringbuffer_lock(ringbuf);
|
||||
__ringbuffer_reset(ringbuf);
|
||||
__ringbuffer_free(ringbuf);
|
||||
ringbuffer_unlock(ringbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ringbuffer_write_atomic(struct ringbuffer *ringbuffer,
|
||||
const void *data, size_t len)
|
||||
int ringbuffer_reset(struct ringbuffer *ringbuf)
|
||||
{
|
||||
#ifdef RINGBUFFER_USE_SPINLOCK
|
||||
unsigned long flags;
|
||||
#endif
|
||||
const u8 *p = data;
|
||||
size_t buf_size, data_size, tail_pos, write_size;
|
||||
if (atomic_read_acquire(&ringbuf->state))
|
||||
return -EBUSY;
|
||||
|
||||
if (atomic_read(&ringbuffer->avail) != 2)
|
||||
return -EIO;
|
||||
ringbuffer_lock(ringbuf);
|
||||
__ringbuffer_reset(ringbuf);
|
||||
ringbuffer_unlock(ringbuf);
|
||||
|
||||
atomic_add(1, &ringbuffer->rw_cnt);
|
||||
|
||||
if (atomic_read(&ringbuffer->wait_cnt)) {
|
||||
atomic_sub(1, &ringbuffer->rw_cnt);
|
||||
wake_up(&ringbuffer->wait);
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf_size = ringbuffer->buf_size;
|
||||
tail_pos = ringbuffer->tail_pos;
|
||||
int ringbuffer_start(struct ringbuffer *ringbuf)
|
||||
{
|
||||
if (atomic_cmpxchg(&ringbuf->state, 0, 1))
|
||||
return -EALREADY;
|
||||
|
||||
#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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ringbuffer->tail_pos = tail_pos;
|
||||
int ringbuffer_stop(struct ringbuffer *ringbuf)
|
||||
{
|
||||
if (!atomic_xchg(&ringbuf->state, 0))
|
||||
return -EALREADY;
|
||||
|
||||
#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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!atomic_sub_return(1, &ringbuffer->rw_cnt) &&
|
||||
atomic_read(&ringbuffer->wait_cnt))
|
||||
wake_up(&ringbuffer->wait);
|
||||
int ringbuffer_ready_read(struct ringbuffer *ringbuf)
|
||||
{
|
||||
if (!atomic_cmpxchg(&ringbuf->state, 1, 2))
|
||||
return -EINVAL;
|
||||
|
||||
return (write_size != len) ? (-ECANCELED) : (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ringbuffer_read_user(struct ringbuffer *ringbuffer,
|
||||
int ringbuffer_read_user(struct ringbuffer *ringbuf,
|
||||
void __user *buf, size_t *len)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 *p = buf;
|
||||
size_t buf_size, l = *len, buf_pos = 0;
|
||||
u8 *p;
|
||||
size_t buf_size, actual_size, head, read_size;
|
||||
|
||||
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)) {
|
||||
atomic_sub(1, &ringbuffer->rw_cnt);
|
||||
wake_up(&ringbuffer->wait);
|
||||
return -EIO;
|
||||
}
|
||||
read_size = (*len <= actual_size) ? *len : actual_size;
|
||||
if (read_size) {
|
||||
size_t tmp = (head + read_size <= buf_size) ? read_size : (buf_size - head);
|
||||
unsigned long res;
|
||||
|
||||
buf_size = ringbuffer->buf_size;
|
||||
res = copy_to_user(buf, p + head, tmp);
|
||||
|
||||
while (l > buf_pos) {
|
||||
#ifdef RINGBUFFER_USE_SPINLOCK
|
||||
unsigned long flags;
|
||||
#endif
|
||||
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;
|
||||
if (tmp < read_size) {
|
||||
res = copy_to_user(((u8 *)buf) + tmp, p,
|
||||
read_size - tmp);
|
||||
head = read_size - tmp;
|
||||
} 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;
|
||||
buf_pos += read_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
|
||||
atomic_long_xchg(&ringbuf->head, head);
|
||||
atomic_long_sub_return_release(read_size,
|
||||
&ringbuf->actual_size);
|
||||
}
|
||||
|
||||
*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) &&
|
||||
atomic_read(&ringbuffer->wait_cnt))
|
||||
wake_up(&ringbuffer->wait);
|
||||
*len = read_size;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Ringbuffer definitions (ringbuffer.h)
|
||||
*
|
||||
* Copyright (c) 2018-2019 nns779
|
||||
* Copyright (c) 2018-2020 nns779
|
||||
*/
|
||||
|
||||
#ifndef __RINGBUFFER_H__
|
||||
@@ -10,40 +10,33 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
struct ringbuffer {
|
||||
#ifdef RINGBUFFER_USE_SPINLOCK
|
||||
spinlock_t lock; // for data_size
|
||||
#endif
|
||||
atomic_t avail;
|
||||
atomic_t rw_cnt;
|
||||
atomic_t wait_cnt;
|
||||
atomic_t state;
|
||||
atomic_t rw_count;
|
||||
atomic_t wait_count;
|
||||
wait_queue_head_t wait;
|
||||
wait_queue_head_t data_wait;
|
||||
u8 *buf;
|
||||
size_t buf_size;
|
||||
#ifdef RINGBUFFER_USE_SPINLOCK
|
||||
size_t data_size;
|
||||
#else
|
||||
atomic_t data_size;
|
||||
#endif
|
||||
size_t tail_pos; // write
|
||||
size_t head_pos; // read
|
||||
size_t write_size;
|
||||
size_t write_threshold_size;
|
||||
size_t size;
|
||||
atomic_long_t actual_size;
|
||||
atomic_long_t head; // read
|
||||
atomic_long_t tail; // write
|
||||
};
|
||||
|
||||
int ringbuffer_create(struct ringbuffer **ringbuffer);
|
||||
int ringbuffer_destroy(struct ringbuffer *ringbuffer);
|
||||
int ringbuffer_alloc(struct ringbuffer *ringbuffer, size_t size);
|
||||
int ringbuffer_free(struct ringbuffer *ringbuffer);
|
||||
int ringbuffer_start(struct ringbuffer *ringbuffer);
|
||||
int ringbuffer_stop(struct ringbuffer *ringbuffer);
|
||||
int ringbuffer_write_atomic(struct ringbuffer *ringbuffer,
|
||||
const void *data, size_t len);
|
||||
int ringbuffer_read_user(struct ringbuffer *ringbuffer,
|
||||
int ringbuffer_create(struct ringbuffer **ringbuf);
|
||||
int ringbuffer_destroy(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_alloc(struct ringbuffer *ringbuf, size_t size);
|
||||
int ringbuffer_free(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_reset(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_start(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_stop(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_ready_read(struct ringbuffer *ringbuf);
|
||||
int ringbuffer_read_user(struct ringbuffer *ringbuf,
|
||||
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
|
||||
|
@@ -366,6 +366,7 @@ int tc90522_init(struct tc90522_demod *demod)
|
||||
{
|
||||
mutex_init(&demod->priv.lock);
|
||||
|
||||
demod->i2c_master.gate_ctrl = NULL;
|
||||
demod->i2c_master.request = tc90522_i2c_master_request;
|
||||
demod->i2c_master.priv = demod;
|
||||
|
||||
@@ -374,6 +375,7 @@ int tc90522_init(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.priv = NULL;
|
||||
|
||||
|
Reference in New Issue
Block a user