#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <string.h> #include "dbg_putchar.h" #define MIDI_BASE 18 #define ZERO_BASE 28 #define KEYS_COUNT 76 #define hiz(port, dir) do { \ (dir) = 0; \ (port) = 0; \ } while(0) #define alow(port, dir) do { \ (dir) = 0xff; \ (port) = 0; \ } while(0) uint8_t keys[KEYS_COUNT]; /* Get state of a row by its index * starting from 1 to 13 */ uint8_t getRow(uint8_t idx) { if (idx <= 8) { return (PINC & (1 << (8 - idx))); } else if (idx >= 9 && idx <= 11) { return (PINE & (1 << (11 - idx))); } else if (idx == 12) { return (PINA & (1 << PIN6)); } else if (idx == 13) { return (PINA & (1 << PIN4)); } return 0; } inline void activateColumn1(uint8_t idx) { PORTD = 0x00 | (1 << (8 - idx)); PORTB = 0x00; } void activateColumn2(uint8_t idx) { if (idx <= 3) { PORTB = 0x00 | (1 << (idx + 4)); PORTD = 0x00; } else if (idx == 4) { PORTB = 0x00 | (1 << PIN4); PORTD = 0x00; } else if (idx == 5 || idx == 6) { PORTD = 0x00 | (1 << (idx - 5)); PORTB = 0x00; } } inline void deactivateColumns(void) { PORTD = 0x00; PORTB = 0x00; } inline void initPorts(void) { hiz(PORTA, DDRA); hiz(PORTC, DDRC); hiz(PORTE, DDRE); PORTB = 0x00; DDRB = 0xfe; DDRD = 0xff; } void resetRows(void) { /* output low */ alow(PORTC, DDRC); alow(PORTE, DDRE); /* don't touch PA7 & PA5 */ DDRA |= 0x5f; PORTA &= ~0x5f; _delay_us(10); /* back to floating input */ hiz(PORTC, DDRC); hiz(PORTE, DDRE); DDRA &= ~0x5f; } /* base MIDI note number is 25: C#0 */ int main(void) { uint8_t row, col, layer; uint8_t note, offset; initPorts(); memset(keys, 0, sizeof(keys)); dbg_tx_init(); dbg_putchar('O'); dbg_putchar('K'); while(1) { for (layer = 0; layer < 2; layer++) { for (col = 1; col <= 6; col++) { if (!layer) activateColumn1(col); else activateColumn2(col); for (row = 1; row <= 13; row++) { note = 6 * row + col + MIDI_BASE; offset = note - ZERO_BASE; if (getRow(row)) { if (!layer) { /* increase velocity counter */ if (keys[offset] < 254 && !(keys[offset] & 0x80)) keys[offset]++; } else { if (!(keys[offset] & 0x80)) { /* generate note-on event */ dbg_putchar(0x90); dbg_putchar(note); /*dbg_putchar(keys[offset]);*/ dbg_putchar(0x7f); /* stop counting */ keys[offset] |= 0x80; } } } else { if (layer) continue; if (keys[offset] & 0x80) { /* generate note off event */ dbg_putchar(0x90); dbg_putchar(note); dbg_putchar(0x00); /* reset key state */ keys[offset] = 0x00; } } } deactivateColumns(); resetRows(); } } } return 0; }
import serial import binascii import struct import intelhex import sys class TSB(object): CONFIRM = '!' REQUEST = '?' def __init__(self, port): self.port = serial.Serial(port, baudrate=9600, timeout=1) self.flashsz = 0 def check(self): if not self.flashsz: raise Exception("Not activated") def activate(self): self.port.write("@@@") (self.tsb, self.version, self.status, self.sign, self.pagesz, self.flashsz, self.eepsz) = \ struct.unpack("<3sHB3sBHH", self.port.read(14)) self.port.read(2) self.pagesz *= 2 self.flashsz *= 2 self.eepsz += 1 assert(self.port.read() == self.CONFIRM) def rflash(self, progress=None, size=0): self.check() self.port.write("f") self.addr = 0 self.flash = "" size = self.flashsz if not size else size while self.addr < size: if progress is not None: progress("read", self.addr, size) self.port.write(self.CONFIRM) page = self.port.read(self.pagesz) if len(page) != self.pagesz: raise Exception("Received page too short: %d" % len(page)) self.addr += len(page) self.flash += page return self.flash.rstrip('\xff') def wflash(self, data, progress=None): if len(data) % self.pagesz != 0: data = data + "\xff" * (self.pagesz - (len(data) % self.pagesz)) assert(len(data) % self.pagesz == 0) self.check() self.port.write("F") self.addr = 0 assert(self.port.read() == self.REQUEST) while self.addr < len(data): if progress is not None: progress("write", self.addr, len(data)) self.port.write(self.CONFIRM) self.port.write(data[self.addr:self.addr + self.pagesz]) self.addr += self.pagesz assert(self.port.read() == self.REQUEST) self.port.write(self.REQUEST) return self.port.read() == self.CONFIRM def vflash(self, data, progress=None): fw = self.rflash(progress, len(data)) return fw == data def info(self): print "Tiny Safe Bootloader: %s" % self.tsb print "Page size: %d" % self.pagesz print "Flash size: %d" % self.flashsz print "EEPROM size: %d" % self.eepsz if __name__ == "__main__": import argparse def progress(op, addr, total): sys.stdout.write("\r%s address: $%0.4x/$%0.4x" % (op, addr, total)) sys.stdout.flush() parser = argparse.ArgumentParser() parser.add_argument("filename", help="firmware file in Intel HEX format") parser.add_argument("--device", help="Serial port to use for programming", default="/dev/ttyUSB0") args = parser.parse_args() tsb = TSB(args.device) tsb.activate() tsb.info() fw = intelhex.IntelHex(args.filename) assert(tsb.wflash(fw.tobinstr(), progress)) assert(tsb.vflash(fw.tobinstr(), progress)) print "\nOK\n"
init/main.c
file, the start_kernel
function, to determine the location, in which everything collapses. Thus, it turned out that the last thing the kernel hrtimers_init()
do was call the hrtimers_init()
function, which initializes the high-resolution timers and interrupts them. This code depends on the specific platform, and in our case is in arch/arm/plat-s5p/hr-time-rtc.c
. As I said, one of the main features of the kernel with the PREEMPT RT patch is that interrupts become threads. This is also possible in a regular kernel, but the kernel with PREEMPT RT by default tries to do almost all interrupts. Further analysis of the code showed that the kthreadd_task task is used for these threads, which is initialized at the very end of the start_kernel
function — much later than the initialization of the timers occurs. The fall occurred due to the fact that the kernel interrupted the timer interrupt and made it threaded, while kthreadd_task was still NULL. This is solved by setting for individual interrupts that should not be streamed under any circumstances, the IRQF_NO_THREAD flag which was added to the timer interrupt flags in hr-time-rtc.c
. Hooray! The kernel has booted, but this is just the beginning ...drivers/tty/serial/samsung.c
). I added the required support to the driver based on this patch, after which the debugger started working.serial_core.c
and serial_core.h
, affecting in general all serial ports. Because in the PREEMPT RT kernel, you cannot use spin_lock_t in drivers that are NO_THREAD, but you must use raw_spinlock_t.arch/arm/mach-s5pv210/mach-tq210.c
, which is the main collection point for all the information about the configuration and devices of a particular machine (in our case, the boards). We comment on the allocation of memory - a call to the function s5p_reserve_bootmem
, and we get 120 MB of additional memory for the synthesizer to work.sound/soc/samsung/dma.c
to 128 bytes, after which the minimum buffer size was reduced to a few milliseconds without compromising stability and performance. #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/time.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <sysexits.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <termios.h> #include <jack/jack.h> #include <jack/midiport.h> #define UART_SPEED B9600 jack_port_t *output_port; jack_client_t *jack_client = NULL; int input_fd; void init_serial(int fd) { struct termios termios; int res; res = tcgetattr (fd, &termios); if (res < 0) { fprintf (stderr, "Termios get error: %s\n", strerror(errno)); exit (EXIT_FAILURE); } cfsetispeed (&termios, UART_SPEED); cfsetospeed (&termios, UART_SPEED); termios.c_iflag &= ~(IGNPAR | IXON | IXOFF); termios.c_iflag |= IGNPAR; termios.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CREAD | CLOCAL); termios.c_cflag |= CS8; termios.c_cflag |= CREAD; termios.c_cflag |= CLOCAL; termios.c_lflag &= ~(ICANON | ECHO); termios.c_cc[VMIN] = 3; termios.c_cc[VTIME] = 0; res = tcsetattr (fd, TCSANOW, &termios); if (res < 0) { fprintf (stderr, "Termios set error: %s\n", strerror(errno)); exit (EXIT_FAILURE); } } double get_time(void) { double seconds; int ret; struct timeval tv; ret = gettimeofday(&tv, NULL); if (ret) { perror("gettimeofday"); exit(EX_OSERR); } seconds = tv.tv_sec + tv.tv_usec / 1000000.0; return seconds; } double get_delta_time(void) { static double previously = -1.0; double now; double delta; now = get_time(); if (previously == -1.0) { previously = now; return 0; } delta = now - previously; previously = now; assert(delta >= 0.0); return delta; } static double nframes_to_ms(jack_nframes_t nframes) { jack_nframes_t sr; sr = jack_get_sample_rate(jack_client); assert(sr > 0); return (nframes * 1000.0) / (double)sr; } static double nframes_to_seconds(jack_nframes_t nframes) { return nframes_to_ms(nframes) / 1000.0; } static jack_nframes_t ms_to_nframes(double ms) { jack_nframes_t sr; sr = jack_get_sample_rate(jack_client); assert(sr > 0); return ((double)sr * ms) / 1000.0; } static jack_nframes_t seconds_to_nframes(double seconds) { return ms_to_nframes(seconds * 1000.0); } static void process_midi_output(jack_nframes_t nframes) { int t, res; void *port_buffer; char midi_buffer[3]; jack_nframes_t last_frame_time; port_buffer = jack_port_get_buffer(output_port, nframes); if (port_buffer == NULL) { printf("jack_port_get_buffer failed, cannot send anything.\n"); return; } jack_midi_clear_buffer(port_buffer); last_frame_time = jack_last_frame_time(jack_client); t = seconds_to_nframes(get_delta_time()); res = read(input_fd, midi_buffer, sizeof(midi_buffer)); if (res < 0 && errno == EAGAIN) return; res = jack_midi_event_write(port_buffer, t, midi_buffer, 3); if (res != 0) { printf("jack_midi_event_write failed, NOTE LOST."); } } static int process_callback(jack_nframes_t nframes, void *notused) { if (nframes <= 0) { printf("Process callback called with nframes = 0; bug in JACK?"); return 0; } process_midi_output(nframes); return 0; } int connect_to_input_port(const char *port) { int ret; ret = jack_port_disconnect(jack_client, output_port); if (ret) { printf("Cannot disconnect MIDI port."); return -3; } ret = jack_connect(jack_client, jack_port_name(output_port), port); if (ret) { printf("Cannot connect to %s.", port); return -4; } printf("Connected to %s.", port); return 0; } static void init_jack(void) { int i, err; jack_client = jack_client_open("midibridge", JackNullOption, NULL); if (jack_client == NULL) { printf("Could not connect to the JACK server; run jackd first?"); exit(EXIT_FAILURE); } err = jack_set_process_callback(jack_client, process_callback, 0); if (err) { printf("Could not register JACK process callback."); exit(EXIT_FAILURE); } char port_name[32]; snprintf(port_name, sizeof(port_name), "midi_out"); output_port = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); if (output_port == NULL) { printf("Could not register JACK output port '%s'.", port_name); exit(EXIT_FAILURE); } if (jack_activate(jack_client)) { printf("Cannot activate JACK client."); exit(EXIT_FAILURE); } } static void usage(void) { fprintf(stderr, "usage: midibridge -a <input port>\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int ch; char *autoconnect_port_name = NULL; while ((ch = getopt(argc, argv, "a:")) != -1) { switch (ch) { case 'a': autoconnect_port_name = strdup(optarg); break; default: usage(); } } input_fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (input_fd < 0) { fprintf(stderr, "Cannot open serial port %s\n", strerror(errno)); return EXIT_FAILURE; } init_serial (input_fd); init_jack(); if (autoconnect_port_name) { if (connect_to_input_port(autoconnect_port_name)) { printf("Couldn't connect to '%s', exiting.", autoconnect_port_name); exit(EXIT_FAILURE); } } getc(stdin); return 0; }
jack-smf-player
, which I compiled for ARM and WAV / MP3 via mplayer with support for audio output in JACK.Source: https://habr.com/ru/post/224987/
All Articles