220 lines
5.3 KiB
C
220 lines
5.3 KiB
C
/*
|
|
Copyright (C) 2001-2003 Paul Davis
|
|
Copyright (C) 2005 Jussi Laako
|
|
Copyright (C) 2004-2008 Grame
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "JackConstants.h"
|
|
#include "JackTime.h"
|
|
#include "JackTypes.h"
|
|
#include "JackError.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
|
|
jack_time_t (*_jack_get_microseconds)(void) = 0;
|
|
|
|
#if defined(__gnu_linux__) && (defined(__i386__) || defined(__x86_64__))
|
|
#define HPET_SUPPORT
|
|
#define HPET_MMAP_SIZE 1024
|
|
#define HPET_CAPS 0x000
|
|
#define HPET_PERIOD 0x004
|
|
#define HPET_COUNTER 0x0f0
|
|
#define HPET_CAPS_COUNTER_64BIT (1 << 13)
|
|
#if defined(__x86_64__)
|
|
typedef uint64_t hpet_counter_t;
|
|
#else
|
|
typedef uint32_t hpet_counter_t;
|
|
#endif
|
|
static int hpet_fd;
|
|
static unsigned char *hpet_ptr;
|
|
static uint32_t hpet_period; /* period length in femto secs */
|
|
static uint64_t hpet_offset = 0;
|
|
static uint64_t hpet_wrap;
|
|
static hpet_counter_t hpet_previous = 0;
|
|
#endif /* defined(__gnu_linux__) && (__i386__ || __x86_64__) */
|
|
|
|
#ifdef HPET_SUPPORT
|
|
|
|
static int jack_hpet_init ()
|
|
{
|
|
uint32_t hpet_caps;
|
|
|
|
hpet_fd = open("/dev/hpet", O_RDONLY);
|
|
if (hpet_fd < 0) {
|
|
jack_error ("This system has no accessible HPET device (%s)", strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
hpet_ptr = (unsigned char *) mmap(NULL, HPET_MMAP_SIZE,
|
|
PROT_READ, MAP_SHARED, hpet_fd, 0);
|
|
if (hpet_ptr == MAP_FAILED) {
|
|
jack_error ("This system has no mappable HPET device (%s)", strerror (errno));
|
|
close (hpet_fd);
|
|
return -1;
|
|
}
|
|
|
|
/* this assumes period to be constant. if needed,
|
|
it can be moved to the clock access function
|
|
*/
|
|
hpet_period = *((uint32_t *) (hpet_ptr + HPET_PERIOD));
|
|
hpet_caps = *((uint32_t *) (hpet_ptr + HPET_CAPS));
|
|
hpet_wrap = ((hpet_caps & HPET_CAPS_COUNTER_64BIT) &&
|
|
(sizeof(hpet_counter_t) == sizeof(uint64_t))) ?
|
|
0 : ((uint64_t) 1 << 32);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static jack_time_t jack_get_microseconds_from_hpet (void)
|
|
{
|
|
hpet_counter_t hpet_counter;
|
|
long double hpet_time;
|
|
|
|
hpet_counter = *((hpet_counter_t *) (hpet_ptr + HPET_COUNTER));
|
|
if (hpet_counter < hpet_previous)
|
|
hpet_offset += hpet_wrap;
|
|
hpet_previous = hpet_counter;
|
|
hpet_time = (long double) (hpet_offset + hpet_counter) *
|
|
(long double) hpet_period * (long double) 1e-9;
|
|
return ((jack_time_t) (hpet_time + 0.5));
|
|
}
|
|
|
|
#else
|
|
|
|
static int jack_hpet_init ()
|
|
{
|
|
jack_error ("This version of JACK or this computer does not have HPET support.\n"
|
|
"Please choose a different clock source.");
|
|
return -1;
|
|
}
|
|
|
|
static jack_time_t jack_get_microseconds_from_hpet (void)
|
|
{
|
|
/* never called */
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HPET_SUPPORT */
|
|
|
|
#define HAVE_CLOCK_GETTIME 1
|
|
|
|
#ifndef HAVE_CLOCK_GETTIME
|
|
|
|
static jack_time_t jack_get_microseconds_from_system (void)
|
|
{
|
|
jack_time_t jackTime;
|
|
struct timeval tv;
|
|
|
|
gettimeofday (&tv, NULL);
|
|
jackTime = (jack_time_t) tv.tv_sec * 1000000 + (jack_time_t) tv.tv_usec;
|
|
return jackTime;
|
|
}
|
|
|
|
#else
|
|
|
|
static jack_time_t jack_get_microseconds_from_system (void)
|
|
{
|
|
jack_time_t jackTime;
|
|
struct timespec time;
|
|
|
|
#ifdef CLOCK_MONOTONIC_RAW
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
|
#else
|
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
|
#endif
|
|
jackTime = (jack_time_t) time.tv_sec * 1e6 +
|
|
(jack_time_t) time.tv_nsec / 1e3;
|
|
return jackTime;
|
|
}
|
|
|
|
#endif /* HAVE_CLOCK_GETTIME */
|
|
|
|
|
|
SERVER_EXPORT void JackSleep(long usec)
|
|
{
|
|
usleep(usec);
|
|
}
|
|
|
|
SERVER_EXPORT void InitTime()
|
|
{
|
|
/* nothing to do on a generic system - we use the system clock */
|
|
}
|
|
|
|
SERVER_EXPORT void EndTime()
|
|
{}
|
|
|
|
void SetClockSource(jack_timer_type_t source)
|
|
{
|
|
jack_log("Clock source : %s", ClockSourceName(source));
|
|
|
|
switch (source)
|
|
{
|
|
case JACK_TIMER_HPET:
|
|
if (jack_hpet_init () == 0) {
|
|
_jack_get_microseconds = jack_get_microseconds_from_hpet;
|
|
} else {
|
|
_jack_get_microseconds = jack_get_microseconds_from_system;
|
|
}
|
|
break;
|
|
|
|
case JACK_TIMER_SYSTEM_CLOCK:
|
|
default:
|
|
_jack_get_microseconds = jack_get_microseconds_from_system;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char* ClockSourceName(jack_timer_type_t source)
|
|
{
|
|
switch (source) {
|
|
case JACK_TIMER_HPET:
|
|
return "hpet";
|
|
case JACK_TIMER_SYSTEM_CLOCK:
|
|
#ifdef HAVE_CLOCK_GETTIME
|
|
return "system clock via clock_gettime";
|
|
#else
|
|
return "system clock via gettimeofday";
|
|
#endif
|
|
}
|
|
|
|
/* what is wrong with gcc ? */
|
|
return "unknown";
|
|
}
|
|
|
|
SERVER_EXPORT jack_time_t GetMicroSeconds()
|
|
{
|
|
return _jack_get_microseconds();
|
|
}
|
|
|
|
SERVER_EXPORT jack_time_t jack_get_microseconds()
|
|
{
|
|
return _jack_get_microseconds();
|
|
}
|
|
|