| /* 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 "2sysincludes.h" |
| #include "2common.h" |
| |
| #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, <= 5 minutes. |
| */ |
| #define REQUIRED_NOISE_TIME 1000 |
| #define REQUIRED_NOISE_WITHIN 22000 |
| #define REQUIRED_TOTAL_DELAY 30000 |
| #define MAX_CUSTOM_DELAY 300000 |
| |
| /* 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; |
| |
| /* Flag to override GBB_FLAG_DEV_SCREEN_SHORT_DELAY gbb flag */ |
| int audio_open_count = 0; |
| |
| /* 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; |
| |
| VB2_DEBUG("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))) { |
| VB2_DEBUG("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) { |
| VB2_DEBUG("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))) { |
| VB2_DEBUG("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) { |
| VB2_DEBUG("VbGetDevMusicNotes: mysum=%08x, want=%08x\n", |
| mysum, hdr->checksum); |
| goto nope; |
| } |
| |
| VB2_DEBUG("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 */ |
| VB2_DEBUG("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 5 minutes. No |
| * real reason, it just gives us less to worry about. |
| */ |
| VB2_DEBUG("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)) { |
| VB2_DEBUG("VbGetDevMusicNotes: they're all out to get me!\n"); |
| goto nope; |
| } |
| |
| /* Looks good. Allocate the space (plus one) and copy it over. */ |
| notebuf = malloc((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++; |
| VB2_DEBUG("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. */ |
| VB2_DEBUG("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 ; |
| VB2_DEBUG("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)) { |
| VB2_DEBUG("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))) { |
| VB2_DEBUG("VbAudioOpen() - using short dev screen delay\n"); |
| use_short = 1; |
| } |
| |
| /* Only use short dev screen delay on the first audio */ |
| if (audio_open_count++ > 0) |
| use_short = 0; |
| |
| VbGetDevMusicNotes(audio, use_short); |
| VB2_DEBUG("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; |
| |
| /* if no audio context, never timeout */ |
| if (!audio) |
| return 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) |
| { |
| if (!audio) |
| return; |
| |
| VbExBeep(0,0); |
| if (audio->free_notes_when_done) |
| free(audio->music_notes); |
| } |