mce-lib.c 12.9 KB
Newer Older
1 2 3 4 5
/**
 * @file mce-lib.c
 * This file provides various helper functions
 * for the Mode Control Entity
 * <p>
Santtu Lakkala's avatar
Santtu Lakkala committed
6
 * Copyright © 2004-2011 Nokia Corporation and/or its subsidiary(-ies).
7
 * Copyright (C) 2014-2019 Jolla Ltd.
8 9
 * <p>
 * @author David Weinehall <david.weinehall@nokia.com>
10 11 12
 * @author Tapio Rantala <ext-tapio.rantala@nokia.com>
 * @author Santtu Lakkala <ext-santtu.1.lakkala@nokia.com>
 * @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * mce is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * mce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with mce.  If not, see <http://www.gnu.org/licenses/>.
 */

27
#include "mce-lib.h"
28

29 30
#include "mce.h"
#include "mce-log.h"
31
#include "mce-wakelock.h"
32

33 34
#include <stdio.h>
#include <string.h>
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

/**
 * Set a bit
 *
 * @param bit The bit to set
 * @param bitfield A pointer to an array with the bitfield
 */
void set_bit(guint bit, gulong **bitfield)
{
	if ((bitfield == NULL) || (*bitfield == NULL))
		goto EXIT;

	(*bitfield)[bit / bitsize_of(**bitfield)] |= 1UL << (bit % bitsize_of(**bitfield));

EXIT:
	return;
}

/**
 * Clear a bit
 *
 * @param bit The bit to clear
 * @param bitfield A pointer to an array with the bitfield
 */
void clear_bit(guint bit, gulong **bitfield)
{
	if ((bitfield == NULL) || (*bitfield == NULL))
		goto EXIT;

	(*bitfield)[bit / bitsize_of(**bitfield)] &= ~(1UL << (bit % bitsize_of(**bitfield)));

EXIT:
	return;
}

/**
 * Test whether a bit is set
 *
 * @param bit The bit to test for
 * @param bitfield An array with the bitfield
 * @return TRUE if the bit is set,
 *         FALSE if the bit is unset
 */
gboolean test_bit(guint bit, const gulong *bitfield)
{
	return ((1UL << (bit % bitsize_of(*bitfield))) &
		(((gulong *)bitfield)[bit / bitsize_of(*bitfield)])) != 0;
}

/**
 * Convert a string to a bitfield
 *
 * @param string The string with comma-separated numbers
 *               to turn into a bitfield
 * @param[in,out] bitfield A bitfield to return the string in
 * @param bitfieldsize The size of the bitfield
 * @return TRUE on success,
 *         FALSE if the string could not be parsed numerically
 *               or if a number was out of range for the bitfield
 */
gboolean string_to_bitfield(const gchar *string,
			    gulong **bitfield, gsize bitfieldsize)
{
	gchar *tmp = (gchar *)string;
	gboolean status = FALSE;
	int offset = 0;
	guint num;

	if ((string == NULL) || (bitfield == NULL) || (*bitfield == NULL))
		goto EXIT;

	while ((sscanf(tmp, "%u%n", &num, &offset) != 0) && (offset != 0)) {
		/* Make sure we can represent this number */
		if (num > (bitfieldsize * bitsize_of(**bitfield)))
			goto EXIT;

		set_bit(num, bitfield);
		tmp += (offset + 1);
		offset = 0;
	}

	status = TRUE;

EXIT:
	return status;
}

/**
 * Convert a bitfield to a string
 *
 * @param bitfield The bitfield to convert to a comma-separated string
 *                 with the numbers of the set bits
 * @param bitfieldsize The size of the bitfield
 * @return A string with the newly allocated string on success,
 *         NULL on failure
 */
char *bitfield_to_string(const gulong *bitfield, gsize bitfieldsize)
{
	gchar *tmp = NULL;
	guint i, j;

	/* Always pass 0; this way a NULL string represents failure,
	 * and a string with no bits set will represent an empty mask;
	 * we also simplify the g_strdup_printf() case quite a bit
	 */
	if ((tmp = strdup("0")) == NULL) {
		mce_log(LL_CRIT,
			"Failed to allocate memory "
			"for tmp");
		goto EXIT;
	}

	for (i = 0; i < bitfieldsize; i++) {
		for (j = 0; bitfield[i] && j < bitsize_of(*bitfield); j++) {
			if (bitfield[i] & (1UL << j)) {
				gchar *tmp2;

				tmp2 = g_strdup_printf("%s,%u",
						       tmp, (i * bitsize_of(*bitfield)) + j);

				g_free(tmp);

				if (tmp2 == NULL) {
					mce_log(LL_CRIT,
						"Failed to allocate memory "
						"for tmp2");
					goto EXIT;
				}

				tmp = tmp2;
			}
		}
	}

EXIT:
	return tmp;
}

/**
 * Convert a value to a binary string (9-bit, since it's for Lysti)
 * FIXME: convert to handle arbitrary length instead and make reentrant
 * @note This function is non-reentrant; it returns a fixed sized,
 *       statically allocated, string that should not be freed
 *
 * @param bin The value to convert to a binary string
 * @return A static string with a representation the value
 */
const gchar *bin_to_string(guint bin)
{
	static gchar bin_string[] = "000000000";
	gint i;

	for (i = 0; i < 9; i++) {
		bin_string[8 - i] = (bin & (1 << i)) ? '1' : '0';
	}

	return bin_string;
}

/**
 * Translate an integer to its string representation;
 * if no valid mapping exists, return the provided default string
 * (if one has been provided)
 *
 * @param translation A mce_translation_t mapping
 * @param number The number to map to a string
 * @param default_string The default string to return if no match is found
 * @return A string translation of the integer
 */
const gchar *mce_translate_int_to_string_with_default(const mce_translation_t translation[], gint number, const gchar *default_string)
{
	const gchar *string;
	gint i = 0;

	/* This might seem awkward, but it's made to allow sparse
	 * number spaces
	 */
	do {
		string = translation[i].string;
	} while (translation[i].number != MCE_INVALID_TRANSLATION &&
		 translation[i++].number != number);

	/* XXX: will this really behave correctly if there's only
	 * one (MCE_INVALID_TRANSLATION) element in the structure?
	 */
	if ((translation[i].number == MCE_INVALID_TRANSLATION) &&
	    (translation[i - 1].number != number) &&
	    (default_string != NULL))
		string = default_string;

	return string;
}

/**
 * Translate an integer to its string representation
 *
 * @param translation A mce_translation_t mapping
 * @param number The number to map to a string
 * @return A string translation of the integer
 */
const gchar *mce_translate_int_to_string(const mce_translation_t translation[],
					 gint number)
{
	return mce_translate_int_to_string_with_default(translation, number, NULL);
}

/**
 * Translate a string to its integer representation
 * if no valid mapping exists, return the provided default integer
 * (if one has been provided)
 *
 * @param translation A mce_translation_t mapping
 * @param string The string to map to an number
 * @param default_integer The number to return if no match is found
 * @return An integer translation value of the string
 */
gint mce_translate_string_to_int_with_default(const mce_translation_t translation[], const gchar *const string, gint default_integer)
{
	gint number = MCE_INVALID_TRANSLATION;
	gint i = 0;

	while (translation[i].number != MCE_INVALID_TRANSLATION) {
		/* If the string matches, set number and stop searching */
Santtu Lakkala's avatar
Santtu Lakkala committed
258
		if (!strcmp(translation[i].string, string)) {
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
			number = translation[i].number;
			break;
		}

		i++;
	}

	if (translation[i].number == MCE_INVALID_TRANSLATION)
		number = default_integer;

	return number;
}
/**
 * Translate a string to its integer representation
 *
 * @param translation A mce_translation_t mapping
 * @param string The string to map to an number
 * @return An integer translation value of the string
 */
gint mce_translate_string_to_int(const mce_translation_t translation[],
				 const gchar *const string)
{
	return mce_translate_string_to_int_with_default(translation, string, MCE_INVALID_TRANSLATION);
}

/**
 * Locate a delimited substring
 *
 * @param haystack The string to search in
 * @param needle The string to search for
 * @param delimiter The delimiter
 * @return A pointer to the position of the substring on match,
 *         NULL if no match found
 */
gchar *strstr_delim(const gchar *const haystack, const char *needle,
		    const char *const delimiter)
{
	char *match = NULL;
	const char *tmp2;
	size_t dlen;

	if ((haystack == NULL) || (needle == NULL))
		return NULL;

	/* If there's no delimiter, we'll behave as strstr */
	dlen = (delimiter == NULL) ? 0 : strlen(delimiter);

	tmp2 = haystack;

	while (tmp2 != NULL) {
		const char *tmp;
		ptrdiff_t len;

		/* Find the first occurence of the delimiter */
		if (dlen != 0)
			tmp = strstr(tmp2, delimiter);
		else
			tmp = NULL;

		/* If there's a delimiter, match up to it,
		 * if not, match the entire remaining string
		 */
		if (tmp != NULL)
			len = tmp - tmp2;
		else
			len = strlen(tmp2);

		/* If we find a match, we're done */
		if ((match = g_strstr_len(tmp2, len, needle)) != NULL)
			goto EXIT;

		/* If there's no more delimiters, we're done */
		if (len == 0)
			goto EXIT;

		/* Skip past the current token + the delimiter */
		tmp2 += (len + dlen);
	};

EXIT:
	return match;
}
Tapio Rantala's avatar
Tapio Rantala committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

/**
 * Compare a string with memory, with length checks
 *
 * @param mem The memory to compare with
 * @param str The string to compare the memory to
 * @param len The length of the memory area
 * @return TRUE if the string matches the memory area,
 *         FALSE if the memory area does not match, or if the lengths differ
 */
gboolean strmemcmp(guint8 *mem, const gchar *str, gulong len)
{
	gboolean result = FALSE;

	if (strlen(str) != len)
		goto EXIT;

	if (memcmp(mem, str, len) != 0)
		goto EXIT;

	result = TRUE;

EXIT:
	return result;
}
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

/** Get clock id specific time stamp in milliseconds
 *
 * @param id  Clock id such as CLOCK_REALTIME or CLOCK_MONOTONIC
 *
 * @return 64-bit timestamp
 */
static int64_t mce_lib_get_tick(clockid_t id)
{
	int64_t res = 0;

	struct timespec ts;

	if( clock_gettime(id, &ts) == 0 ) {
		res = ts.tv_sec;
		res *= 1000;
		res += ts.tv_nsec / 1000000;
	}

	return res;
}

/** Get CLOCK_BOOTTIME time stamp in milliseconds
 *
 * @return 64-bit timestamp
 */
int64_t mce_lib_get_boot_tick(void)
{
	return mce_lib_get_tick(CLOCK_BOOTTIME);
}

/** Get CLOCK_MONOTONIC time stamp in milliseconds
 *
 * @return 64-bit timestamp
 */
int64_t mce_lib_get_mono_tick(void)
{
	return mce_lib_get_tick(CLOCK_MONOTONIC);
}

/** Get CLOCK_REALTIME time stamp in milliseconds
 *
 * @return 64-bit timestamp
 */
int64_t mce_lib_get_real_tick(void)
{
	return mce_lib_get_tick(CLOCK_REALTIME);
}
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

/** Bookkeeping data for wakelocked glib timers */
typedef struct timeout_gate_t
{
	gchar       *tg_lock;
	GSourceFunc  tg_func;
	gpointer     tg_data;
	GDestroyNotify tg_free;
} timeout_gate_t;

/** Delete wakelocked glib timer gate
 */
static void
timeout_gate_delete_cb(gpointer aptr)
{
	timeout_gate_t *self = aptr;

	if( self->tg_free )
		self->tg_free(self->tg_data);

	mce_wakelock_release(self->tg_lock);

	g_free(self->tg_lock);
	g_slice_free1(sizeof *self, self);
}

/** Create wakelocked glib timer gate
 */
static timeout_gate_t *
timeout_gate_create(GSourceFunc func, gpointer aptr, GDestroyNotify notify)
{
	static unsigned uniq = 0;

	timeout_gate_t *self = g_slice_alloc0(sizeof *self);
	self->tg_lock = g_strdup_printf("mce_timeout_%u", ++uniq);
	self->tg_func = func;
	self->tg_data = aptr;
	self->tg_free = notify;
	mce_wakelock_obtain(self->tg_lock, -1);
	return self;
}

/** Handle wakelocked glib timeout
 */
static gboolean
timeout_gate_cb(gpointer aptr)
{
	timeout_gate_t *self = aptr;
	return self->tg_func(self->tg_data);
}

/** Wakelocking alternative for g_timeout_add_full()
 *
 * Obtains multiplexed wakelock that is released when the timeout
 * source is released either implicitly by returning FALSE from callback
 * function, or explicitly by calling g_source_remove().
 *
 * @param priority  the priority of the timeout source
 * @param interval  the time between calls to the function, in milliseconds
 * @param function  function to call
 * @param data      data to pass to function
 * @param notify    function to call when the timeout is removed, or NULL
 *
 * @return glib source identifier
 */
guint
mce_wakelocked_timeout_add_full(gint priority, guint interval,
				GSourceFunc function,
				gpointer data, GDestroyNotify notify)
{
	return g_timeout_add_full(priority, interval, timeout_gate_cb,
				  timeout_gate_create(function, data, notify),
				  timeout_gate_delete_cb);
}

/** Wakelocking alternative for g_timeout_add()
 *
 * See g_timeout_add_full() for details.
 *
 * @param interval  the time between calls to the function, in milliseconds
 * @param function  function to call
 * @param data      data to pass to function
 *
 * @return glib source identifier
 */
guint
mce_wakelocked_timeout_add(guint interval, GSourceFunc function, gpointer data)
{
	return mce_wakelocked_timeout_add_full(G_PRIORITY_DEFAULT, interval,
					       function, data, NULL);
}

/** Wakelocking alternative for g_idle_add()
 *
 * See g_timeout_add_full() for details.
 *
 * @param function  function to call
 * @param data      data to pass to function
 *
 * @return glib source identifier
 */
guint
mce_wakelocked_idle_add(GSourceFunc function, gpointer data)
{
	/* NB This not exactly like g_idle_add() */
	return mce_wakelocked_timeout_add_full(G_PRIORITY_DEFAULT, 0,
					       function, data, NULL);
}