Commit 6b2bbb08 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

media: cec: rework the cec event handling

Event handling was always fairly simplistic since there were only
two events. With the addition of pin events this needed to be redesigned.

The state_change and lost_msgs events are now core events with the
guarantee that the last state is always available. The new pin events
are a queue of events (up to 64 for each event) and the oldest event
will be dropped if the application cannot keep up. Lost events are
marked with a new event flag.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Reviewed-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 6303d978
...@@ -78,42 +78,62 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr ...@@ -78,42 +78,62 @@ static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr
* Queue a new event for this filehandle. If ts == 0, then set it * Queue a new event for this filehandle. If ts == 0, then set it
* to the current time. * to the current time.
* *
* The two events that are currently defined do not need to keep track * We keep a queue of at most max_event events where max_event differs
* of intermediate events, so no actual queue of events is needed, * per event. If the queue becomes full, then drop the oldest event and
* instead just store the latest state and the total number of lost * keep track of how many events we've dropped.
* messages.
*
* Should new events be added in the future that require intermediate
* results to be queued as well, then a proper queue data structure is
* required. But until then, just keep it simple.
*/ */
void cec_queue_event_fh(struct cec_fh *fh, void cec_queue_event_fh(struct cec_fh *fh,
const struct cec_event *new_ev, u64 ts) const struct cec_event *new_ev, u64 ts)
{ {
struct cec_event *ev = &fh->events[new_ev->event - 1]; static const u8 max_events[CEC_NUM_EVENTS] = {
1, 1, 64, 64,
};
struct cec_event_entry *entry;
unsigned int ev_idx = new_ev->event - 1;
if (WARN_ON(ev_idx >= ARRAY_SIZE(fh->events)))
return;
if (ts == 0) if (ts == 0)
ts = ktime_get_ns(); ts = ktime_get_ns();
mutex_lock(&fh->lock); mutex_lock(&fh->lock);
if (new_ev->event == CEC_EVENT_LOST_MSGS && if (ev_idx < CEC_NUM_CORE_EVENTS)
fh->pending_events & (1 << new_ev->event)) { entry = &fh->core_events[ev_idx];
/* else
* If there is already a lost_msgs event, then just entry = kmalloc(sizeof(*entry), GFP_KERNEL);
* update the lost_msgs count. This effectively if (entry) {
* merges the old and new events into one. if (new_ev->event == CEC_EVENT_LOST_MSGS &&
*/ fh->queued_events[ev_idx]) {
ev->lost_msgs.lost_msgs += new_ev->lost_msgs.lost_msgs; entry->ev.lost_msgs.lost_msgs +=
goto unlock; new_ev->lost_msgs.lost_msgs;
} goto unlock;
}
entry->ev = *new_ev;
entry->ev.ts = ts;
if (fh->queued_events[ev_idx] < max_events[ev_idx]) {
/* Add new msg at the end of the queue */
list_add_tail(&entry->list, &fh->events[ev_idx]);
fh->queued_events[ev_idx]++;
fh->total_queued_events++;
goto unlock;
}
/* if (ev_idx >= CEC_NUM_CORE_EVENTS) {
* Intermediate states are not interesting, so just list_add_tail(&entry->list, &fh->events[ev_idx]);
* overwrite any older event. /* drop the oldest event */
*/ entry = list_first_entry(&fh->events[ev_idx],
*ev = *new_ev; struct cec_event_entry, list);
ev->ts = ts; list_del(&entry->list);
fh->pending_events |= 1 << new_ev->event; kfree(entry);
}
}
/* Mark that events were lost */
entry = list_first_entry_or_null(&fh->events[ev_idx],
struct cec_event_entry, list);
if (entry)
entry->ev.flags |= CEC_EVENT_FL_DROPPED_EVENTS;
unlock: unlock:
mutex_unlock(&fh->lock); mutex_unlock(&fh->lock);
...@@ -134,46 +154,50 @@ static void cec_queue_event(struct cec_adapter *adap, ...@@ -134,46 +154,50 @@ static void cec_queue_event(struct cec_adapter *adap,
} }
/* /*
* Queue a new message for this filehandle. If there is no more room * Queue a new message for this filehandle.
* in the queue, then send the LOST_MSGS event instead. *
* We keep a queue of at most CEC_MAX_MSG_RX_QUEUE_SZ messages. If the
* queue becomes full, then drop the oldest message and keep track
* of how many messages we've dropped.
*/ */
static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg) static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
{ {
static const struct cec_event ev_lost_msg = { static const struct cec_event ev_lost_msgs = {
.ts = 0,
.event = CEC_EVENT_LOST_MSGS, .event = CEC_EVENT_LOST_MSGS,
.flags = 0, .lost_msgs.lost_msgs = 1,
{
.lost_msgs.lost_msgs = 1,
},
}; };
struct cec_msg_entry *entry; struct cec_msg_entry *entry;
mutex_lock(&fh->lock); mutex_lock(&fh->lock);
entry = kmalloc(sizeof(*entry), GFP_KERNEL); entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) if (entry) {
goto lost_msgs; entry->msg = *msg;
/* Add new msg at the end of the queue */
entry->msg = *msg; list_add_tail(&entry->list, &fh->msgs);
/* Add new msg at the end of the queue */
list_add_tail(&entry->list, &fh->msgs); if (fh->queued_msgs < CEC_MAX_MSG_RX_QUEUE_SZ) {
/* All is fine if there is enough room */
fh->queued_msgs++;
mutex_unlock(&fh->lock);
wake_up_interruptible(&fh->wait);
return;
}
/* /*
* if the queue now has more than CEC_MAX_MSG_RX_QUEUE_SZ * if the message queue is full, then drop the oldest one and
* messages, drop the oldest one and send a lost message event. * send a lost message event.
*/ */
if (fh->queued_msgs == CEC_MAX_MSG_RX_QUEUE_SZ) { entry = list_first_entry(&fh->msgs, struct cec_msg_entry, list);
list_del(&entry->list); list_del(&entry->list);
goto lost_msgs; kfree(entry);
} }
fh->queued_msgs++;
mutex_unlock(&fh->lock); mutex_unlock(&fh->lock);
wake_up_interruptible(&fh->wait);
return;
lost_msgs: /*
mutex_unlock(&fh->lock); * We lost a message, either because kmalloc failed or the queue
cec_queue_event_fh(fh, &ev_lost_msg, 0); * was full.
*/
cec_queue_event_fh(fh, &ev_lost_msgs, ktime_get_ns());
} }
/* /*
......
...@@ -57,7 +57,7 @@ static unsigned int cec_poll(struct file *filp, ...@@ -57,7 +57,7 @@ static unsigned int cec_poll(struct file *filp,
res |= POLLOUT | POLLWRNORM; res |= POLLOUT | POLLWRNORM;
if (fh->queued_msgs) if (fh->queued_msgs)
res |= POLLIN | POLLRDNORM; res |= POLLIN | POLLRDNORM;
if (fh->pending_events) if (fh->total_queued_events)
res |= POLLPRI; res |= POLLPRI;
poll_wait(filp, &fh->wait, poll); poll_wait(filp, &fh->wait, poll);
mutex_unlock(&adap->lock); mutex_unlock(&adap->lock);
...@@ -289,15 +289,17 @@ static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh, ...@@ -289,15 +289,17 @@ static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh, static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
bool block, struct cec_event __user *parg) bool block, struct cec_event __user *parg)
{ {
struct cec_event *ev = NULL; struct cec_event_entry *ev = NULL;
u64 ts = ~0ULL; u64 ts = ~0ULL;
unsigned int i; unsigned int i;
unsigned int ev_idx;
long err = 0; long err = 0;
mutex_lock(&fh->lock); mutex_lock(&fh->lock);
while (!fh->pending_events && block) { while (!fh->total_queued_events && block) {
mutex_unlock(&fh->lock); mutex_unlock(&fh->lock);
err = wait_event_interruptible(fh->wait, fh->pending_events); err = wait_event_interruptible(fh->wait,
fh->total_queued_events);
if (err) if (err)
return err; return err;
mutex_lock(&fh->lock); mutex_lock(&fh->lock);
...@@ -305,23 +307,29 @@ static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh, ...@@ -305,23 +307,29 @@ static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
/* Find the oldest event */ /* Find the oldest event */
for (i = 0; i < CEC_NUM_EVENTS; i++) { for (i = 0; i < CEC_NUM_EVENTS; i++) {
if (fh->pending_events & (1 << (i + 1)) && struct cec_event_entry *entry =
fh->events[i].ts <= ts) { list_first_entry_or_null(&fh->events[i],
ev = &fh->events[i]; struct cec_event_entry, list);
ts = ev->ts;
if (entry && entry->ev.ts <= ts) {
ev = entry;
ev_idx = i;
ts = ev->ev.ts;
} }
} }
if (!ev) { if (!ev) {
err = -EAGAIN; err = -EAGAIN;
goto unlock; goto unlock;
} }
list_del(&ev->list);
if (copy_to_user(parg, ev, sizeof(*ev))) { if (copy_to_user(parg, &ev->ev, sizeof(ev->ev)))
err = -EFAULT; err = -EFAULT;
goto unlock; if (ev_idx >= CEC_NUM_CORE_EVENTS)
} kfree(ev);
fh->queued_events[ev_idx]--;
fh->pending_events &= ~(1 << ev->event); fh->total_queued_events--;
unlock: unlock:
mutex_unlock(&fh->lock); mutex_unlock(&fh->lock);
...@@ -495,6 +503,7 @@ static int cec_open(struct inode *inode, struct file *filp) ...@@ -495,6 +503,7 @@ static int cec_open(struct inode *inode, struct file *filp)
.event = CEC_EVENT_STATE_CHANGE, .event = CEC_EVENT_STATE_CHANGE,
.flags = CEC_EVENT_FL_INITIAL_STATE, .flags = CEC_EVENT_FL_INITIAL_STATE,
}; };
unsigned int i;
int err; int err;
if (!fh) if (!fh)
...@@ -502,6 +511,8 @@ static int cec_open(struct inode *inode, struct file *filp) ...@@ -502,6 +511,8 @@ static int cec_open(struct inode *inode, struct file *filp)
INIT_LIST_HEAD(&fh->msgs); INIT_LIST_HEAD(&fh->msgs);
INIT_LIST_HEAD(&fh->xfer_list); INIT_LIST_HEAD(&fh->xfer_list);
for (i = 0; i < CEC_NUM_EVENTS; i++)
INIT_LIST_HEAD(&fh->events[i]);
mutex_init(&fh->lock); mutex_init(&fh->lock);
init_waitqueue_head(&fh->wait); init_waitqueue_head(&fh->wait);
...@@ -544,6 +555,7 @@ static int cec_release(struct inode *inode, struct file *filp) ...@@ -544,6 +555,7 @@ static int cec_release(struct inode *inode, struct file *filp)
struct cec_devnode *devnode = cec_devnode_data(filp); struct cec_devnode *devnode = cec_devnode_data(filp);
struct cec_adapter *adap = to_cec_adapter(devnode); struct cec_adapter *adap = to_cec_adapter(devnode);
struct cec_fh *fh = filp->private_data; struct cec_fh *fh = filp->private_data;
unsigned int i;
mutex_lock(&adap->lock); mutex_lock(&adap->lock);
if (adap->cec_initiator == fh) if (adap->cec_initiator == fh)
...@@ -585,6 +597,16 @@ static int cec_release(struct inode *inode, struct file *filp) ...@@ -585,6 +597,16 @@ static int cec_release(struct inode *inode, struct file *filp)
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
} }
for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) {
while (!list_empty(&fh->events[i])) {
struct cec_event_entry *entry =
list_first_entry(&fh->events[i],
struct cec_event_entry, list);
list_del(&entry->list);
kfree(entry);
}
}
kfree(fh); kfree(fh);
cec_put_device(devnode); cec_put_device(devnode);
......
...@@ -81,7 +81,13 @@ struct cec_msg_entry { ...@@ -81,7 +81,13 @@ struct cec_msg_entry {
struct cec_msg msg; struct cec_msg msg;
}; };
#define CEC_NUM_EVENTS CEC_EVENT_LOST_MSGS struct cec_event_entry {
struct list_head list;
struct cec_event ev;
};
#define CEC_NUM_CORE_EVENTS 2
#define CEC_NUM_EVENTS CEC_EVENT_PIN_HIGH
struct cec_fh { struct cec_fh {
struct list_head list; struct list_head list;
...@@ -92,9 +98,11 @@ struct cec_fh { ...@@ -92,9 +98,11 @@ struct cec_fh {
/* Events */ /* Events */
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned int pending_events;
struct cec_event events[CEC_NUM_EVENTS];
struct mutex lock; struct mutex lock;
struct list_head events[CEC_NUM_EVENTS]; /* queued events */
u8 queued_events[CEC_NUM_EVENTS];
unsigned int total_queued_events;
struct cec_event_entry core_events[CEC_NUM_CORE_EVENTS];
struct list_head msgs; /* queued messages */ struct list_head msgs; /* queued messages */
unsigned int queued_msgs; unsigned int queued_msgs;
}; };
......
...@@ -412,6 +412,7 @@ struct cec_log_addrs { ...@@ -412,6 +412,7 @@ struct cec_log_addrs {
#define CEC_EVENT_PIN_HIGH 4 #define CEC_EVENT_PIN_HIGH 4
#define CEC_EVENT_FL_INITIAL_STATE (1 << 0) #define CEC_EVENT_FL_INITIAL_STATE (1 << 0)
#define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1)
/** /**
* struct cec_event_state_change - used when the CEC adapter changes state. * struct cec_event_state_change - used when the CEC adapter changes state.
...@@ -424,7 +425,7 @@ struct cec_event_state_change { ...@@ -424,7 +425,7 @@ struct cec_event_state_change {
}; };
/** /**
* struct cec_event_lost_msgs - tells you how many messages were lost due. * struct cec_event_lost_msgs - tells you how many messages were lost.
* @lost_msgs: how many messages were lost. * @lost_msgs: how many messages were lost.
*/ */
struct cec_event_lost_msgs { struct cec_event_lost_msgs {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment