int ToneAlarm::write(file *filp, const char *buffer, size_t len) { // sanity-check the buffer for length and nul-termination if (len > _tune_max) return -EFBIG; // if we have an existing user tune, free it if (_user_tune != nullptr) { // if we are playing the user tune, stop if (_tune == _user_tune) { _tune = nullptr; _next = nullptr; } // free the old user tune free((void *)_user_tune); _user_tune = nullptr; } // if the new tune is empty, we're done if (buffer[0] == '\0') return OK; // allocate a copy of the new tune _user_tune = strndup(buffer, len); if (_user_tune == nullptr) return -ENOMEM; // and play it start_tune(_user_tune); return len; }
int ToneAlarm::ioctl(file *filp, int cmd, unsigned long arg) { int result = OK; DEVICE_DEBUG("ioctl %i %u", cmd, arg); // irqstate_t flags = enter_critical_section(); /* decide whether to increase the alarm level to cmd or leave it alone */ switch (cmd) { case TONE_SET_ALARM: DEVICE_DEBUG("TONE_SET_ALARM %u", arg); if (arg < TONE_NUMBER_OF_TUNES) { if (arg == TONE_STOP_TUNE) { // stop the tune _tune = nullptr; _next = nullptr; _repeat = false; _default_tune_number = 0; } else { /* always interrupt alarms, unless they are repeating and already playing */ if (!(_repeat && _default_tune_number == arg)) { /* play the selected tune */ _default_tune_number = arg; start_tune(_default_tunes[arg]); } } } else { result = -EINVAL; } break; default: result = -ENOTTY; break; } // leave_critical_section(flags); /* give it to the superclass if we didn't like it */ if (result == -ENOTTY) { result = CDev::ioctl(filp, cmd, arg); } return result; }
void ToneAlarm::next_note() { // do we have an inter-note gap to wait for? if (_silence_length > 0) { stop_note(); hrt_call_after(&_note_call, (hrt_abstime)_silence_length, (hrt_callout)next_trampoline, this); _silence_length = 0; return; } // make sure we still have a tune - may be removed by the write / ioctl handler if ((_next == nullptr) || (_tune == nullptr)) { stop_note(); return; } // parse characters out of the string until we have resolved a note unsigned note = 0; unsigned note_length = _note_length; unsigned duration; while (note == 0) { // we always need at least one character from the string int c = next_char(); if (c == 0) { goto tune_end; } _next++; switch (c) { case 'L': // select note length _note_length = next_number(); if (_note_length < 1) { goto tune_error; } break; case 'O': // select octave _octave = next_number(); if (_octave > 6) { _octave = 6; } break; case '<': // decrease octave if (_octave > 0) { _octave--; } break; case '>': // increase octave if (_octave < 6) { _octave++; } break; case 'M': // select inter-note gap c = next_char(); if (c == 0) { goto tune_error; } _next++; switch (c) { case 'N': _note_mode = MODE_NORMAL; break; case 'L': _note_mode = MODE_LEGATO; break; case 'S': _note_mode = MODE_STACCATO; break; case 'F': _repeat = false; break; case 'B': _repeat = true; break; default: goto tune_error; } break; case 'P': // pause for a note length stop_note(); hrt_call_after(&_note_call, (hrt_abstime)rest_duration(next_number(), next_dots()), (hrt_callout)next_trampoline, this); return; case 'T': { // change tempo unsigned nt = next_number(); if ((nt >= 32) && (nt <= 255)) { _tempo = nt; } else { goto tune_error; } break; } case 'N': // play an arbitrary note note = next_number(); if (note > 84) { goto tune_error; } if (note == 0) { // this is a rest - pause for the current note length hrt_call_after(&_note_call, (hrt_abstime)rest_duration(_note_length, next_dots()), (hrt_callout)next_trampoline, this); return; } break; case 'A'...'G': // play a note in the current octave note = _note_tab[c - 'A'] + (_octave * 12) + 1; c = next_char(); switch (c) { case '#': // up a semitone case '+': if (note < 84) { note++; } _next++; break; case '-': // down a semitone if (note > 1) { note--; } _next++; break; default: // 0 / no next char here is OK break; } // shorthand length notation note_length = next_number(); if (note_length == 0) { note_length = _note_length; } break; default: goto tune_error; } } // compute the duration of the note and the following silence (if any) duration = note_duration(_silence_length, note_length, next_dots()); // start playing the note start_note(note); // and arrange a callback when the note should stop hrt_call_after(&_note_call, (hrt_abstime)duration, (hrt_callout)next_trampoline, this); return; // tune looks bad (unexpected EOF, bad character, etc.) tune_error: syslog(LOG_ERR, "tune error\n"); _repeat = false; // don't loop on error // stop (and potentially restart) the tune tune_end: stop_note(); if (_repeat) { start_tune(_tune); } else { _tune = nullptr; _default_tune_number = 0; } return; }