| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright (c) 1985, 1997 by Sun Microsystems, Inc. |
| * All rights reserved. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * Vuid (Virtual User Input Device) queue management. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/vuid_event.h> |
| #include <sys/vuid_queue.h> |
| |
| static Vuid_q_node *vq_alloc_node(Vuid_queue *vq); |
| static void vq_free_node(Vuid_queue *vq, Vuid_q_node *vqn); |
| |
| static int tv_equal(struct timeval32 a, struct timeval32 b); |
| static struct timeval32 tv_subt(struct timeval32 atv, struct timeval32 btv); |
| static struct timeval32 tv_divide(struct timeval32 tv, int dividend); |
| static struct timeval32 tv_mult(struct timeval32 tv, int multiplier); |
| #define tv_to_usec(tv) (((tv).tv_sec * 1000000) + (tv).tv_usec) |
| /* Works for small numbers (< 1000) of seconds */ |
| static struct timeval32 usec_to_tv(int usec); |
| |
| void |
| vq_initialize(Vuid_queue *vq, caddr_t data, u_int bytes) |
| { |
| Vuid_q_node *new_vqns, *vqn; |
| |
| /* Initialize vq */ |
| vq->top = vq->bottom = vq->free = VUID_Q_NODE_NULL; |
| vq->size = 1 + (bytes - sizeof (Vuid_q_node)) / sizeof (Vuid_q_node); |
| /* Place in pool by freeing all nodes (fudge vq->num for this) */ |
| new_vqns = (Vuid_q_node *)data; |
| vq->num = vq->size; |
| for (vqn = new_vqns; vqn < new_vqns + vq->size; vqn++) |
| vq_free_node(vq, vqn); |
| } |
| |
| Vuid_q_code |
| vq_put(Vuid_queue *vq, Firm_event *firm_event) |
| { |
| Vuid_q_node *vp; |
| |
| /* Merge into existing events based on time stamp */ |
| for (vp = vq->bottom; vp; vp = vp->prev) { |
| /* Put later times closer to the bottom than earlier ones */ |
| if (timercmp(&vp->firm_event.time, &firm_event->time, |
| < /* comment to make cstyle happy - heinous! */) || |
| tv_equal(vp->firm_event.time, firm_event->time)) { |
| Vuid_q_node *vqn = vq_alloc_node(vq); |
| |
| if (vqn == VUID_Q_NODE_NULL) |
| return (VUID_Q_OVERFLOW); |
| vqn->firm_event = *firm_event; |
| /* Insert vqn before vq (neither are null) */ |
| /* Initialize vqn's next and prev */ |
| vqn->next = vp->next; |
| vqn->prev = vp; |
| /* Fix up vp next's prev */ |
| if (vp->next != VUID_Q_NODE_NULL) |
| vp->next->prev = vqn; |
| /* Fix up vp's next */ |
| vp->next = vqn; |
| /* Change bottom */ |
| if (vp == vq->bottom) |
| vq->bottom = vqn; |
| /* Change top */ |
| if (vq->top == VUID_Q_NODE_NULL) |
| vq->top = vqn; |
| return (VUID_Q_OK); |
| } |
| } |
| /* Place at top of queue */ |
| return (vq_putback(vq, firm_event)); |
| } |
| |
| Vuid_q_code |
| vq_get(Vuid_queue *vq, Firm_event *firm_event) |
| { |
| Vuid_q_node *vqn = vq->top; |
| |
| if (vqn == VUID_Q_NODE_NULL) |
| return (VUID_Q_EMPTY); |
| /* Conditionally copy data */ |
| if (firm_event != FIRM_EVENT_NULL) |
| *firm_event = vqn->firm_event; |
| /* Change top */ |
| vq->top = vqn->next; |
| /* Null new top's prev */ |
| if (vq->top != VUID_Q_NODE_NULL) |
| vq->top->prev = VUID_Q_NODE_NULL; |
| /* Change bottom */ |
| if (vq->bottom == vqn) |
| vq->bottom = VUID_Q_NODE_NULL; |
| /* Release storage */ |
| vq_free_node(vq, vqn); |
| return (VUID_Q_OK); |
| } |
| |
| Vuid_q_code |
| vq_peek(Vuid_queue *vq, Firm_event *firm_event) |
| { |
| if (vq->top == VUID_Q_NODE_NULL) |
| return (VUID_Q_EMPTY); |
| *firm_event = vq->top->firm_event; |
| return (VUID_Q_OK); |
| } |
| |
| Vuid_q_code |
| vq_putback(Vuid_queue *vq, Firm_event *firm_event) |
| { |
| Vuid_q_node *vqn = vq_alloc_node(vq); |
| |
| if (vqn == VUID_Q_NODE_NULL) |
| return (VUID_Q_OVERFLOW); |
| vqn->firm_event = *firm_event; |
| /* Change new top's next */ |
| vqn->next = vq->top; |
| /* Null new top's prev */ |
| vqn->prev = VUID_Q_NODE_NULL; |
| /* Change old top's prev */ |
| if (vq->top != VUID_Q_NODE_NULL) |
| vq->top->prev = vqn; |
| /* Change top */ |
| vq->top = vqn; |
| /* Change bottom */ |
| if (vq->bottom == VUID_Q_NODE_NULL) |
| vq->bottom = vqn; |
| return (VUID_Q_OK); |
| } |
| |
| int |
| vq_compress(Vuid_queue *vq, int factor) |
| { |
| struct timeval32 tv_interval, tv_avg_diff, tv_diff; /* Intermediates */ |
| struct timeval32 tv_threshold; |
| Vuid_q_node *base, *victim; |
| Vuid_q_node *victim_next; |
| int num_start; |
| |
| if (vq->top == VUID_Q_NODE_NULL) |
| return (0); |
| num_start = vq->num; |
| /* |
| * Determine the threshold time interval under which events of |
| * the same type (valuator only) are collapsed. |
| * min_time_betvqnen_values = ((first_entry_time - last_entry_time) / |
| * max_number_of_queue_entries) * factor_by_which_to_compress_queue; |
| */ |
| tv_interval = tv_subt(vq->bottom->firm_event.time, |
| vq->top->firm_event.time); |
| tv_avg_diff = tv_divide(tv_interval, vq->num); |
| tv_threshold = tv_mult(tv_avg_diff, factor); |
| /* March down list */ |
| for (base = vq->top; base; base = base->next) { |
| /* See if valuator event */ |
| if (!vq_is_valuator(base)) |
| continue; |
| /* Run down list looking for a collapse victim */ |
| for (victim = base->next; victim; victim = victim_next) { |
| /* Remember next victim incase axe victim below */ |
| victim_next = victim->next; |
| /* Fail if not valuator event */ |
| if (!vq_is_valuator(victim)) |
| goto Advance_Base; |
| /* |
| * May peek ahead and do the collapse as long as the |
| * intervening times of other valuator event types |
| * are the same. Fail if intervening event's time |
| * differs from victim's. |
| */ |
| if (victim->prev != base) { |
| if (!tv_equal(victim->prev->firm_event.time, |
| victim->firm_event.time)) |
| goto Advance_Base; |
| } |
| /* Fail if time difference is above threshold */ |
| tv_diff = tv_subt(victim->firm_event.time, |
| base->firm_event.time); |
| /* Zero factor means collapse regardless of threshold */ |
| if ((factor > 0) && |
| (timercmp(&tv_diff, &tv_threshold, > /* cstyle */))) |
| goto Advance_Base; |
| /* Do collapse if same event id */ |
| if (victim->firm_event.id == base->firm_event.id) { |
| /* Collapse value into base event */ |
| switch (base->firm_event.pair_type) { |
| case FE_PAIR_ABSOLUTE: |
| /* id is delta */ |
| base->firm_event.value += |
| victim->firm_event.value; |
| break; |
| case FE_PAIR_DELTA: |
| /* id is absolute */ |
| /* Fall through */ |
| default: |
| /* Assume id is absolute */ |
| base->firm_event.value = |
| victim->firm_event.value; |
| break; |
| } |
| /* Remove victim node */ |
| vq_delete_node(vq, victim); |
| } |
| } |
| Advance_Base: |
| {} |
| } |
| return (num_start - vq->num); |
| } |
| |
| int |
| vq_is_valuator(Vuid_q_node *vqn) |
| { |
| return ((vqn->firm_event.value < 1 && vqn->firm_event.value > -1) || |
| (vqn->firm_event.pair_type == FE_PAIR_DELTA) || |
| (vqn->firm_event.pair_type == FE_PAIR_ABSOLUTE)); |
| } |
| |
| void |
| vq_delete_node(Vuid_queue *vq, Vuid_q_node *vqn) |
| { |
| /* Use get if removing off top of queue */ |
| if (vqn == vq->top) { |
| (void) vq_get(vq, FIRM_EVENT_NULL); |
| return; |
| } |
| /* Update previous next link (not null else vqn would be top) */ |
| vqn->prev->next = vqn->next; |
| /* Change bottom */ |
| if (vq->bottom == vqn) |
| vq->bottom = vqn->prev; |
| else |
| /* Update next previous link (if null else vqn is bottom) */ |
| vqn->next->prev = vqn->prev; |
| /* Release storage */ |
| vq_free_node(vq, vqn); |
| } |
| |
| /* |
| * Caller must initialize data returned from vq_alloc_node. |
| * VUID_Q_NODE_NULL is possible. |
| */ |
| static Vuid_q_node * |
| vq_alloc_node(Vuid_queue *vq) |
| { |
| Vuid_q_node *vqn; |
| |
| if (vq->free == VUID_Q_NODE_NULL) |
| return (VUID_Q_NODE_NULL); |
| vqn = vq->free; |
| vq->free = vq->free->next; |
| vq->num++; |
| vqn->next = VUID_Q_NODE_NULL; |
| return (vqn); |
| } |
| |
| static void |
| vq_free_node(Vuid_queue *vq, Vuid_q_node *vqn) |
| { |
| vqn->next = vq->free; |
| vqn->prev = VUID_Q_NODE_NULL; |
| vq->free = vqn; |
| vq->num--; |
| } |
| |
| static int |
| tv_equal(struct timeval32 a, struct timeval32 b) |
| { |
| return (a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec); |
| } |
| |
| /* atv-btv */ |
| static struct timeval32 |
| tv_subt(struct timeval32 atv, struct timeval32 btv) |
| { |
| if ((atv.tv_usec < btv.tv_usec) && atv.tv_sec) { |
| atv.tv_usec += 1000000; |
| atv.tv_sec--; |
| } |
| if (atv.tv_usec > btv.tv_usec) |
| atv.tv_usec -= btv.tv_usec; |
| else |
| atv.tv_usec = 0; |
| if (atv.tv_sec > btv.tv_sec) |
| atv.tv_sec -= btv.tv_sec; |
| else { |
| if (atv.tv_sec < btv.tv_sec) |
| atv.tv_usec = 0; |
| atv.tv_sec = 0; |
| } |
| return (atv); |
| } |
| |
| /* tv / dividend */ |
| static struct timeval32 |
| tv_divide(struct timeval32 tv, int dividend) |
| { |
| int usecs; |
| |
| if (dividend == 0) |
| return (tv); |
| usecs = tv_to_usec(tv); |
| usecs /= dividend; |
| tv = usec_to_tv(usecs); |
| return (tv); |
| } |
| |
| /* tv * multiplier (works for small multipliers * seconds, < 1000) */ |
| static struct timeval32 |
| tv_mult(struct timeval32 tv, int multiplier) |
| { |
| int usecs; |
| |
| usecs = tv_to_usec(tv); |
| usecs *= multiplier; |
| tv = usec_to_tv(usecs); |
| return (tv); |
| } |
| |
| static struct timeval32 |
| usec_to_tv(int usec) |
| { |
| struct timeval32 tv; |
| |
| tv.tv_sec = usec / 1000000; |
| tv.tv_usec = usec % 1000000; |
| return (tv); |
| } |