mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
UPSTREAM: [media] cec: move the CEC framework out of staging and to media
The last open issues have been addressed, so it is time to move
this out of staging and into the mainline and to move the public
cec headers to include/uapi/linux.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
(cherry picked from commit 0dbacebede)
Change-Id: I7bf14806f5043a2565328fa715cf44fe2b4ad713
Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
This commit is contained in:
@@ -80,6 +80,22 @@ config MEDIA_RC_SUPPORT
|
||||
|
||||
Say Y when you have a TV or an IR device.
|
||||
|
||||
config MEDIA_CEC_SUPPORT
|
||||
bool "HDMI CEC support"
|
||||
select MEDIA_CEC_EDID
|
||||
---help---
|
||||
Enable support for HDMI CEC (Consumer Electronics Control),
|
||||
which is an optional HDMI feature.
|
||||
|
||||
Say Y when you have an HDMI receiver, transmitter or a USB CEC
|
||||
adapter that supports HDMI CEC.
|
||||
|
||||
config MEDIA_CEC_DEBUG
|
||||
bool "HDMI CEC debugfs interface"
|
||||
depends on MEDIA_CEC_SUPPORT && DEBUG_FS
|
||||
---help---
|
||||
Turns on the DebugFS interface for CEC devices.
|
||||
|
||||
config MEDIA_CEC_EDID
|
||||
bool
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ ifeq ($(CONFIG_MEDIA_CEC_EDID),y)
|
||||
obj-$(CONFIG_MEDIA_SUPPORT) += cec-edid.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y)
|
||||
obj-$(CONFIG_MEDIA_SUPPORT) += cec/
|
||||
endif
|
||||
|
||||
media-objs := media-device.o media-devnode.o media-entity.o
|
||||
|
||||
#
|
||||
|
||||
5
drivers/media/cec/Makefile
Normal file
5
drivers/media/cec/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
cec-objs := cec-core.o cec-adap.o cec-api.o
|
||||
|
||||
ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y)
|
||||
obj-$(CONFIG_MEDIA_SUPPORT) += cec.o
|
||||
endif
|
||||
1856
drivers/media/cec/cec-adap.c
Normal file
1856
drivers/media/cec/cec-adap.c
Normal file
File diff suppressed because it is too large
Load Diff
588
drivers/media/cec/cec-api.c
Normal file
588
drivers/media/cec/cec-api.c
Normal file
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* cec-api.c - HDMI Consumer Electronics Control framework - API
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "cec-priv.h"
|
||||
|
||||
static inline struct cec_devnode *cec_devnode_data(struct file *filp)
|
||||
{
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
|
||||
return &fh->adap->devnode;
|
||||
}
|
||||
|
||||
/* CEC file operations */
|
||||
|
||||
static unsigned int cec_poll(struct file *filp,
|
||||
struct poll_table_struct *poll)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
struct cec_adapter *adap = fh->adap;
|
||||
unsigned int res = 0;
|
||||
|
||||
if (!devnode->registered)
|
||||
return POLLERR | POLLHUP;
|
||||
mutex_lock(&adap->lock);
|
||||
if (adap->is_configured &&
|
||||
adap->transmit_queue_sz < CEC_MAX_MSG_TX_QUEUE_SZ)
|
||||
res |= POLLOUT | POLLWRNORM;
|
||||
if (fh->queued_msgs)
|
||||
res |= POLLIN | POLLRDNORM;
|
||||
if (fh->pending_events)
|
||||
res |= POLLPRI;
|
||||
poll_wait(filp, &fh->wait, poll);
|
||||
mutex_unlock(&adap->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool cec_is_busy(const struct cec_adapter *adap,
|
||||
const struct cec_fh *fh)
|
||||
{
|
||||
bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
|
||||
bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
|
||||
|
||||
/*
|
||||
* Exclusive initiators and followers can always access the CEC adapter
|
||||
*/
|
||||
if (valid_initiator || valid_follower)
|
||||
return false;
|
||||
/*
|
||||
* All others can only access the CEC adapter if there is no
|
||||
* exclusive initiator and they are in INITIATOR mode.
|
||||
*/
|
||||
return adap->cec_initiator ||
|
||||
fh->mode_initiator == CEC_MODE_NO_INITIATOR;
|
||||
}
|
||||
|
||||
static long cec_adap_g_caps(struct cec_adapter *adap,
|
||||
struct cec_caps __user *parg)
|
||||
{
|
||||
struct cec_caps caps = {};
|
||||
|
||||
strlcpy(caps.driver, adap->devnode.parent->driver->name,
|
||||
sizeof(caps.driver));
|
||||
strlcpy(caps.name, adap->name, sizeof(caps.name));
|
||||
caps.available_log_addrs = adap->available_log_addrs;
|
||||
caps.capabilities = adap->capabilities;
|
||||
caps.version = LINUX_VERSION_CODE;
|
||||
if (copy_to_user(parg, &caps, sizeof(caps)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_g_phys_addr(struct cec_adapter *adap,
|
||||
__u16 __user *parg)
|
||||
{
|
||||
u16 phys_addr;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
phys_addr = adap->phys_addr;
|
||||
mutex_unlock(&adap->lock);
|
||||
if (copy_to_user(parg, &phys_addr, sizeof(phys_addr)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, __u16 __user *parg)
|
||||
{
|
||||
u16 phys_addr;
|
||||
long err;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
|
||||
return -EFAULT;
|
||||
|
||||
err = cec_phys_addr_validate(phys_addr, NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&adap->lock);
|
||||
if (cec_is_busy(adap, fh))
|
||||
err = -EBUSY;
|
||||
else
|
||||
__cec_s_phys_addr(adap, phys_addr, block);
|
||||
mutex_unlock(&adap->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long cec_adap_g_log_addrs(struct cec_adapter *adap,
|
||||
struct cec_log_addrs __user *parg)
|
||||
{
|
||||
struct cec_log_addrs log_addrs;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
log_addrs = adap->log_addrs;
|
||||
if (!adap->is_configured)
|
||||
memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
|
||||
sizeof(log_addrs.log_addr));
|
||||
mutex_unlock(&adap->lock);
|
||||
|
||||
if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_log_addrs __user *parg)
|
||||
{
|
||||
struct cec_log_addrs log_addrs;
|
||||
long err = -EBUSY;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
log_addrs.flags &= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK |
|
||||
CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
|
||||
CEC_LOG_ADDRS_FL_CDC_ONLY;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configuring &&
|
||||
(!log_addrs.num_log_addrs || !adap->is_configured) &&
|
||||
!cec_is_busy(adap, fh)) {
|
||||
err = __cec_s_log_addrs(adap, &log_addrs, block);
|
||||
if (!err)
|
||||
log_addrs = adap->log_addrs;
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_msg __user *parg)
|
||||
{
|
||||
struct cec_msg msg = {};
|
||||
long err = 0;
|
||||
|
||||
if (!(adap->capabilities & CEC_CAP_TRANSMIT))
|
||||
return -ENOTTY;
|
||||
if (copy_from_user(&msg, parg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
|
||||
/* A CDC-Only device can only send CDC messages */
|
||||
if ((adap->log_addrs.flags & CEC_LOG_ADDRS_FL_CDC_ONLY) &&
|
||||
(msg.len == 1 || msg.msg[1] != CEC_MSG_CDC_MESSAGE))
|
||||
return -EINVAL;
|
||||
|
||||
msg.flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configured)
|
||||
err = -ENONET;
|
||||
else if (cec_is_busy(adap, fh))
|
||||
err = -EBUSY;
|
||||
else
|
||||
err = cec_transmit_msg_fh(adap, &msg, fh, block);
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &msg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called by CEC_RECEIVE: wait for a message to arrive */
|
||||
static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
|
||||
{
|
||||
u32 timeout = msg->timeout;
|
||||
int res;
|
||||
|
||||
do {
|
||||
mutex_lock(&fh->lock);
|
||||
/* Are there received messages queued up? */
|
||||
if (fh->queued_msgs) {
|
||||
/* Yes, return the first one */
|
||||
struct cec_msg_entry *entry =
|
||||
list_first_entry(&fh->msgs,
|
||||
struct cec_msg_entry, list);
|
||||
|
||||
list_del(&entry->list);
|
||||
*msg = entry->msg;
|
||||
kfree(entry);
|
||||
fh->queued_msgs--;
|
||||
mutex_unlock(&fh->lock);
|
||||
/* restore original timeout value */
|
||||
msg->timeout = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No, return EAGAIN in non-blocking mode or wait */
|
||||
mutex_unlock(&fh->lock);
|
||||
|
||||
/* Return when in non-blocking mode */
|
||||
if (!block)
|
||||
return -EAGAIN;
|
||||
|
||||
if (msg->timeout) {
|
||||
/* The user specified a timeout */
|
||||
res = wait_event_interruptible_timeout(fh->wait,
|
||||
fh->queued_msgs,
|
||||
msecs_to_jiffies(msg->timeout));
|
||||
if (res == 0)
|
||||
res = -ETIMEDOUT;
|
||||
else if (res > 0)
|
||||
res = 0;
|
||||
} else {
|
||||
/* Wait indefinitely */
|
||||
res = wait_event_interruptible(fh->wait,
|
||||
fh->queued_msgs);
|
||||
}
|
||||
/* Exit on error, otherwise loop to get the new message */
|
||||
} while (!res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_msg __user *parg)
|
||||
{
|
||||
struct cec_msg msg = {};
|
||||
long err = 0;
|
||||
|
||||
if (copy_from_user(&msg, parg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
mutex_lock(&adap->lock);
|
||||
if (!adap->is_configured && fh->mode_follower < CEC_MODE_MONITOR)
|
||||
err = -ENONET;
|
||||
mutex_unlock(&adap->lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = cec_receive_msg(fh, &msg, block);
|
||||
if (err)
|
||||
return err;
|
||||
if (copy_to_user(parg, &msg, sizeof(msg)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
bool block, struct cec_event __user *parg)
|
||||
{
|
||||
struct cec_event *ev = NULL;
|
||||
u64 ts = ~0ULL;
|
||||
unsigned int i;
|
||||
long err = 0;
|
||||
|
||||
mutex_lock(&fh->lock);
|
||||
while (!fh->pending_events && block) {
|
||||
mutex_unlock(&fh->lock);
|
||||
err = wait_event_interruptible(fh->wait, fh->pending_events);
|
||||
if (err)
|
||||
return err;
|
||||
mutex_lock(&fh->lock);
|
||||
}
|
||||
|
||||
/* Find the oldest event */
|
||||
for (i = 0; i < CEC_NUM_EVENTS; i++) {
|
||||
if (fh->pending_events & (1 << (i + 1)) &&
|
||||
fh->events[i].ts <= ts) {
|
||||
ev = &fh->events[i];
|
||||
ts = ev->ts;
|
||||
}
|
||||
}
|
||||
if (!ev) {
|
||||
err = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (copy_to_user(parg, ev, sizeof(*ev))) {
|
||||
err = -EFAULT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
fh->pending_events &= ~(1 << ev->event);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&fh->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
u32 __user *parg)
|
||||
{
|
||||
u32 mode = fh->mode_initiator | fh->mode_follower;
|
||||
|
||||
if (copy_to_user(parg, &mode, sizeof(mode)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh,
|
||||
u32 __user *parg)
|
||||
{
|
||||
u32 mode;
|
||||
u8 mode_initiator;
|
||||
u8 mode_follower;
|
||||
long err = 0;
|
||||
|
||||
if (copy_from_user(&mode, parg, sizeof(mode)))
|
||||
return -EFAULT;
|
||||
if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK))
|
||||
return -EINVAL;
|
||||
|
||||
mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
|
||||
mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
|
||||
|
||||
if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
|
||||
mode_follower > CEC_MODE_MONITOR_ALL)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode_follower == CEC_MODE_MONITOR_ALL &&
|
||||
!(adap->capabilities & CEC_CAP_MONITOR_ALL))
|
||||
return -EINVAL;
|
||||
|
||||
/* Follower modes should always be able to send CEC messages */
|
||||
if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
|
||||
!(adap->capabilities & CEC_CAP_TRANSMIT)) &&
|
||||
mode_follower >= CEC_MODE_FOLLOWER &&
|
||||
mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU)
|
||||
return -EINVAL;
|
||||
|
||||
/* Monitor modes require CEC_MODE_NO_INITIATOR */
|
||||
if (mode_initiator && mode_follower >= CEC_MODE_MONITOR)
|
||||
return -EINVAL;
|
||||
|
||||
/* Monitor modes require CAP_NET_ADMIN */
|
||||
if (mode_follower >= CEC_MODE_MONITOR && !capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
/*
|
||||
* You can't become exclusive follower if someone else already
|
||||
* has that job.
|
||||
*/
|
||||
if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
|
||||
adap->cec_follower && adap->cec_follower != fh)
|
||||
err = -EBUSY;
|
||||
/*
|
||||
* You can't become exclusive initiator if someone else already
|
||||
* has that job.
|
||||
*/
|
||||
if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
|
||||
adap->cec_initiator && adap->cec_initiator != fh)
|
||||
err = -EBUSY;
|
||||
|
||||
if (!err) {
|
||||
bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
|
||||
bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
|
||||
|
||||
if (old_mon_all != new_mon_all) {
|
||||
if (new_mon_all)
|
||||
err = cec_monitor_all_cnt_inc(adap);
|
||||
else
|
||||
cec_monitor_all_cnt_dec(adap);
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mutex_unlock(&adap->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (fh->mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt--;
|
||||
if (mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt++;
|
||||
if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
|
||||
adap->passthrough =
|
||||
mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
|
||||
adap->cec_follower = fh;
|
||||
} else if (adap->cec_follower == fh) {
|
||||
adap->passthrough = false;
|
||||
adap->cec_follower = NULL;
|
||||
}
|
||||
if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
|
||||
adap->cec_initiator = fh;
|
||||
else if (adap->cec_initiator == fh)
|
||||
adap->cec_initiator = NULL;
|
||||
fh->mode_initiator = mode_initiator;
|
||||
fh->mode_follower = mode_follower;
|
||||
mutex_unlock(&adap->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
struct cec_adapter *adap = fh->adap;
|
||||
bool block = !(filp->f_flags & O_NONBLOCK);
|
||||
void __user *parg = (void __user *)arg;
|
||||
|
||||
if (!devnode->registered)
|
||||
return -ENODEV;
|
||||
|
||||
switch (cmd) {
|
||||
case CEC_ADAP_G_CAPS:
|
||||
return cec_adap_g_caps(adap, parg);
|
||||
|
||||
case CEC_ADAP_G_PHYS_ADDR:
|
||||
return cec_adap_g_phys_addr(adap, parg);
|
||||
|
||||
case CEC_ADAP_S_PHYS_ADDR:
|
||||
return cec_adap_s_phys_addr(adap, fh, block, parg);
|
||||
|
||||
case CEC_ADAP_G_LOG_ADDRS:
|
||||
return cec_adap_g_log_addrs(adap, parg);
|
||||
|
||||
case CEC_ADAP_S_LOG_ADDRS:
|
||||
return cec_adap_s_log_addrs(adap, fh, block, parg);
|
||||
|
||||
case CEC_TRANSMIT:
|
||||
return cec_transmit(adap, fh, block, parg);
|
||||
|
||||
case CEC_RECEIVE:
|
||||
return cec_receive(adap, fh, block, parg);
|
||||
|
||||
case CEC_DQEVENT:
|
||||
return cec_dqevent(adap, fh, block, parg);
|
||||
|
||||
case CEC_G_MODE:
|
||||
return cec_g_mode(adap, fh, parg);
|
||||
|
||||
case CEC_S_MODE:
|
||||
return cec_s_mode(adap, fh, parg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static int cec_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct cec_devnode *devnode =
|
||||
container_of(inode->i_cdev, struct cec_devnode, cdev);
|
||||
struct cec_adapter *adap = to_cec_adapter(devnode);
|
||||
struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
|
||||
/*
|
||||
* Initial events that are automatically sent when the cec device is
|
||||
* opened.
|
||||
*/
|
||||
struct cec_event ev_state = {
|
||||
.event = CEC_EVENT_STATE_CHANGE,
|
||||
.flags = CEC_EVENT_FL_INITIAL_STATE,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (!fh)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&fh->msgs);
|
||||
INIT_LIST_HEAD(&fh->xfer_list);
|
||||
mutex_init(&fh->lock);
|
||||
init_waitqueue_head(&fh->wait);
|
||||
|
||||
fh->mode_initiator = CEC_MODE_INITIATOR;
|
||||
fh->adap = adap;
|
||||
|
||||
err = cec_get_device(devnode);
|
||||
if (err) {
|
||||
kfree(fh);
|
||||
return err;
|
||||
}
|
||||
|
||||
filp->private_data = fh;
|
||||
|
||||
mutex_lock(&devnode->lock);
|
||||
/* Queue up initial state events */
|
||||
ev_state.state_change.phys_addr = adap->phys_addr;
|
||||
ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
|
||||
cec_queue_event_fh(fh, &ev_state, 0);
|
||||
|
||||
list_add(&fh->list, &devnode->fhs);
|
||||
mutex_unlock(&devnode->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Override for the release function */
|
||||
static int cec_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct cec_devnode *devnode = cec_devnode_data(filp);
|
||||
struct cec_adapter *adap = to_cec_adapter(devnode);
|
||||
struct cec_fh *fh = filp->private_data;
|
||||
|
||||
mutex_lock(&adap->lock);
|
||||
if (adap->cec_initiator == fh)
|
||||
adap->cec_initiator = NULL;
|
||||
if (adap->cec_follower == fh) {
|
||||
adap->cec_follower = NULL;
|
||||
adap->passthrough = false;
|
||||
}
|
||||
if (fh->mode_follower == CEC_MODE_FOLLOWER)
|
||||
adap->follower_cnt--;
|
||||
if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
|
||||
cec_monitor_all_cnt_dec(adap);
|
||||
mutex_unlock(&adap->lock);
|
||||
|
||||
mutex_lock(&devnode->lock);
|
||||
list_del(&fh->list);
|
||||
mutex_unlock(&devnode->lock);
|
||||
|
||||
/* Unhook pending transmits from this filehandle. */
|
||||
mutex_lock(&adap->lock);
|
||||
while (!list_empty(&fh->xfer_list)) {
|
||||
struct cec_data *data =
|
||||
list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
|
||||
|
||||
data->blocking = false;
|
||||
data->fh = NULL;
|
||||
list_del(&data->xfer_list);
|
||||
}
|
||||
mutex_unlock(&adap->lock);
|
||||
while (!list_empty(&fh->msgs)) {
|
||||
struct cec_msg_entry *entry =
|
||||
list_first_entry(&fh->msgs, struct cec_msg_entry, list);
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
kfree(fh);
|
||||
|
||||
cec_put_device(devnode);
|
||||
filp->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations cec_devnode_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = cec_open,
|
||||
.unlocked_ioctl = cec_ioctl,
|
||||
.release = cec_release,
|
||||
.poll = cec_poll,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
411
drivers/media/cec/cec-core.c
Normal file
411
drivers/media/cec/cec-core.c
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* cec-core.c - HDMI Consumer Electronics Control framework - Core
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "cec-priv.h"
|
||||
|
||||
#define CEC_NUM_DEVICES 256
|
||||
#define CEC_NAME "cec"
|
||||
|
||||
int cec_debug;
|
||||
module_param_named(debug, cec_debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "debug level (0-2)");
|
||||
|
||||
static dev_t cec_dev_t;
|
||||
|
||||
/* Active devices */
|
||||
static DEFINE_MUTEX(cec_devnode_lock);
|
||||
static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
|
||||
|
||||
static struct dentry *top_cec_dir;
|
||||
|
||||
/* dev to cec_devnode */
|
||||
#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
|
||||
|
||||
int cec_get_device(struct cec_devnode *devnode)
|
||||
{
|
||||
/*
|
||||
* Check if the cec device is available. This needs to be done with
|
||||
* the devnode->lock held to prevent an open/unregister race:
|
||||
* without the lock, the device could be unregistered and freed between
|
||||
* the devnode->registered check and get_device() calls, leading to
|
||||
* a crash.
|
||||
*/
|
||||
mutex_lock(&devnode->lock);
|
||||
/*
|
||||
* return ENXIO if the cec device has been removed
|
||||
* already or if it is not registered anymore.
|
||||
*/
|
||||
if (!devnode->registered) {
|
||||
mutex_unlock(&devnode->lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
/* and increase the device refcount */
|
||||
get_device(&devnode->dev);
|
||||
mutex_unlock(&devnode->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cec_put_device(struct cec_devnode *devnode)
|
||||
{
|
||||
put_device(&devnode->dev);
|
||||
}
|
||||
|
||||
/* Called when the last user of the cec device exits. */
|
||||
static void cec_devnode_release(struct device *cd)
|
||||
{
|
||||
struct cec_devnode *devnode = to_cec_devnode(cd);
|
||||
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
/* Mark device node number as free */
|
||||
clear_bit(devnode->minor, cec_devnode_nums);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
|
||||
cec_delete_adapter(to_cec_adapter(devnode));
|
||||
}
|
||||
|
||||
static struct bus_type cec_bus_type = {
|
||||
.name = CEC_NAME,
|
||||
};
|
||||
|
||||
/*
|
||||
* Register a cec device node
|
||||
*
|
||||
* The registration code assigns minor numbers and registers the new device node
|
||||
* with the kernel. An error is returned if no free minor number can be found,
|
||||
* or if the registration of the device node fails.
|
||||
*
|
||||
* Zero is returned on success.
|
||||
*
|
||||
* Note that if the cec_devnode_register call fails, the release() callback of
|
||||
* the cec_devnode structure is *not* called, so the caller is responsible for
|
||||
* freeing any data.
|
||||
*/
|
||||
static int __must_check cec_devnode_register(struct cec_devnode *devnode,
|
||||
struct module *owner)
|
||||
{
|
||||
int minor;
|
||||
int ret;
|
||||
|
||||
/* Initialization */
|
||||
INIT_LIST_HEAD(&devnode->fhs);
|
||||
mutex_init(&devnode->lock);
|
||||
|
||||
/* Part 1: Find a free minor number */
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
|
||||
if (minor == CEC_NUM_DEVICES) {
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
pr_err("could not get a free minor\n");
|
||||
return -ENFILE;
|
||||
}
|
||||
|
||||
set_bit(minor, cec_devnode_nums);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
|
||||
devnode->minor = minor;
|
||||
devnode->dev.bus = &cec_bus_type;
|
||||
devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
|
||||
devnode->dev.release = cec_devnode_release;
|
||||
devnode->dev.parent = devnode->parent;
|
||||
dev_set_name(&devnode->dev, "cec%d", devnode->minor);
|
||||
device_initialize(&devnode->dev);
|
||||
|
||||
/* Part 2: Initialize and register the character device */
|
||||
cdev_init(&devnode->cdev, &cec_devnode_fops);
|
||||
devnode->cdev.kobj.parent = &devnode->dev.kobj;
|
||||
devnode->cdev.owner = owner;
|
||||
|
||||
ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: cdev_add failed\n", __func__);
|
||||
goto clr_bit;
|
||||
}
|
||||
|
||||
ret = device_add(&devnode->dev);
|
||||
if (ret)
|
||||
goto cdev_del;
|
||||
|
||||
devnode->registered = true;
|
||||
return 0;
|
||||
|
||||
cdev_del:
|
||||
cdev_del(&devnode->cdev);
|
||||
clr_bit:
|
||||
mutex_lock(&cec_devnode_lock);
|
||||
clear_bit(devnode->minor, cec_devnode_nums);
|
||||
mutex_unlock(&cec_devnode_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister a cec device node
|
||||
*
|
||||
* This unregisters the passed device. Future open calls will be met with
|
||||
* errors.
|
||||
*
|
||||
* This function can safely be called if the device node has never been
|
||||
* registered or has already been unregistered.
|
||||
*/
|
||||
static void cec_devnode_unregister(struct cec_devnode *devnode)
|
||||
{
|
||||
struct cec_fh *fh;
|
||||
|
||||
mutex_lock(&devnode->lock);
|
||||
|
||||
/* Check if devnode was never registered or already unregistered */
|
||||
if (!devnode->registered || devnode->unregistered) {
|
||||
mutex_unlock(&devnode->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(fh, &devnode->fhs, list)
|
||||
wake_up_interruptible(&fh->wait);
|
||||
|
||||
devnode->registered = false;
|
||||
devnode->unregistered = true;
|
||||
mutex_unlock(&devnode->lock);
|
||||
|
||||
device_del(&devnode->dev);
|
||||
cdev_del(&devnode->cdev);
|
||||
put_device(&devnode->dev);
|
||||
}
|
||||
|
||||
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
||||
void *priv, const char *name, u32 caps,
|
||||
u8 available_las, struct device *parent)
|
||||
{
|
||||
struct cec_adapter *adap;
|
||||
int res;
|
||||
|
||||
if (WARN_ON(!parent))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!caps))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!ops))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
|
||||
return ERR_PTR(-EINVAL);
|
||||
adap = kzalloc(sizeof(*adap), GFP_KERNEL);
|
||||
if (!adap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
adap->owner = parent->driver->owner;
|
||||
adap->devnode.parent = parent;
|
||||
strlcpy(adap->name, name, sizeof(adap->name));
|
||||
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
|
||||
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
|
||||
adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
|
||||
adap->capabilities = caps;
|
||||
adap->available_log_addrs = available_las;
|
||||
adap->sequence = 0;
|
||||
adap->ops = ops;
|
||||
adap->priv = priv;
|
||||
memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
|
||||
mutex_init(&adap->lock);
|
||||
INIT_LIST_HEAD(&adap->transmit_queue);
|
||||
INIT_LIST_HEAD(&adap->wait_queue);
|
||||
init_waitqueue_head(&adap->kthread_waitq);
|
||||
|
||||
adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
|
||||
if (IS_ERR(adap->kthread)) {
|
||||
pr_err("cec-%s: kernel_thread() failed\n", name);
|
||||
res = PTR_ERR(adap->kthread);
|
||||
kfree(adap);
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
if (!(caps & CEC_CAP_RC))
|
||||
return adap;
|
||||
|
||||
#if IS_REACHABLE(CONFIG_RC_CORE)
|
||||
/* Prepare the RC input device */
|
||||
adap->rc = rc_allocate_device();
|
||||
if (!adap->rc) {
|
||||
pr_err("cec-%s: failed to allocate memory for rc_dev\n",
|
||||
name);
|
||||
kthread_stop(adap->kthread);
|
||||
kfree(adap);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
snprintf(adap->input_name, sizeof(adap->input_name),
|
||||
"RC for %s", name);
|
||||
snprintf(adap->input_phys, sizeof(adap->input_phys),
|
||||
"%s/input0", name);
|
||||
|
||||
adap->rc->input_name = adap->input_name;
|
||||
adap->rc->input_phys = adap->input_phys;
|
||||
adap->rc->input_id.bustype = BUS_CEC;
|
||||
adap->rc->input_id.vendor = 0;
|
||||
adap->rc->input_id.product = 0;
|
||||
adap->rc->input_id.version = 1;
|
||||
adap->rc->dev.parent = parent;
|
||||
adap->rc->driver_type = RC_DRIVER_SCANCODE;
|
||||
adap->rc->driver_name = CEC_NAME;
|
||||
adap->rc->allowed_protocols = RC_BIT_CEC;
|
||||
adap->rc->priv = adap;
|
||||
adap->rc->map_name = RC_MAP_CEC;
|
||||
adap->rc->timeout = MS_TO_NS(100);
|
||||
#else
|
||||
adap->capabilities &= ~CEC_CAP_RC;
|
||||
#endif
|
||||
return adap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_allocate_adapter);
|
||||
|
||||
int cec_register_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return 0;
|
||||
|
||||
#if IS_REACHABLE(CONFIG_RC_CORE)
|
||||
if (adap->capabilities & CEC_CAP_RC) {
|
||||
res = rc_register_device(adap->rc);
|
||||
|
||||
if (res) {
|
||||
pr_err("cec-%s: failed to prepare input device\n",
|
||||
adap->name);
|
||||
rc_free_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res = cec_devnode_register(&adap->devnode, adap->owner);
|
||||
if (res) {
|
||||
#if IS_REACHABLE(CONFIG_RC_CORE)
|
||||
/* Note: rc_unregister also calls rc_free */
|
||||
rc_unregister_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&adap->devnode.dev, adap);
|
||||
#ifdef CONFIG_MEDIA_CEC_DEBUG
|
||||
if (!top_cec_dir)
|
||||
return 0;
|
||||
|
||||
adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir);
|
||||
if (IS_ERR_OR_NULL(adap->cec_dir)) {
|
||||
pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name);
|
||||
return 0;
|
||||
}
|
||||
adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev,
|
||||
"status", adap->cec_dir, cec_adap_status);
|
||||
if (IS_ERR_OR_NULL(adap->status_file)) {
|
||||
pr_warn("cec-%s: Failed to create status file\n", adap->name);
|
||||
debugfs_remove_recursive(adap->cec_dir);
|
||||
adap->cec_dir = NULL;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_register_adapter);
|
||||
|
||||
void cec_unregister_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return;
|
||||
|
||||
#if IS_REACHABLE(CONFIG_RC_CORE)
|
||||
/* Note: rc_unregister also calls rc_free */
|
||||
rc_unregister_device(adap->rc);
|
||||
adap->rc = NULL;
|
||||
#endif
|
||||
debugfs_remove_recursive(adap->cec_dir);
|
||||
cec_devnode_unregister(&adap->devnode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_unregister_adapter);
|
||||
|
||||
void cec_delete_adapter(struct cec_adapter *adap)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(adap))
|
||||
return;
|
||||
mutex_lock(&adap->lock);
|
||||
__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
|
||||
mutex_unlock(&adap->lock);
|
||||
kthread_stop(adap->kthread);
|
||||
if (adap->kthread_config)
|
||||
kthread_stop(adap->kthread_config);
|
||||
#if IS_REACHABLE(CONFIG_RC_CORE)
|
||||
rc_free_device(adap->rc);
|
||||
#endif
|
||||
kfree(adap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cec_delete_adapter);
|
||||
|
||||
/*
|
||||
* Initialise cec for linux
|
||||
*/
|
||||
static int __init cec_devnode_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("Linux cec interface: v0.10\n");
|
||||
ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
|
||||
CEC_NAME);
|
||||
if (ret < 0) {
|
||||
pr_warn("cec: unable to allocate major\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEDIA_CEC_DEBUG
|
||||
top_cec_dir = debugfs_create_dir("cec", NULL);
|
||||
if (IS_ERR_OR_NULL(top_cec_dir)) {
|
||||
pr_warn("cec: Failed to create debugfs cec dir\n");
|
||||
top_cec_dir = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = bus_register(&cec_bus_type);
|
||||
if (ret < 0) {
|
||||
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
|
||||
pr_warn("cec: bus_register failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cec_devnode_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(top_cec_dir);
|
||||
bus_unregister(&cec_bus_type);
|
||||
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
|
||||
}
|
||||
|
||||
subsys_initcall(cec_devnode_init);
|
||||
module_exit(cec_devnode_exit)
|
||||
|
||||
MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
|
||||
MODULE_DESCRIPTION("Device node registration for cec drivers");
|
||||
MODULE_LICENSE("GPL");
|
||||
56
drivers/media/cec/cec-priv.h
Normal file
56
drivers/media/cec/cec-priv.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* cec-priv.h - HDMI Consumer Electronics Control internal header
|
||||
*
|
||||
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _CEC_PRIV_H
|
||||
#define _CEC_PRIV_H
|
||||
|
||||
#include <linux/cec-funcs.h>
|
||||
#include <media/cec.h>
|
||||
|
||||
#define dprintk(lvl, fmt, arg...) \
|
||||
do { \
|
||||
if (lvl <= cec_debug) \
|
||||
pr_info("cec-%s: " fmt, adap->name, ## arg); \
|
||||
} while (0)
|
||||
|
||||
/* devnode to cec_adapter */
|
||||
#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
|
||||
|
||||
/* cec-core.c */
|
||||
extern int cec_debug;
|
||||
int cec_get_device(struct cec_devnode *devnode);
|
||||
void cec_put_device(struct cec_devnode *devnode);
|
||||
|
||||
/* cec-adap.c */
|
||||
int cec_monitor_all_cnt_inc(struct cec_adapter *adap);
|
||||
void cec_monitor_all_cnt_dec(struct cec_adapter *adap);
|
||||
int cec_adap_status(struct seq_file *file, void *priv);
|
||||
int cec_thread_func(void *_adap);
|
||||
void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
|
||||
int __cec_s_log_addrs(struct cec_adapter *adap,
|
||||
struct cec_log_addrs *log_addrs, bool block);
|
||||
int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
|
||||
struct cec_fh *fh, bool block);
|
||||
void cec_queue_event_fh(struct cec_fh *fh,
|
||||
const struct cec_event *new_ev, u64 ts);
|
||||
|
||||
/* cec-api.c */
|
||||
extern const struct file_operations cec_devnode_fops;
|
||||
|
||||
#endif
|
||||
@@ -196,7 +196,7 @@ static inline bool cec_is_sink(const struct cec_adapter *adap)
|
||||
return adap->phys_addr == 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_MEDIA_CEC)
|
||||
#if IS_ENABLED(CONFIG_MEDIA_CEC_SUPPORT)
|
||||
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
|
||||
void *priv, const char *name, u32 caps, u8 available_las,
|
||||
struct device *parent);
|
||||
|
||||
@@ -81,6 +81,8 @@ header-y += capi.h
|
||||
header-y += cciss_defs.h
|
||||
header-y += cciss_ioctl.h
|
||||
header-y += cdrom.h
|
||||
header-y += cec.h
|
||||
header-y += cec-funcs.h
|
||||
header-y += cgroupstats.h
|
||||
header-y += chio.h
|
||||
header-y += cm4000_cs.h
|
||||
|
||||
1965
include/uapi/linux/cec-funcs.h
Normal file
1965
include/uapi/linux/cec-funcs.h
Normal file
File diff suppressed because it is too large
Load Diff
1065
include/uapi/linux/cec.h
Normal file
1065
include/uapi/linux/cec.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user