1de73b88cSMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-only
2de73b88cSMauro Carvalho Chehab /*
3de73b88cSMauro Carvalho Chehab * cec-api.c - HDMI Consumer Electronics Control framework - API
4de73b88cSMauro Carvalho Chehab *
5de73b88cSMauro Carvalho Chehab * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6de73b88cSMauro Carvalho Chehab */
7de73b88cSMauro Carvalho Chehab
8de73b88cSMauro Carvalho Chehab #include <linux/errno.h>
9de73b88cSMauro Carvalho Chehab #include <linux/init.h>
10de73b88cSMauro Carvalho Chehab #include <linux/module.h>
11de73b88cSMauro Carvalho Chehab #include <linux/kernel.h>
12de73b88cSMauro Carvalho Chehab #include <linux/kmod.h>
13de73b88cSMauro Carvalho Chehab #include <linux/ktime.h>
14de73b88cSMauro Carvalho Chehab #include <linux/slab.h>
15de73b88cSMauro Carvalho Chehab #include <linux/mm.h>
16de73b88cSMauro Carvalho Chehab #include <linux/string.h>
17de73b88cSMauro Carvalho Chehab #include <linux/types.h>
18de73b88cSMauro Carvalho Chehab #include <linux/uaccess.h>
19de73b88cSMauro Carvalho Chehab #include <linux/version.h>
20de73b88cSMauro Carvalho Chehab
21de73b88cSMauro Carvalho Chehab #include <media/cec-pin.h>
22de73b88cSMauro Carvalho Chehab #include "cec-priv.h"
23de73b88cSMauro Carvalho Chehab #include "cec-pin-priv.h"
24de73b88cSMauro Carvalho Chehab
cec_devnode_data(struct file * filp)25de73b88cSMauro Carvalho Chehab static inline struct cec_devnode *cec_devnode_data(struct file *filp)
26de73b88cSMauro Carvalho Chehab {
27de73b88cSMauro Carvalho Chehab struct cec_fh *fh = filp->private_data;
28de73b88cSMauro Carvalho Chehab
29de73b88cSMauro Carvalho Chehab return &fh->adap->devnode;
30de73b88cSMauro Carvalho Chehab }
31de73b88cSMauro Carvalho Chehab
32de73b88cSMauro Carvalho Chehab /* CEC file operations */
33de73b88cSMauro Carvalho Chehab
cec_poll(struct file * filp,struct poll_table_struct * poll)34de73b88cSMauro Carvalho Chehab static __poll_t cec_poll(struct file *filp,
35de73b88cSMauro Carvalho Chehab struct poll_table_struct *poll)
36de73b88cSMauro Carvalho Chehab {
37de73b88cSMauro Carvalho Chehab struct cec_fh *fh = filp->private_data;
38de73b88cSMauro Carvalho Chehab struct cec_adapter *adap = fh->adap;
39de73b88cSMauro Carvalho Chehab __poll_t res = 0;
40de73b88cSMauro Carvalho Chehab
41de73b88cSMauro Carvalho Chehab poll_wait(filp, &fh->wait, poll);
42de73b88cSMauro Carvalho Chehab if (!cec_is_registered(adap))
434f20b7beSHans Verkuil return EPOLLERR | EPOLLHUP | EPOLLPRI;
44de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
45de73b88cSMauro Carvalho Chehab if (adap->is_configured &&
46de73b88cSMauro Carvalho Chehab adap->transmit_queue_sz < CEC_MAX_MSG_TX_QUEUE_SZ)
47de73b88cSMauro Carvalho Chehab res |= EPOLLOUT | EPOLLWRNORM;
48de73b88cSMauro Carvalho Chehab if (fh->queued_msgs)
49de73b88cSMauro Carvalho Chehab res |= EPOLLIN | EPOLLRDNORM;
50de73b88cSMauro Carvalho Chehab if (fh->total_queued_events)
51de73b88cSMauro Carvalho Chehab res |= EPOLLPRI;
52de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
53de73b88cSMauro Carvalho Chehab return res;
54de73b88cSMauro Carvalho Chehab }
55de73b88cSMauro Carvalho Chehab
cec_is_busy(const struct cec_adapter * adap,const struct cec_fh * fh)56de73b88cSMauro Carvalho Chehab static bool cec_is_busy(const struct cec_adapter *adap,
57de73b88cSMauro Carvalho Chehab const struct cec_fh *fh)
58de73b88cSMauro Carvalho Chehab {
59de73b88cSMauro Carvalho Chehab bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
60de73b88cSMauro Carvalho Chehab bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
61de73b88cSMauro Carvalho Chehab
62de73b88cSMauro Carvalho Chehab /*
63de73b88cSMauro Carvalho Chehab * Exclusive initiators and followers can always access the CEC adapter
64de73b88cSMauro Carvalho Chehab */
65de73b88cSMauro Carvalho Chehab if (valid_initiator || valid_follower)
66de73b88cSMauro Carvalho Chehab return false;
67de73b88cSMauro Carvalho Chehab /*
68de73b88cSMauro Carvalho Chehab * All others can only access the CEC adapter if there is no
69de73b88cSMauro Carvalho Chehab * exclusive initiator and they are in INITIATOR mode.
70de73b88cSMauro Carvalho Chehab */
71de73b88cSMauro Carvalho Chehab return adap->cec_initiator ||
72de73b88cSMauro Carvalho Chehab fh->mode_initiator == CEC_MODE_NO_INITIATOR;
73de73b88cSMauro Carvalho Chehab }
74de73b88cSMauro Carvalho Chehab
cec_adap_g_caps(struct cec_adapter * adap,struct cec_caps __user * parg)75de73b88cSMauro Carvalho Chehab static long cec_adap_g_caps(struct cec_adapter *adap,
76de73b88cSMauro Carvalho Chehab struct cec_caps __user *parg)
77de73b88cSMauro Carvalho Chehab {
78de73b88cSMauro Carvalho Chehab struct cec_caps caps = {};
79de73b88cSMauro Carvalho Chehab
80de73b88cSMauro Carvalho Chehab strscpy(caps.driver, adap->devnode.dev.parent->driver->name,
81de73b88cSMauro Carvalho Chehab sizeof(caps.driver));
82de73b88cSMauro Carvalho Chehab strscpy(caps.name, adap->name, sizeof(caps.name));
83de73b88cSMauro Carvalho Chehab caps.available_log_addrs = adap->available_log_addrs;
84de73b88cSMauro Carvalho Chehab caps.capabilities = adap->capabilities;
85de73b88cSMauro Carvalho Chehab caps.version = LINUX_VERSION_CODE;
86de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &caps, sizeof(caps)))
87de73b88cSMauro Carvalho Chehab return -EFAULT;
88de73b88cSMauro Carvalho Chehab return 0;
89de73b88cSMauro Carvalho Chehab }
90de73b88cSMauro Carvalho Chehab
cec_adap_g_phys_addr(struct cec_adapter * adap,__u16 __user * parg)91de73b88cSMauro Carvalho Chehab static long cec_adap_g_phys_addr(struct cec_adapter *adap,
92de73b88cSMauro Carvalho Chehab __u16 __user *parg)
93de73b88cSMauro Carvalho Chehab {
94de73b88cSMauro Carvalho Chehab u16 phys_addr;
95de73b88cSMauro Carvalho Chehab
96de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
97de73b88cSMauro Carvalho Chehab phys_addr = adap->phys_addr;
98de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
99de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &phys_addr, sizeof(phys_addr)))
100de73b88cSMauro Carvalho Chehab return -EFAULT;
101de73b88cSMauro Carvalho Chehab return 0;
102de73b88cSMauro Carvalho Chehab }
103de73b88cSMauro Carvalho Chehab
cec_validate_phys_addr(u16 phys_addr)104de73b88cSMauro Carvalho Chehab static int cec_validate_phys_addr(u16 phys_addr)
105de73b88cSMauro Carvalho Chehab {
106de73b88cSMauro Carvalho Chehab int i;
107de73b88cSMauro Carvalho Chehab
108de73b88cSMauro Carvalho Chehab if (phys_addr == CEC_PHYS_ADDR_INVALID)
109de73b88cSMauro Carvalho Chehab return 0;
110de73b88cSMauro Carvalho Chehab for (i = 0; i < 16; i += 4)
111de73b88cSMauro Carvalho Chehab if (phys_addr & (0xf << i))
112de73b88cSMauro Carvalho Chehab break;
113de73b88cSMauro Carvalho Chehab if (i == 16)
114de73b88cSMauro Carvalho Chehab return 0;
115de73b88cSMauro Carvalho Chehab for (i += 4; i < 16; i += 4)
116de73b88cSMauro Carvalho Chehab if ((phys_addr & (0xf << i)) == 0)
117de73b88cSMauro Carvalho Chehab return -EINVAL;
118de73b88cSMauro Carvalho Chehab return 0;
119de73b88cSMauro Carvalho Chehab }
120de73b88cSMauro Carvalho Chehab
cec_adap_s_phys_addr(struct cec_adapter * adap,struct cec_fh * fh,bool block,__u16 __user * parg)121de73b88cSMauro Carvalho Chehab static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
122de73b88cSMauro Carvalho Chehab bool block, __u16 __user *parg)
123de73b88cSMauro Carvalho Chehab {
124de73b88cSMauro Carvalho Chehab u16 phys_addr;
125de73b88cSMauro Carvalho Chehab long err;
126de73b88cSMauro Carvalho Chehab
127de73b88cSMauro Carvalho Chehab if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
128de73b88cSMauro Carvalho Chehab return -ENOTTY;
129de73b88cSMauro Carvalho Chehab if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
130de73b88cSMauro Carvalho Chehab return -EFAULT;
131de73b88cSMauro Carvalho Chehab
132de73b88cSMauro Carvalho Chehab err = cec_validate_phys_addr(phys_addr);
133de73b88cSMauro Carvalho Chehab if (err)
134de73b88cSMauro Carvalho Chehab return err;
135de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
136de73b88cSMauro Carvalho Chehab if (cec_is_busy(adap, fh))
137de73b88cSMauro Carvalho Chehab err = -EBUSY;
138de73b88cSMauro Carvalho Chehab else
139de73b88cSMauro Carvalho Chehab __cec_s_phys_addr(adap, phys_addr, block);
140de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
141de73b88cSMauro Carvalho Chehab return err;
142de73b88cSMauro Carvalho Chehab }
143de73b88cSMauro Carvalho Chehab
cec_adap_g_log_addrs(struct cec_adapter * adap,struct cec_log_addrs __user * parg)144de73b88cSMauro Carvalho Chehab static long cec_adap_g_log_addrs(struct cec_adapter *adap,
145de73b88cSMauro Carvalho Chehab struct cec_log_addrs __user *parg)
146de73b88cSMauro Carvalho Chehab {
147de73b88cSMauro Carvalho Chehab struct cec_log_addrs log_addrs;
148de73b88cSMauro Carvalho Chehab
149de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
1506c42227cSHans Verkuil /*
1516c42227cSHans Verkuil * We use memcpy here instead of assignment since there is a
1526c42227cSHans Verkuil * hole at the end of struct cec_log_addrs that an assignment
1536c42227cSHans Verkuil * might ignore. So when we do copy_to_user() we could leak
1546c42227cSHans Verkuil * one byte of memory.
1556c42227cSHans Verkuil */
1566c42227cSHans Verkuil memcpy(&log_addrs, &adap->log_addrs, sizeof(log_addrs));
157de73b88cSMauro Carvalho Chehab if (!adap->is_configured)
158de73b88cSMauro Carvalho Chehab memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
159de73b88cSMauro Carvalho Chehab sizeof(log_addrs.log_addr));
160de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
161de73b88cSMauro Carvalho Chehab
162de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
163de73b88cSMauro Carvalho Chehab return -EFAULT;
164de73b88cSMauro Carvalho Chehab return 0;
165de73b88cSMauro Carvalho Chehab }
166de73b88cSMauro Carvalho Chehab
cec_adap_s_log_addrs(struct cec_adapter * adap,struct cec_fh * fh,bool block,struct cec_log_addrs __user * parg)167de73b88cSMauro Carvalho Chehab static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
168de73b88cSMauro Carvalho Chehab bool block, struct cec_log_addrs __user *parg)
169de73b88cSMauro Carvalho Chehab {
170de73b88cSMauro Carvalho Chehab struct cec_log_addrs log_addrs;
171de73b88cSMauro Carvalho Chehab long err = -EBUSY;
172de73b88cSMauro Carvalho Chehab
173de73b88cSMauro Carvalho Chehab if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
174de73b88cSMauro Carvalho Chehab return -ENOTTY;
175de73b88cSMauro Carvalho Chehab if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
176de73b88cSMauro Carvalho Chehab return -EFAULT;
177de73b88cSMauro Carvalho Chehab log_addrs.flags &= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK |
178de73b88cSMauro Carvalho Chehab CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
179de73b88cSMauro Carvalho Chehab CEC_LOG_ADDRS_FL_CDC_ONLY;
180de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
181*1af4790bSHans Verkuil if (!adap->is_claiming_log_addrs && !adap->is_configuring &&
182de73b88cSMauro Carvalho Chehab (!log_addrs.num_log_addrs || !adap->is_configured) &&
183de73b88cSMauro Carvalho Chehab !cec_is_busy(adap, fh)) {
184de73b88cSMauro Carvalho Chehab err = __cec_s_log_addrs(adap, &log_addrs, block);
185de73b88cSMauro Carvalho Chehab if (!err)
186de73b88cSMauro Carvalho Chehab log_addrs = adap->log_addrs;
187de73b88cSMauro Carvalho Chehab }
188de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
189de73b88cSMauro Carvalho Chehab if (err)
190de73b88cSMauro Carvalho Chehab return err;
191de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
192de73b88cSMauro Carvalho Chehab return -EFAULT;
193de73b88cSMauro Carvalho Chehab return 0;
194de73b88cSMauro Carvalho Chehab }
195de73b88cSMauro Carvalho Chehab
cec_adap_g_connector_info(struct cec_adapter * adap,struct cec_log_addrs __user * parg)196de73b88cSMauro Carvalho Chehab static long cec_adap_g_connector_info(struct cec_adapter *adap,
197de73b88cSMauro Carvalho Chehab struct cec_log_addrs __user *parg)
198de73b88cSMauro Carvalho Chehab {
199de73b88cSMauro Carvalho Chehab int ret = 0;
200de73b88cSMauro Carvalho Chehab
201de73b88cSMauro Carvalho Chehab if (!(adap->capabilities & CEC_CAP_CONNECTOR_INFO))
202de73b88cSMauro Carvalho Chehab return -ENOTTY;
203de73b88cSMauro Carvalho Chehab
204de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
205de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &adap->conn_info, sizeof(adap->conn_info)))
206de73b88cSMauro Carvalho Chehab ret = -EFAULT;
207de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
208de73b88cSMauro Carvalho Chehab return ret;
209de73b88cSMauro Carvalho Chehab }
210de73b88cSMauro Carvalho Chehab
cec_transmit(struct cec_adapter * adap,struct cec_fh * fh,bool block,struct cec_msg __user * parg)211de73b88cSMauro Carvalho Chehab static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
212de73b88cSMauro Carvalho Chehab bool block, struct cec_msg __user *parg)
213de73b88cSMauro Carvalho Chehab {
214de73b88cSMauro Carvalho Chehab struct cec_msg msg = {};
215de73b88cSMauro Carvalho Chehab long err = 0;
216de73b88cSMauro Carvalho Chehab
217de73b88cSMauro Carvalho Chehab if (!(adap->capabilities & CEC_CAP_TRANSMIT))
218de73b88cSMauro Carvalho Chehab return -ENOTTY;
219de73b88cSMauro Carvalho Chehab if (copy_from_user(&msg, parg, sizeof(msg)))
220de73b88cSMauro Carvalho Chehab return -EFAULT;
221de73b88cSMauro Carvalho Chehab
222de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
223de73b88cSMauro Carvalho Chehab if (adap->log_addrs.num_log_addrs == 0)
224de73b88cSMauro Carvalho Chehab err = -EPERM;
225de73b88cSMauro Carvalho Chehab else if (adap->is_configuring)
226de73b88cSMauro Carvalho Chehab err = -ENONET;
227de73b88cSMauro Carvalho Chehab else if (cec_is_busy(adap, fh))
228de73b88cSMauro Carvalho Chehab err = -EBUSY;
229de73b88cSMauro Carvalho Chehab else
230de73b88cSMauro Carvalho Chehab err = cec_transmit_msg_fh(adap, &msg, fh, block);
231de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
232de73b88cSMauro Carvalho Chehab if (err)
233de73b88cSMauro Carvalho Chehab return err;
234de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &msg, sizeof(msg)))
235de73b88cSMauro Carvalho Chehab return -EFAULT;
236de73b88cSMauro Carvalho Chehab return 0;
237de73b88cSMauro Carvalho Chehab }
238de73b88cSMauro Carvalho Chehab
239de73b88cSMauro Carvalho Chehab /* Called by CEC_RECEIVE: wait for a message to arrive */
cec_receive_msg(struct cec_fh * fh,struct cec_msg * msg,bool block)240de73b88cSMauro Carvalho Chehab static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
241de73b88cSMauro Carvalho Chehab {
242de73b88cSMauro Carvalho Chehab u32 timeout = msg->timeout;
243de73b88cSMauro Carvalho Chehab int res;
244de73b88cSMauro Carvalho Chehab
245de73b88cSMauro Carvalho Chehab do {
246de73b88cSMauro Carvalho Chehab mutex_lock(&fh->lock);
247de73b88cSMauro Carvalho Chehab /* Are there received messages queued up? */
248de73b88cSMauro Carvalho Chehab if (fh->queued_msgs) {
249de73b88cSMauro Carvalho Chehab /* Yes, return the first one */
250de73b88cSMauro Carvalho Chehab struct cec_msg_entry *entry =
251de73b88cSMauro Carvalho Chehab list_first_entry(&fh->msgs,
252de73b88cSMauro Carvalho Chehab struct cec_msg_entry, list);
253de73b88cSMauro Carvalho Chehab
254de73b88cSMauro Carvalho Chehab list_del(&entry->list);
255de73b88cSMauro Carvalho Chehab *msg = entry->msg;
256de73b88cSMauro Carvalho Chehab kfree(entry);
257de73b88cSMauro Carvalho Chehab fh->queued_msgs--;
258de73b88cSMauro Carvalho Chehab mutex_unlock(&fh->lock);
259de73b88cSMauro Carvalho Chehab /* restore original timeout value */
260de73b88cSMauro Carvalho Chehab msg->timeout = timeout;
261de73b88cSMauro Carvalho Chehab return 0;
262de73b88cSMauro Carvalho Chehab }
263de73b88cSMauro Carvalho Chehab
264de73b88cSMauro Carvalho Chehab /* No, return EAGAIN in non-blocking mode or wait */
265de73b88cSMauro Carvalho Chehab mutex_unlock(&fh->lock);
266de73b88cSMauro Carvalho Chehab
267de73b88cSMauro Carvalho Chehab /* Return when in non-blocking mode */
268de73b88cSMauro Carvalho Chehab if (!block)
269de73b88cSMauro Carvalho Chehab return -EAGAIN;
270de73b88cSMauro Carvalho Chehab
271de73b88cSMauro Carvalho Chehab if (msg->timeout) {
272de73b88cSMauro Carvalho Chehab /* The user specified a timeout */
273de73b88cSMauro Carvalho Chehab res = wait_event_interruptible_timeout(fh->wait,
274de73b88cSMauro Carvalho Chehab fh->queued_msgs,
275de73b88cSMauro Carvalho Chehab msecs_to_jiffies(msg->timeout));
276de73b88cSMauro Carvalho Chehab if (res == 0)
277de73b88cSMauro Carvalho Chehab res = -ETIMEDOUT;
278de73b88cSMauro Carvalho Chehab else if (res > 0)
279de73b88cSMauro Carvalho Chehab res = 0;
280de73b88cSMauro Carvalho Chehab } else {
281de73b88cSMauro Carvalho Chehab /* Wait indefinitely */
282de73b88cSMauro Carvalho Chehab res = wait_event_interruptible(fh->wait,
283de73b88cSMauro Carvalho Chehab fh->queued_msgs);
284de73b88cSMauro Carvalho Chehab }
285de73b88cSMauro Carvalho Chehab /* Exit on error, otherwise loop to get the new message */
286de73b88cSMauro Carvalho Chehab } while (!res);
287de73b88cSMauro Carvalho Chehab return res;
288de73b88cSMauro Carvalho Chehab }
289de73b88cSMauro Carvalho Chehab
cec_receive(struct cec_adapter * adap,struct cec_fh * fh,bool block,struct cec_msg __user * parg)290de73b88cSMauro Carvalho Chehab static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
291de73b88cSMauro Carvalho Chehab bool block, struct cec_msg __user *parg)
292de73b88cSMauro Carvalho Chehab {
293de73b88cSMauro Carvalho Chehab struct cec_msg msg = {};
294de73b88cSMauro Carvalho Chehab long err;
295de73b88cSMauro Carvalho Chehab
296de73b88cSMauro Carvalho Chehab if (copy_from_user(&msg, parg, sizeof(msg)))
297de73b88cSMauro Carvalho Chehab return -EFAULT;
298de73b88cSMauro Carvalho Chehab
299de73b88cSMauro Carvalho Chehab err = cec_receive_msg(fh, &msg, block);
300de73b88cSMauro Carvalho Chehab if (err)
301de73b88cSMauro Carvalho Chehab return err;
302de73b88cSMauro Carvalho Chehab msg.flags = 0;
303de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &msg, sizeof(msg)))
304de73b88cSMauro Carvalho Chehab return -EFAULT;
305de73b88cSMauro Carvalho Chehab return 0;
306de73b88cSMauro Carvalho Chehab }
307de73b88cSMauro Carvalho Chehab
cec_dqevent(struct cec_adapter * adap,struct cec_fh * fh,bool block,struct cec_event __user * parg)308de73b88cSMauro Carvalho Chehab static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
309de73b88cSMauro Carvalho Chehab bool block, struct cec_event __user *parg)
310de73b88cSMauro Carvalho Chehab {
311de73b88cSMauro Carvalho Chehab struct cec_event_entry *ev = NULL;
312de73b88cSMauro Carvalho Chehab u64 ts = ~0ULL;
313de73b88cSMauro Carvalho Chehab unsigned int i;
314de73b88cSMauro Carvalho Chehab unsigned int ev_idx;
315de73b88cSMauro Carvalho Chehab long err = 0;
316de73b88cSMauro Carvalho Chehab
317de73b88cSMauro Carvalho Chehab mutex_lock(&fh->lock);
318de73b88cSMauro Carvalho Chehab while (!fh->total_queued_events && block) {
319de73b88cSMauro Carvalho Chehab mutex_unlock(&fh->lock);
320de73b88cSMauro Carvalho Chehab err = wait_event_interruptible(fh->wait,
321de73b88cSMauro Carvalho Chehab fh->total_queued_events);
322de73b88cSMauro Carvalho Chehab if (err)
323de73b88cSMauro Carvalho Chehab return err;
324de73b88cSMauro Carvalho Chehab mutex_lock(&fh->lock);
325de73b88cSMauro Carvalho Chehab }
326de73b88cSMauro Carvalho Chehab
327de73b88cSMauro Carvalho Chehab /* Find the oldest event */
328de73b88cSMauro Carvalho Chehab for (i = 0; i < CEC_NUM_EVENTS; i++) {
329de73b88cSMauro Carvalho Chehab struct cec_event_entry *entry =
330de73b88cSMauro Carvalho Chehab list_first_entry_or_null(&fh->events[i],
331de73b88cSMauro Carvalho Chehab struct cec_event_entry, list);
332de73b88cSMauro Carvalho Chehab
333de73b88cSMauro Carvalho Chehab if (entry && entry->ev.ts <= ts) {
334de73b88cSMauro Carvalho Chehab ev = entry;
335de73b88cSMauro Carvalho Chehab ev_idx = i;
336de73b88cSMauro Carvalho Chehab ts = ev->ev.ts;
337de73b88cSMauro Carvalho Chehab }
338de73b88cSMauro Carvalho Chehab }
339de73b88cSMauro Carvalho Chehab
340de73b88cSMauro Carvalho Chehab if (!ev) {
341de73b88cSMauro Carvalho Chehab err = -EAGAIN;
342de73b88cSMauro Carvalho Chehab goto unlock;
343de73b88cSMauro Carvalho Chehab }
344de73b88cSMauro Carvalho Chehab list_del(&ev->list);
345de73b88cSMauro Carvalho Chehab
346de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &ev->ev, sizeof(ev->ev)))
347de73b88cSMauro Carvalho Chehab err = -EFAULT;
348de73b88cSMauro Carvalho Chehab if (ev_idx >= CEC_NUM_CORE_EVENTS)
349de73b88cSMauro Carvalho Chehab kfree(ev);
350de73b88cSMauro Carvalho Chehab fh->queued_events[ev_idx]--;
351de73b88cSMauro Carvalho Chehab fh->total_queued_events--;
352de73b88cSMauro Carvalho Chehab
353de73b88cSMauro Carvalho Chehab unlock:
354de73b88cSMauro Carvalho Chehab mutex_unlock(&fh->lock);
355de73b88cSMauro Carvalho Chehab return err;
356de73b88cSMauro Carvalho Chehab }
357de73b88cSMauro Carvalho Chehab
cec_g_mode(struct cec_adapter * adap,struct cec_fh * fh,u32 __user * parg)358de73b88cSMauro Carvalho Chehab static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh,
359de73b88cSMauro Carvalho Chehab u32 __user *parg)
360de73b88cSMauro Carvalho Chehab {
361de73b88cSMauro Carvalho Chehab u32 mode = fh->mode_initiator | fh->mode_follower;
362de73b88cSMauro Carvalho Chehab
363de73b88cSMauro Carvalho Chehab if (copy_to_user(parg, &mode, sizeof(mode)))
364de73b88cSMauro Carvalho Chehab return -EFAULT;
365de73b88cSMauro Carvalho Chehab return 0;
366de73b88cSMauro Carvalho Chehab }
367de73b88cSMauro Carvalho Chehab
cec_s_mode(struct cec_adapter * adap,struct cec_fh * fh,u32 __user * parg)368de73b88cSMauro Carvalho Chehab static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh,
369de73b88cSMauro Carvalho Chehab u32 __user *parg)
370de73b88cSMauro Carvalho Chehab {
371de73b88cSMauro Carvalho Chehab u32 mode;
372de73b88cSMauro Carvalho Chehab u8 mode_initiator;
373de73b88cSMauro Carvalho Chehab u8 mode_follower;
374de73b88cSMauro Carvalho Chehab bool send_pin_event = false;
375de73b88cSMauro Carvalho Chehab long err = 0;
376de73b88cSMauro Carvalho Chehab
377de73b88cSMauro Carvalho Chehab if (copy_from_user(&mode, parg, sizeof(mode)))
378de73b88cSMauro Carvalho Chehab return -EFAULT;
379de73b88cSMauro Carvalho Chehab if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK)) {
380de73b88cSMauro Carvalho Chehab dprintk(1, "%s: invalid mode bits set\n", __func__);
381de73b88cSMauro Carvalho Chehab return -EINVAL;
382de73b88cSMauro Carvalho Chehab }
383de73b88cSMauro Carvalho Chehab
384de73b88cSMauro Carvalho Chehab mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
385de73b88cSMauro Carvalho Chehab mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
386de73b88cSMauro Carvalho Chehab
387de73b88cSMauro Carvalho Chehab if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
388de73b88cSMauro Carvalho Chehab mode_follower > CEC_MODE_MONITOR_ALL) {
389de73b88cSMauro Carvalho Chehab dprintk(1, "%s: unknown mode\n", __func__);
390de73b88cSMauro Carvalho Chehab return -EINVAL;
391de73b88cSMauro Carvalho Chehab }
392de73b88cSMauro Carvalho Chehab
393de73b88cSMauro Carvalho Chehab if (mode_follower == CEC_MODE_MONITOR_ALL &&
394de73b88cSMauro Carvalho Chehab !(adap->capabilities & CEC_CAP_MONITOR_ALL)) {
395de73b88cSMauro Carvalho Chehab dprintk(1, "%s: MONITOR_ALL not supported\n", __func__);
396de73b88cSMauro Carvalho Chehab return -EINVAL;
397de73b88cSMauro Carvalho Chehab }
398de73b88cSMauro Carvalho Chehab
399de73b88cSMauro Carvalho Chehab if (mode_follower == CEC_MODE_MONITOR_PIN &&
400de73b88cSMauro Carvalho Chehab !(adap->capabilities & CEC_CAP_MONITOR_PIN)) {
401de73b88cSMauro Carvalho Chehab dprintk(1, "%s: MONITOR_PIN not supported\n", __func__);
402de73b88cSMauro Carvalho Chehab return -EINVAL;
403de73b88cSMauro Carvalho Chehab }
404de73b88cSMauro Carvalho Chehab
405de73b88cSMauro Carvalho Chehab /* Follower modes should always be able to send CEC messages */
406de73b88cSMauro Carvalho Chehab if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
407de73b88cSMauro Carvalho Chehab !(adap->capabilities & CEC_CAP_TRANSMIT)) &&
408de73b88cSMauro Carvalho Chehab mode_follower >= CEC_MODE_FOLLOWER &&
409de73b88cSMauro Carvalho Chehab mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
410de73b88cSMauro Carvalho Chehab dprintk(1, "%s: cannot transmit\n", __func__);
411de73b88cSMauro Carvalho Chehab return -EINVAL;
412de73b88cSMauro Carvalho Chehab }
413de73b88cSMauro Carvalho Chehab
414de73b88cSMauro Carvalho Chehab /* Monitor modes require CEC_MODE_NO_INITIATOR */
415de73b88cSMauro Carvalho Chehab if (mode_initiator && mode_follower >= CEC_MODE_MONITOR_PIN) {
416de73b88cSMauro Carvalho Chehab dprintk(1, "%s: monitor modes require NO_INITIATOR\n",
417de73b88cSMauro Carvalho Chehab __func__);
418de73b88cSMauro Carvalho Chehab return -EINVAL;
419de73b88cSMauro Carvalho Chehab }
420de73b88cSMauro Carvalho Chehab
421de73b88cSMauro Carvalho Chehab /* Monitor modes require CAP_NET_ADMIN */
422de73b88cSMauro Carvalho Chehab if (mode_follower >= CEC_MODE_MONITOR_PIN && !capable(CAP_NET_ADMIN))
423de73b88cSMauro Carvalho Chehab return -EPERM;
424de73b88cSMauro Carvalho Chehab
425de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
426de73b88cSMauro Carvalho Chehab /*
427de73b88cSMauro Carvalho Chehab * You can't become exclusive follower if someone else already
428de73b88cSMauro Carvalho Chehab * has that job.
429de73b88cSMauro Carvalho Chehab */
430de73b88cSMauro Carvalho Chehab if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
431de73b88cSMauro Carvalho Chehab mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
432de73b88cSMauro Carvalho Chehab adap->cec_follower && adap->cec_follower != fh)
433de73b88cSMauro Carvalho Chehab err = -EBUSY;
434de73b88cSMauro Carvalho Chehab /*
435de73b88cSMauro Carvalho Chehab * You can't become exclusive initiator if someone else already
436de73b88cSMauro Carvalho Chehab * has that job.
437de73b88cSMauro Carvalho Chehab */
438de73b88cSMauro Carvalho Chehab if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
439de73b88cSMauro Carvalho Chehab adap->cec_initiator && adap->cec_initiator != fh)
440de73b88cSMauro Carvalho Chehab err = -EBUSY;
441de73b88cSMauro Carvalho Chehab
442de73b88cSMauro Carvalho Chehab if (!err) {
443de73b88cSMauro Carvalho Chehab bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
444de73b88cSMauro Carvalho Chehab bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
445de73b88cSMauro Carvalho Chehab
446de73b88cSMauro Carvalho Chehab if (old_mon_all != new_mon_all) {
447de73b88cSMauro Carvalho Chehab if (new_mon_all)
448de73b88cSMauro Carvalho Chehab err = cec_monitor_all_cnt_inc(adap);
449de73b88cSMauro Carvalho Chehab else
450de73b88cSMauro Carvalho Chehab cec_monitor_all_cnt_dec(adap);
451de73b88cSMauro Carvalho Chehab }
452de73b88cSMauro Carvalho Chehab }
453de73b88cSMauro Carvalho Chehab
454de73b88cSMauro Carvalho Chehab if (!err) {
455de73b88cSMauro Carvalho Chehab bool old_mon_pin = fh->mode_follower == CEC_MODE_MONITOR_PIN;
456de73b88cSMauro Carvalho Chehab bool new_mon_pin = mode_follower == CEC_MODE_MONITOR_PIN;
457de73b88cSMauro Carvalho Chehab
458de73b88cSMauro Carvalho Chehab if (old_mon_pin != new_mon_pin) {
459de73b88cSMauro Carvalho Chehab send_pin_event = new_mon_pin;
460de73b88cSMauro Carvalho Chehab if (new_mon_pin)
461de73b88cSMauro Carvalho Chehab err = cec_monitor_pin_cnt_inc(adap);
462de73b88cSMauro Carvalho Chehab else
463de73b88cSMauro Carvalho Chehab cec_monitor_pin_cnt_dec(adap);
464de73b88cSMauro Carvalho Chehab }
465de73b88cSMauro Carvalho Chehab }
466de73b88cSMauro Carvalho Chehab
467de73b88cSMauro Carvalho Chehab if (err) {
468de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
469de73b88cSMauro Carvalho Chehab return err;
470de73b88cSMauro Carvalho Chehab }
471de73b88cSMauro Carvalho Chehab
472de73b88cSMauro Carvalho Chehab if (fh->mode_follower == CEC_MODE_FOLLOWER)
473de73b88cSMauro Carvalho Chehab adap->follower_cnt--;
474de73b88cSMauro Carvalho Chehab if (mode_follower == CEC_MODE_FOLLOWER)
475de73b88cSMauro Carvalho Chehab adap->follower_cnt++;
476de73b88cSMauro Carvalho Chehab if (send_pin_event) {
477de73b88cSMauro Carvalho Chehab struct cec_event ev = {
478de73b88cSMauro Carvalho Chehab .flags = CEC_EVENT_FL_INITIAL_STATE,
479de73b88cSMauro Carvalho Chehab };
480de73b88cSMauro Carvalho Chehab
481de73b88cSMauro Carvalho Chehab ev.event = adap->cec_pin_is_high ? CEC_EVENT_PIN_CEC_HIGH :
482de73b88cSMauro Carvalho Chehab CEC_EVENT_PIN_CEC_LOW;
483de73b88cSMauro Carvalho Chehab cec_queue_event_fh(fh, &ev, 0);
484de73b88cSMauro Carvalho Chehab }
485de73b88cSMauro Carvalho Chehab if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
486de73b88cSMauro Carvalho Chehab mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
487de73b88cSMauro Carvalho Chehab adap->passthrough =
488de73b88cSMauro Carvalho Chehab mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
489de73b88cSMauro Carvalho Chehab adap->cec_follower = fh;
490de73b88cSMauro Carvalho Chehab } else if (adap->cec_follower == fh) {
491de73b88cSMauro Carvalho Chehab adap->passthrough = false;
492de73b88cSMauro Carvalho Chehab adap->cec_follower = NULL;
493de73b88cSMauro Carvalho Chehab }
494de73b88cSMauro Carvalho Chehab if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
495de73b88cSMauro Carvalho Chehab adap->cec_initiator = fh;
496de73b88cSMauro Carvalho Chehab else if (adap->cec_initiator == fh)
497de73b88cSMauro Carvalho Chehab adap->cec_initiator = NULL;
498de73b88cSMauro Carvalho Chehab fh->mode_initiator = mode_initiator;
499de73b88cSMauro Carvalho Chehab fh->mode_follower = mode_follower;
500de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
501de73b88cSMauro Carvalho Chehab return 0;
502de73b88cSMauro Carvalho Chehab }
503de73b88cSMauro Carvalho Chehab
cec_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)504de73b88cSMauro Carvalho Chehab static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
505de73b88cSMauro Carvalho Chehab {
506de73b88cSMauro Carvalho Chehab struct cec_fh *fh = filp->private_data;
507de73b88cSMauro Carvalho Chehab struct cec_adapter *adap = fh->adap;
508de73b88cSMauro Carvalho Chehab bool block = !(filp->f_flags & O_NONBLOCK);
509de73b88cSMauro Carvalho Chehab void __user *parg = (void __user *)arg;
510de73b88cSMauro Carvalho Chehab
511de73b88cSMauro Carvalho Chehab if (!cec_is_registered(adap))
512de73b88cSMauro Carvalho Chehab return -ENODEV;
513de73b88cSMauro Carvalho Chehab
514de73b88cSMauro Carvalho Chehab switch (cmd) {
515de73b88cSMauro Carvalho Chehab case CEC_ADAP_G_CAPS:
516de73b88cSMauro Carvalho Chehab return cec_adap_g_caps(adap, parg);
517de73b88cSMauro Carvalho Chehab
518de73b88cSMauro Carvalho Chehab case CEC_ADAP_G_PHYS_ADDR:
519de73b88cSMauro Carvalho Chehab return cec_adap_g_phys_addr(adap, parg);
520de73b88cSMauro Carvalho Chehab
521de73b88cSMauro Carvalho Chehab case CEC_ADAP_S_PHYS_ADDR:
522de73b88cSMauro Carvalho Chehab return cec_adap_s_phys_addr(adap, fh, block, parg);
523de73b88cSMauro Carvalho Chehab
524de73b88cSMauro Carvalho Chehab case CEC_ADAP_G_LOG_ADDRS:
525de73b88cSMauro Carvalho Chehab return cec_adap_g_log_addrs(adap, parg);
526de73b88cSMauro Carvalho Chehab
527de73b88cSMauro Carvalho Chehab case CEC_ADAP_S_LOG_ADDRS:
528de73b88cSMauro Carvalho Chehab return cec_adap_s_log_addrs(adap, fh, block, parg);
529de73b88cSMauro Carvalho Chehab
530de73b88cSMauro Carvalho Chehab case CEC_ADAP_G_CONNECTOR_INFO:
531de73b88cSMauro Carvalho Chehab return cec_adap_g_connector_info(adap, parg);
532de73b88cSMauro Carvalho Chehab
533de73b88cSMauro Carvalho Chehab case CEC_TRANSMIT:
534de73b88cSMauro Carvalho Chehab return cec_transmit(adap, fh, block, parg);
535de73b88cSMauro Carvalho Chehab
536de73b88cSMauro Carvalho Chehab case CEC_RECEIVE:
537de73b88cSMauro Carvalho Chehab return cec_receive(adap, fh, block, parg);
538de73b88cSMauro Carvalho Chehab
539de73b88cSMauro Carvalho Chehab case CEC_DQEVENT:
540de73b88cSMauro Carvalho Chehab return cec_dqevent(adap, fh, block, parg);
541de73b88cSMauro Carvalho Chehab
542de73b88cSMauro Carvalho Chehab case CEC_G_MODE:
543de73b88cSMauro Carvalho Chehab return cec_g_mode(adap, fh, parg);
544de73b88cSMauro Carvalho Chehab
545de73b88cSMauro Carvalho Chehab case CEC_S_MODE:
546de73b88cSMauro Carvalho Chehab return cec_s_mode(adap, fh, parg);
547de73b88cSMauro Carvalho Chehab
548de73b88cSMauro Carvalho Chehab default:
549de73b88cSMauro Carvalho Chehab return -ENOTTY;
550de73b88cSMauro Carvalho Chehab }
551de73b88cSMauro Carvalho Chehab }
552de73b88cSMauro Carvalho Chehab
cec_open(struct inode * inode,struct file * filp)553de73b88cSMauro Carvalho Chehab static int cec_open(struct inode *inode, struct file *filp)
554de73b88cSMauro Carvalho Chehab {
555de73b88cSMauro Carvalho Chehab struct cec_devnode *devnode =
556de73b88cSMauro Carvalho Chehab container_of(inode->i_cdev, struct cec_devnode, cdev);
557de73b88cSMauro Carvalho Chehab struct cec_adapter *adap = to_cec_adapter(devnode);
558de73b88cSMauro Carvalho Chehab struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
559de73b88cSMauro Carvalho Chehab /*
560de73b88cSMauro Carvalho Chehab * Initial events that are automatically sent when the cec device is
561de73b88cSMauro Carvalho Chehab * opened.
562de73b88cSMauro Carvalho Chehab */
563de73b88cSMauro Carvalho Chehab struct cec_event ev = {
564de73b88cSMauro Carvalho Chehab .event = CEC_EVENT_STATE_CHANGE,
565de73b88cSMauro Carvalho Chehab .flags = CEC_EVENT_FL_INITIAL_STATE,
566de73b88cSMauro Carvalho Chehab };
567de73b88cSMauro Carvalho Chehab unsigned int i;
568de73b88cSMauro Carvalho Chehab int err;
569de73b88cSMauro Carvalho Chehab
570de73b88cSMauro Carvalho Chehab if (!fh)
571de73b88cSMauro Carvalho Chehab return -ENOMEM;
572de73b88cSMauro Carvalho Chehab
573de73b88cSMauro Carvalho Chehab INIT_LIST_HEAD(&fh->msgs);
574de73b88cSMauro Carvalho Chehab INIT_LIST_HEAD(&fh->xfer_list);
575de73b88cSMauro Carvalho Chehab for (i = 0; i < CEC_NUM_EVENTS; i++)
576de73b88cSMauro Carvalho Chehab INIT_LIST_HEAD(&fh->events[i]);
577de73b88cSMauro Carvalho Chehab mutex_init(&fh->lock);
578de73b88cSMauro Carvalho Chehab init_waitqueue_head(&fh->wait);
579de73b88cSMauro Carvalho Chehab
580de73b88cSMauro Carvalho Chehab fh->mode_initiator = CEC_MODE_INITIATOR;
581de73b88cSMauro Carvalho Chehab fh->adap = adap;
582de73b88cSMauro Carvalho Chehab
583de73b88cSMauro Carvalho Chehab err = cec_get_device(devnode);
584de73b88cSMauro Carvalho Chehab if (err) {
585de73b88cSMauro Carvalho Chehab kfree(fh);
586de73b88cSMauro Carvalho Chehab return err;
587de73b88cSMauro Carvalho Chehab }
588de73b88cSMauro Carvalho Chehab
589de73b88cSMauro Carvalho Chehab filp->private_data = fh;
590de73b88cSMauro Carvalho Chehab
591de73b88cSMauro Carvalho Chehab /* Queue up initial state events */
592de73b88cSMauro Carvalho Chehab ev.state_change.phys_addr = adap->phys_addr;
593de73b88cSMauro Carvalho Chehab ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
594de73b88cSMauro Carvalho Chehab ev.state_change.have_conn_info =
595de73b88cSMauro Carvalho Chehab adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR;
596de73b88cSMauro Carvalho Chehab cec_queue_event_fh(fh, &ev, 0);
597de73b88cSMauro Carvalho Chehab #ifdef CONFIG_CEC_PIN
598e2ed5024SHans Verkuil if (adap->pin && adap->pin->ops->read_hpd &&
599e2ed5024SHans Verkuil !adap->devnode.unregistered) {
600de73b88cSMauro Carvalho Chehab err = adap->pin->ops->read_hpd(adap);
601de73b88cSMauro Carvalho Chehab if (err >= 0) {
602de73b88cSMauro Carvalho Chehab ev.event = err ? CEC_EVENT_PIN_HPD_HIGH :
603de73b88cSMauro Carvalho Chehab CEC_EVENT_PIN_HPD_LOW;
604de73b88cSMauro Carvalho Chehab cec_queue_event_fh(fh, &ev, 0);
605de73b88cSMauro Carvalho Chehab }
606de73b88cSMauro Carvalho Chehab }
607e2ed5024SHans Verkuil if (adap->pin && adap->pin->ops->read_5v &&
608e2ed5024SHans Verkuil !adap->devnode.unregistered) {
609de73b88cSMauro Carvalho Chehab err = adap->pin->ops->read_5v(adap);
610de73b88cSMauro Carvalho Chehab if (err >= 0) {
611de73b88cSMauro Carvalho Chehab ev.event = err ? CEC_EVENT_PIN_5V_HIGH :
612de73b88cSMauro Carvalho Chehab CEC_EVENT_PIN_5V_LOW;
613de73b88cSMauro Carvalho Chehab cec_queue_event_fh(fh, &ev, 0);
614de73b88cSMauro Carvalho Chehab }
615de73b88cSMauro Carvalho Chehab }
616de73b88cSMauro Carvalho Chehab #endif
617de73b88cSMauro Carvalho Chehab
6183813c932SHans Verkuil mutex_lock(&devnode->lock);
619a9e61076SHans Verkuil mutex_lock(&devnode->lock_fhs);
620de73b88cSMauro Carvalho Chehab list_add(&fh->list, &devnode->fhs);
621a9e61076SHans Verkuil mutex_unlock(&devnode->lock_fhs);
622de73b88cSMauro Carvalho Chehab mutex_unlock(&devnode->lock);
623de73b88cSMauro Carvalho Chehab
624de73b88cSMauro Carvalho Chehab return 0;
625de73b88cSMauro Carvalho Chehab }
626de73b88cSMauro Carvalho Chehab
627de73b88cSMauro Carvalho Chehab /* Override for the release function */
cec_release(struct inode * inode,struct file * filp)628de73b88cSMauro Carvalho Chehab static int cec_release(struct inode *inode, struct file *filp)
629de73b88cSMauro Carvalho Chehab {
630de73b88cSMauro Carvalho Chehab struct cec_devnode *devnode = cec_devnode_data(filp);
631de73b88cSMauro Carvalho Chehab struct cec_adapter *adap = to_cec_adapter(devnode);
632de73b88cSMauro Carvalho Chehab struct cec_fh *fh = filp->private_data;
633de73b88cSMauro Carvalho Chehab unsigned int i;
634de73b88cSMauro Carvalho Chehab
635de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
636de73b88cSMauro Carvalho Chehab if (adap->cec_initiator == fh)
637de73b88cSMauro Carvalho Chehab adap->cec_initiator = NULL;
638de73b88cSMauro Carvalho Chehab if (adap->cec_follower == fh) {
639de73b88cSMauro Carvalho Chehab adap->cec_follower = NULL;
640de73b88cSMauro Carvalho Chehab adap->passthrough = false;
641de73b88cSMauro Carvalho Chehab }
642de73b88cSMauro Carvalho Chehab if (fh->mode_follower == CEC_MODE_FOLLOWER)
643de73b88cSMauro Carvalho Chehab adap->follower_cnt--;
644de73b88cSMauro Carvalho Chehab if (fh->mode_follower == CEC_MODE_MONITOR_PIN)
645de73b88cSMauro Carvalho Chehab cec_monitor_pin_cnt_dec(adap);
646de73b88cSMauro Carvalho Chehab if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
647de73b88cSMauro Carvalho Chehab cec_monitor_all_cnt_dec(adap);
648de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
649de73b88cSMauro Carvalho Chehab
650de73b88cSMauro Carvalho Chehab mutex_lock(&devnode->lock);
651a9e61076SHans Verkuil mutex_lock(&devnode->lock_fhs);
652de73b88cSMauro Carvalho Chehab list_del(&fh->list);
653a9e61076SHans Verkuil mutex_unlock(&devnode->lock_fhs);
654de73b88cSMauro Carvalho Chehab mutex_unlock(&devnode->lock);
655de73b88cSMauro Carvalho Chehab
656de73b88cSMauro Carvalho Chehab /* Unhook pending transmits from this filehandle. */
657de73b88cSMauro Carvalho Chehab mutex_lock(&adap->lock);
658de73b88cSMauro Carvalho Chehab while (!list_empty(&fh->xfer_list)) {
659de73b88cSMauro Carvalho Chehab struct cec_data *data =
660de73b88cSMauro Carvalho Chehab list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
661de73b88cSMauro Carvalho Chehab
662de73b88cSMauro Carvalho Chehab data->blocking = false;
663de73b88cSMauro Carvalho Chehab data->fh = NULL;
6642ddd0330SHans Verkuil list_del_init(&data->xfer_list);
665de73b88cSMauro Carvalho Chehab }
666de73b88cSMauro Carvalho Chehab mutex_unlock(&adap->lock);
667cceda163SHans Verkuil
668cceda163SHans Verkuil mutex_lock(&fh->lock);
669de73b88cSMauro Carvalho Chehab while (!list_empty(&fh->msgs)) {
670de73b88cSMauro Carvalho Chehab struct cec_msg_entry *entry =
671de73b88cSMauro Carvalho Chehab list_first_entry(&fh->msgs, struct cec_msg_entry, list);
672de73b88cSMauro Carvalho Chehab
673de73b88cSMauro Carvalho Chehab list_del(&entry->list);
674de73b88cSMauro Carvalho Chehab kfree(entry);
675de73b88cSMauro Carvalho Chehab }
676de73b88cSMauro Carvalho Chehab for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) {
677de73b88cSMauro Carvalho Chehab while (!list_empty(&fh->events[i])) {
678de73b88cSMauro Carvalho Chehab struct cec_event_entry *entry =
679de73b88cSMauro Carvalho Chehab list_first_entry(&fh->events[i],
680de73b88cSMauro Carvalho Chehab struct cec_event_entry, list);
681de73b88cSMauro Carvalho Chehab
682de73b88cSMauro Carvalho Chehab list_del(&entry->list);
683de73b88cSMauro Carvalho Chehab kfree(entry);
684de73b88cSMauro Carvalho Chehab }
685de73b88cSMauro Carvalho Chehab }
686cceda163SHans Verkuil mutex_unlock(&fh->lock);
687de73b88cSMauro Carvalho Chehab kfree(fh);
688de73b88cSMauro Carvalho Chehab
689de73b88cSMauro Carvalho Chehab cec_put_device(devnode);
690de73b88cSMauro Carvalho Chehab filp->private_data = NULL;
691de73b88cSMauro Carvalho Chehab return 0;
692de73b88cSMauro Carvalho Chehab }
693de73b88cSMauro Carvalho Chehab
694de73b88cSMauro Carvalho Chehab const struct file_operations cec_devnode_fops = {
695de73b88cSMauro Carvalho Chehab .owner = THIS_MODULE,
696de73b88cSMauro Carvalho Chehab .open = cec_open,
697de73b88cSMauro Carvalho Chehab .unlocked_ioctl = cec_ioctl,
698de73b88cSMauro Carvalho Chehab .compat_ioctl = cec_ioctl,
699de73b88cSMauro Carvalho Chehab .release = cec_release,
700de73b88cSMauro Carvalho Chehab .poll = cec_poll,
701de73b88cSMauro Carvalho Chehab .llseek = no_llseek,
702de73b88cSMauro Carvalho Chehab };
703