You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
8.5 KiB
301 lines
8.5 KiB
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* Delay/beep functions used in dev-mode kernel selection.
|
|
*/
|
|
|
|
#include "sysincludes.h"
|
|
|
|
#include "crc32.h"
|
|
#include "gbb_header.h"
|
|
#include "utility.h"
|
|
#include "vboot_api.h"
|
|
#include "vboot_audio.h"
|
|
#include "vboot_audio_private.h"
|
|
#include "vboot_common.h"
|
|
|
|
/* BIOS doesn't have /usr/include */
|
|
#ifndef UINT_MAX
|
|
#define UINT_MAX 4294967295U /* 0xffffffff */
|
|
#endif
|
|
|
|
/*
|
|
* Need one second of noise in the first 22 seconds.
|
|
* Total delay >= 30 seconds, <= 60 seconds.
|
|
*/
|
|
#define REQUIRED_NOISE_TIME 1000
|
|
#define REQUIRED_NOISE_WITHIN 22000
|
|
#define REQUIRED_TOTAL_DELAY 30000
|
|
#define MAX_CUSTOM_DELAY 60000
|
|
|
|
/* These are visible externally only to make testing easier */
|
|
VbDevMusicNote default_notes_[] = { {20000, 0}, /* 20 seconds */
|
|
{250, 400}, /* two beeps */
|
|
{250, 0},
|
|
{250, 400},
|
|
{9250, 0} }; /* total 30 seconds */
|
|
uint32_t default_count_ = sizeof(default_notes_) / sizeof(VbDevMusicNote);
|
|
|
|
VbDevMusicNote short_notes_[] = { {2000, 0} }; /* two seconds */
|
|
uint32_t short_count_ = sizeof(short_notes_) / sizeof(VbDevMusicNote);
|
|
|
|
/* No need to dynamically allocate this, is there? */
|
|
static VbAudioContext au;
|
|
|
|
/* Convert from msecs to VbExGetTimer() units. */
|
|
static uint64_t ticks_per_msec = 0; /* Initialized by VbAudioOpen() */
|
|
static uint64_t VbMsecToTicks(uint16_t msec) {
|
|
return ticks_per_msec * msec;
|
|
}
|
|
|
|
/**
|
|
* Find and return a valid set of note events.
|
|
*
|
|
* We'll use the user's struct if possible, but we will still enforce the
|
|
* 30-second timeout and require at least a second of audible noise within that
|
|
* period. We allocate storage for two reasons: the user's struct will be in
|
|
* flash, which is slow to read, and we may need one extra note at the end to
|
|
* pad out the user's notes to a full 30 seconds. The caller should free it
|
|
* when finished.
|
|
*/
|
|
static void VbGetDevMusicNotes(VbAudioContext *audio, int use_short)
|
|
{
|
|
VbDevMusicNote *notebuf = 0;
|
|
VbDevMusicNote *builtin = 0;
|
|
VbDevMusic *hdr = CUSTOM_MUSIC_NOTES;
|
|
uint32_t maxsize = CUSTOM_MUSIC_MAXSIZE; /* always <= flash size (8M) */
|
|
uint32_t maxnotes, mysum, mylen, i;
|
|
uint32_t this_msecs, on_msecs, total_msecs;
|
|
uint32_t count;
|
|
|
|
VBDEBUG(("VbGetDevMusicNotes: use_short is %d, hdr is %p, "
|
|
"maxsize is %d\n", use_short, hdr, maxsize));
|
|
|
|
if (use_short) {
|
|
builtin = short_notes_;
|
|
count = short_count_;
|
|
goto nope;
|
|
}
|
|
|
|
builtin = default_notes_;
|
|
count = default_count_;
|
|
|
|
/* If we can't beep in the background, don't allow customization. */
|
|
if (!audio->background_beep)
|
|
goto nope;
|
|
|
|
if (!hdr || maxsize < sizeof(VbDevMusic))
|
|
goto nope;
|
|
|
|
if (0 != Memcmp(hdr->sig, "$SND", sizeof(hdr->sig))) {
|
|
VBDEBUG(("VbGetDevMusicNotes: bad sig\n"));
|
|
goto nope;
|
|
}
|
|
|
|
/*
|
|
* How many notes will fit in the flash region? One more than you'd
|
|
* think, because there's one note in the header itself.
|
|
*/
|
|
maxnotes = 1 + (maxsize - sizeof(VbDevMusic)) / sizeof(VbDevMusicNote);
|
|
if (hdr->count == 0 || hdr->count > maxnotes) {
|
|
VBDEBUG(("VbGetDevMusicNotes: count=%d maxnotes=%d\n",
|
|
hdr->count, maxnotes));
|
|
goto nope;
|
|
}
|
|
|
|
/*
|
|
* CUSTOM_MUSIC_MAXSIZE can't be larger than the size of the flash
|
|
* (around 8M or so) so this isn't really necessary, but let's be safe
|
|
* anyway.
|
|
*/
|
|
if ((sizeof(VbDevMusicNote) > UINT_MAX / hdr->count) ||
|
|
(sizeof(hdr->count) >
|
|
UINT_MAX - hdr->count * sizeof(VbDevMusicNote))) {
|
|
VBDEBUG(("VbGetDevMusicNotes: count=%d, just isn't right\n",
|
|
hdr->count));
|
|
goto nope;
|
|
}
|
|
|
|
/* Now we know this won't overflow */
|
|
mylen = (uint32_t)(sizeof(hdr->count) +
|
|
hdr->count * sizeof(VbDevMusicNote));
|
|
mysum = Crc32(&(hdr->count), mylen);
|
|
|
|
if (mysum != hdr->checksum) {
|
|
VBDEBUG(("VbGetDevMusicNotes: mysum=%08x, want=%08x\n",
|
|
mysum, hdr->checksum));
|
|
goto nope;
|
|
}
|
|
|
|
VBDEBUG(("VbGetDevMusicNotes: custom notes struct at %p\n", hdr));
|
|
|
|
/*
|
|
* Measure the audible sound up to the first 22 seconds, being careful
|
|
* to avoid rollover. The note time is 16 bits, and the note count is
|
|
* 32 bits. The product should fit in 64 bits.
|
|
*/
|
|
total_msecs = 0;
|
|
on_msecs = 0;
|
|
for (i=0; i < hdr->count; i++) {
|
|
this_msecs = hdr->notes[i].msec ;
|
|
if (this_msecs) {
|
|
total_msecs += this_msecs;
|
|
if (total_msecs <= REQUIRED_NOISE_WITHIN &&
|
|
hdr->notes[i].frequency >= 100 &&
|
|
hdr->notes[i].frequency <= 2000)
|
|
on_msecs += this_msecs;
|
|
}
|
|
}
|
|
|
|
/* We require at least one second of noise in the first 22 seconds */
|
|
VBDEBUG(("VbGetDevMusicNotes: with %d msecs of sound to begin\n",
|
|
on_msecs));
|
|
if (on_msecs < REQUIRED_NOISE_TIME)
|
|
goto nope;
|
|
|
|
/*
|
|
* We'll also require that the total time be less than a minute. No
|
|
* real reason, it just gives us less to worry about.
|
|
*/
|
|
VBDEBUG(("VbGetDevMusicNotes: lasting %d msecs\n", total_msecs));
|
|
if (total_msecs > MAX_CUSTOM_DELAY) {
|
|
goto nope;
|
|
}
|
|
|
|
/* One more check, just to be paranoid. */
|
|
if (hdr->count > (UINT_MAX / sizeof(VbDevMusicNote) - 1)) {
|
|
VBDEBUG(("VbGetDevMusicNotes: they're all out to get me!\n"));
|
|
goto nope;
|
|
}
|
|
|
|
/* Looks good. Allocate the space (plus one) and copy it over. */
|
|
notebuf = VbExMalloc((hdr->count + 1) * sizeof(VbDevMusicNote));
|
|
Memcpy(notebuf, hdr->notes, hdr->count * sizeof(VbDevMusicNote));
|
|
count = hdr->count;
|
|
|
|
/* We also require at least 30 seconds of delay. */
|
|
if (total_msecs < REQUIRED_TOTAL_DELAY) {
|
|
/*
|
|
* If the total time is less than 30 seconds, the needed
|
|
* difference will fit in 16 bits.
|
|
*/
|
|
this_msecs = (REQUIRED_TOTAL_DELAY - total_msecs) & 0xffff;
|
|
notebuf[hdr->count].msec = this_msecs;
|
|
notebuf[hdr->count].frequency = 0;
|
|
count++;
|
|
VBDEBUG(("VbGetDevMusicNotes: adding %d msecs of silence\n",
|
|
this_msecs));
|
|
}
|
|
|
|
/* Done */
|
|
audio->music_notes = notebuf;
|
|
audio->note_count = count;
|
|
audio->free_notes_when_done = 1;
|
|
return;
|
|
|
|
nope:
|
|
/* No custom notes, use the default. The count is already set. */
|
|
VBDEBUG(("VbGetDevMusicNotes: using %d default notes\n", count));
|
|
audio->music_notes = builtin;
|
|
audio->note_count = count;
|
|
audio->free_notes_when_done = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialization function. Returns context for processing dev-mode delay.
|
|
*/
|
|
VbAudioContext *VbAudioOpen(VbCommonParams *cparams)
|
|
{
|
|
GoogleBinaryBlockHeader *gbb = cparams->gbb;
|
|
VbAudioContext *audio = &au;
|
|
int use_short = 0;
|
|
uint64_t a, b;
|
|
|
|
/* Note: may need to allocate things here in future */
|
|
|
|
/* Calibrate audio delay */
|
|
a = VbExGetTimer();
|
|
VbExSleepMs(10);
|
|
b = VbExGetTimer();
|
|
ticks_per_msec = (b - a) / 10ULL ;
|
|
VBDEBUG(("VbAudioOpen() - ticks_per_msec is %" PRIu64 "\n",
|
|
ticks_per_msec));
|
|
|
|
/* Initialize */
|
|
Memset(audio, 0, sizeof(*audio));
|
|
audio->background_beep = 1;
|
|
audio->play_until = b; /* "zero" starts now */
|
|
|
|
/* See if we have full background sound capability or not. */
|
|
if (VBERROR_SUCCESS != VbExBeep(0,0)) {
|
|
VBDEBUG(("VbAudioOpen() - VbExBeep() is limited\n"));
|
|
audio->background_beep = 0;
|
|
}
|
|
|
|
/*
|
|
* Prepare to generate audio/delay event. Use a short developer screen
|
|
* delay if indicated by GBB flags.
|
|
*/
|
|
if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1
|
|
&& (gbb->flags & GBB_FLAG_DEV_SCREEN_SHORT_DELAY)) {
|
|
VBDEBUG(("VbAudioOpen() - using short dev screen delay\n"));
|
|
use_short = 1;
|
|
}
|
|
|
|
VbGetDevMusicNotes(audio, use_short);
|
|
VBDEBUG(("VbAudioOpen() - note count %d\n", audio->note_count));
|
|
|
|
return audio;
|
|
}
|
|
|
|
/**
|
|
* Caller should loop without extra delay until this returns false.
|
|
*/
|
|
int VbAudioLooping(VbAudioContext *audio)
|
|
{
|
|
uint64_t now;
|
|
uint16_t freq = audio->current_frequency;
|
|
uint16_t msec = 0;
|
|
int looping = 1;
|
|
|
|
now = VbExGetTimer();
|
|
while (audio->next_note < audio->note_count &&
|
|
now >= audio->play_until) {
|
|
freq = audio->music_notes[audio->next_note].frequency;
|
|
msec = audio->music_notes[audio->next_note].msec;
|
|
audio->play_until += VbMsecToTicks(msec);
|
|
audio->next_note++;
|
|
}
|
|
|
|
if (now >= audio->play_until) {
|
|
looping = 0;
|
|
freq = 0;
|
|
}
|
|
|
|
/* Do action here. */
|
|
if (audio->background_beep) {
|
|
if (audio->current_frequency != freq) {
|
|
VbExBeep(0, freq);
|
|
audio->current_frequency = freq;
|
|
}
|
|
} else if (freq && msec) {
|
|
VbExBeep(msec, freq);
|
|
now = VbExGetTimer();
|
|
}
|
|
|
|
audio->last_time = now;
|
|
return looping;
|
|
}
|
|
|
|
/**
|
|
* Caller should call this prior to booting.
|
|
*/
|
|
void VbAudioClose(VbAudioContext *audio)
|
|
{
|
|
VbExBeep(0,0);
|
|
if (audio->free_notes_when_done)
|
|
VbExFree(audio->music_notes);
|
|
}
|