Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ALSA YM2413ドライバ

Fadis
August 15, 2015

ALSA YM2413ドライバ

第1回 カーネル/VM名古屋での発表
あまり語られる事の無いALSAの機能 ALSA Sequencerを使ってYM2413のドライバを書きます

Fadis

August 15, 2015
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. ALSA
    YM2413υϥΠό
    NAOMASA MATSUBAYASHI

    View full-size slide

  2. @fadis_
    Twitter
    Github
    Speakerdeck
    https://speakerdeck.com/fadis
    https://github.com/Fadis/
    NAOMASA MATSUBAYASHI
    ීஈ͸େࡕͰήʔϜ࡞ͬͯ·͢

    View full-size slide

  3. Advanced
    Linux
    Sound
    Architecture

    View full-size slide

  4. #include !
    #include !
    #include !
    #include !
    int main() {!
    snd_pcm_t *pcm = nullptr;!
    if(snd_pcm_open(&pcm,"default",SND_PCM_STREAM_PLAYBACK,0)<0)return -1;!
    if(snd_pcm_set_params(pcm,SND_PCM_FORMAT_S16,!
    SND_PCM_ACCESS_RW_INTERLEAVED,1,44100,1,1)<0)return -1;!
    std::array< int16_t, 1024 > buf;!
    int pos = 0, ret;!
    while(1) {!
    for(int16_t &elem:buf)elem=sin(double(pos++)*440.*M_PI/44100.)*32767;!
    if((ret=snd_pcm_writei(pcm,(const void*)buf.data(),buf.size()))<0)!
    if(snd_pcm_recover(pcm,ret,0)<0)return -1;!
    }!
    }
    Linux؀ڥͰPCMΦʔσΟΦΛ
    ࠶ੜ͢Δͷʹ࢖͏
    https://github.com/Fadis/kernelvm_20150815_samples/blob/master/pcm.cpp

    View full-size slide

  5. #include !
    #include !
    #include !
    #include !
    int main() {!
    snd_pcm_t *pcm = nullptr;!
    if(snd_pcm_open(&pcm,"default",SND_PCM_STREAM_PLAYBACK,0)<0)return -1;!
    if(snd_pcm_set_params(pcm,SND_PCM_FORMAT_S16,!
    SND_PCM_ACCESS_RW_INTERLEAVED,1,44100,1,1)<0)return -1;!
    std::array< int16_t, 1024 > buf;!
    int pos = 0, ret;!
    while(1) {!
    for(int16_t &elem:buf)elem=sin(double(pos++)*440.*M_PI/44100.)*32767;!
    if((ret=snd_pcm_writei(pcm,(const void*)buf.data(),buf.size()))<0)!
    if(snd_pcm_recover(pcm,ret,0)<0)return -1;!
    }!
    }
    σόΠεΛopenͯ͠
    ϑΥʔϚοτΛࢦఆ
    https://github.com/Fadis/kernelvm_20150815_samples/blob/master/pcm.cpp

    View full-size slide

  6. #include !
    #include !
    #include !
    #include !
    int main() {!
    snd_pcm_t *pcm = nullptr;!
    if(snd_pcm_open(&pcm,"default",SND_PCM_STREAM_PLAYBACK,0)<0)return -1;!
    if(snd_pcm_set_params(pcm,SND_PCM_FORMAT_S16,!
    SND_PCM_ACCESS_RW_INTERLEAVED,1,44100,1,1)<0)return -1;!
    std::array< int16_t, 1024 > buf;!
    int pos = 0, ret;!
    while(1) {!
    for(int16_t &elem:buf)elem=sin(double(pos++)*440.*M_PI/44100.)*32767;!
    if((ret=snd_pcm_writei(pcm,(const void*)buf.data(),buf.size()))<0)!
    if(snd_pcm_recover(pcm,ret,0)<0)return -1;!
    }!
    }
    PCMσʔλΛwrite
    https://github.com/Fadis/kernelvm_20150815_samples/blob/master/pcm.cpp

    View full-size slide

  7. Control PCM
    Raw
    MIDI
    VirMIDI
    Seq.MIDI
    Sequencer
    Timer
    Control
    API
    PCM
    API
    RawMIDI
    API
    Sequencer
    API
    Control
    Apps
    Mixer
    Apps
    PCM
    Apps
    MIDI
    Apps
    Sequencer
    User client
    ग़య: http://www.alsa-project.org/~tiwai/lk2k/lk2k.html
    Χʔωϧ
    Ϣʔβۭؒ

    View full-size slide

  8. Control PCM
    Raw
    MIDI
    VirMIDI
    Seq.MIDI
    Sequencer
    Timer
    Control
    API
    PCM
    API
    RawMIDI
    API
    Sequencer
    API
    Control
    Apps
    Mixer
    Apps
    PCM
    Apps
    MIDI
    Apps
    Sequencer
    User client
    ग़య: http://www.alsa-project.org/~tiwai/lk2k/lk2k.html
    Χʔωϧ
    Ϣʔβۭؒ
    Իྔ΍࿥Իιʔεͷબ୒౳
    ϋʔυ΢ΣΞશମͷઃఆΛߦ͏

    View full-size slide

  9. Control PCM
    Raw
    MIDI
    VirMIDI
    Seq.MIDI
    Sequencer
    Timer
    Control
    API
    PCM
    API
    RawMIDI
    API
    Sequencer
    API
    Control
    Apps
    Mixer
    Apps
    PCM
    Apps
    MIDI
    Apps
    Sequencer
    User client
    ग़య: http://www.alsa-project.org/~tiwai/lk2k/lk2k.html
    Χʔωϧ
    Ϣʔβۭؒ
    PCMΛ࠶ੜग़དྷΔσόΠεʹ
    PCMσʔλΛૹͬͯԻΛग़͢

    View full-size slide

  10. Control PCM
    Raw
    MIDI
    VirMIDI
    Seq.MIDI
    Sequencer
    Timer
    Control
    API
    PCM
    API
    RawMIDI
    API
    Sequencer
    API
    Control
    Apps
    Mixer
    Apps
    PCM
    Apps
    MIDI
    Apps
    Sequencer
    User client
    ग़య: http://www.alsa-project.org/~tiwai/lk2k/lk2k.html
    Χʔωϧ
    Ϣʔβۭؒ
    γϯηαΠβʔʹίϚϯυΛૹͬͯ
    MIDI౳Λԋ૗͢Δ

    View full-size slide

  11. CPU
    γϯηαΠβʔ
    ♪ ♪

    γϯηαΠβʔʹίϚϯυΛૹͬͯ
    MIDI౳Λԋ૗͢Δ
    ָේ ೾ܗ

    View full-size slide

  12. Control PCM
    Raw
    MIDI
    VirMIDI
    Seq.MIDI
    Sequencer
    Timer
    Control
    API
    PCM
    API
    RawMIDI
    API
    Sequencer
    API
    Control
    Apps
    Mixer
    Apps
    PCM
    Apps
    MIDI
    Apps
    Sequencer
    User client
    ग़య: http://www.alsa-project.org/~tiwai/lk2k/lk2k.html
    Χʔωϧ
    Ϣʔβۭؒ
    Ϣʔβۭ͔ؒΒLinuxΧʔωϧʹ
    ԋ૗ΠϕϯτΛ఻͑Δํ๏͸2ͭ͋Δ

    View full-size slide

  13. RawMIDI API
    ΧʔωϧʹMIDIϝοηʔδΛͦͷ··ૹΔ
    σόΠευϥΠό͸MIDIϝοηʔδΛͦͷ··ड͚औΔ
    $ printf '\x90\x3c\x7f' >/dev/snd/midiC0D0
    σόΠεϑΝΠϧʹ௚઀
    MIDIϝοηʔδΛॻ͖ࠐΉͱ
    ରԠ͢ΔσόΠε͔ΒԻ͕ग़Δ

    View full-size slide

  14. typedef struct snd_seq_event {!
    snd_seq_event_type_t type; !
    unsigned char flags; !
    unsigned char tag; !
    unsigned char queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t source; !
    snd_seq_addr_t dest; !
    union {!
    snd_seq_ev_note_t note; !
    snd_seq_ev_ctrl_t control; !
    snd_seq_ev_raw8_t raw8; !
    snd_seq_ev_raw32_t raw32; !
    snd_seq_ev_ext_t ext; !
    snd_seq_ev_queue_control_t queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t addr; !
    snd_seq_connect_t connect; !
    snd_seq_result_t result; !
    } data; !
    } snd_seq_event_t;
    Sequencer API
    snd_seq_event
    ΄΅.*%*ͱಉ͡৘ใΛදݱ
    ݻఆ௕
    λΠϜελϯϓͱ
    ૹ৴ݩɺૹ৴ઌͷ৘ใ͕
    ௥Ճ͞Ε͍ͯΔ

    View full-size slide

  15. typedef struct snd_seq_event {!
    snd_seq_event_type_t type; !
    unsigned char flags; !
    unsigned char tag; !
    unsigned char queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t source; !
    snd_seq_addr_t dest; !
    union {!
    snd_seq_ev_note_t note; !
    snd_seq_ev_ctrl_t control; !
    snd_seq_ev_raw8_t raw8; !
    snd_seq_ev_raw32_t raw32; !
    snd_seq_ev_ext_t ext; !
    snd_seq_ev_queue_control_t queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t addr; !
    snd_seq_connect_t connect; !
    snd_seq_result_t result; !
    } data; !
    } snd_seq_event_t;
    Sequencer API
    Կ͕ى͔ͬͨ͜
    伴൫͕ԡ͞Εͨ
    ϐον͕มԽͨ͠
    ͍ͭى͔ͬͨ͜
    Ͳͷ$MJFOU͔Βདྷ͔ͨ
    Ͳͷ$MJFOUѼʹૹΒΕ͔ͨ
    ݸʑͷΠϕϯτͷ
    ৄࡉ͕ೖΔVOJPO
    ԡ͞Εͨ伴൫͸ͲΕ
    ָثͷछྨ͸ͲΕ

    View full-size slide

  16. Χʔωϧ
    Ϣʔβۭؒ
    Sequencer API
    Sequencer
    Core
    Client A Client B
    ♪ ♪

    snd_seq_eventΛ౤͛ͨΓड͚औͬͨΓ͢Δ
    ΞϓϦέʔγϣϯ͸clientͱݺ͹ΕΔ
    ♪ ♪

    client͔ΒૹΒΕͨsnd_seq_event͸
    ΧʔωϧͷSequencer Core͕
    εέδϡʔϦϯά͠ɺద੾ͳclientʹ഑ૹ͢Δ

    View full-size slide

  17. $ aplaymidi -p 14:0 hoge.mid &!
    $ aconnect -l!
    ΫϥΠΞϯτ 0: 'System' [λΠϓ=Χʔωϧ]!
    0 'Timer '!
    1 'Announce '!
    ΫϥΠΞϯτ 14: 'Midi Through' [λΠϓ=Χʔωϧ]!
    0 'Midi Through Port-0'!
    ઀ଓݩ: 128:0!
    ΫϥΠΞϯτ 128: 'aplaymidi' [λΠϓ=Ϣʔβ]!
    0 'aplaymidi '!
    ઀ଓઌ: 14:0
    aconnect
    Sequencer Clientͷ઀ଓͷ
    ֬ೝͱมߋΛߦ͏ίϚϯυ

    View full-size slide

  18. $ aplaymidi -p 14:0 hoge.mid &!
    $ aconnect -l!
    ΫϥΠΞϯτ 0: 'System' [λΠϓ=Χʔωϧ]!
    0 'Timer '!
    1 'Announce '!
    ΫϥΠΞϯτ 14: 'Midi Through' [λΠϓ=Χʔωϧ]!
    0 'Midi Through Port-0'!
    ઀ଓݩ: 128:0!
    ΫϥΠΞϯτ 128: 'aplaymidi' [λΠϓ=Ϣʔβ]!
    0 'aplaymidi '!
    ઀ଓઌ: 14:0
    aconnect
    aplaymidiͰ14:0ʹ޲͔ͬͯΠϕϯτΛ౤͛Δ

    View full-size slide

  19. $ aplaymidi -p 14:0 hoge.mid &!
    $ aconnect -l!
    ΫϥΠΞϯτ 0: 'System' [λΠϓ=Χʔωϧ]!
    0 'Timer '!
    1 'Announce '!
    ΫϥΠΞϯτ 14: 'Midi Through' [λΠϓ=Χʔωϧ]!
    0 'Midi Through Port-0'!
    ઀ଓݩ: 128:0!
    ΫϥΠΞϯτ 128: 'aplaymidi' [λΠϓ=Ϣʔβ]!
    0 'aplaymidi '!
    ઀ଓઌ: 14:0
    aconnect
    ΫϥΠΞϯτ14:0ͱ
    ઌఔىಈͨ͠aplaymidi͕઀ଓ͞Ε͍ͯΔ

    View full-size slide

  20. static void init_seq(void)!
    {!
    int err;!
    err = snd_seq_open(!
    &seq, "default", SND_SEQ_OPEN_DUPLEX, 0!
    );!
    check_snd("open sequencer", err);!
    err = snd_seq_set_client_name(seq, "aplaymidi");!
    check_snd("set client name", err);!
    client = snd_seq_client_id(seq);!
    check_snd("get client id", client);!
    }
    snd_seq_openͰ
    Sequencer ClientΛ࡞Δ
    IUUQTHJUIVCDPNCFBSSXBMTBVUJMTCMPCNBTUFS
    TFRBQMBZNJEJBQMBZNJEJD

    View full-size slide

  21. static void init_seq(void)!
    {!
    int err;!
    err = snd_seq_open(!
    &seq, "default", SND_SEQ_OPEN_DUPLEX, 0!
    );!
    check_snd("open sequencer", err);!
    err = snd_seq_set_client_name(seq, "aplaymidi");!
    check_snd("set client name", err);!
    client = snd_seq_client_id(seq);!
    check_snd("get client id", client);!
    }
    snd_seq_openͰ
    Sequencer ClientΛ࡞Δ
    IUUQTHJUIVCDPNCFBSSXBMTBVUJMTCMPCNBTUFS
    TFRBQMBZNJEJBQMBZNJEJD

    View full-size slide

  22. static void create_source_port(void)!
    {!
    snd_seq_port_info_t *pinfo;!
    int err;!
    snd_seq_port_info_alloca(&pinfo);!
    snd_seq_port_info_set_port(pinfo, 0);!
    snd_seq_port_info_set_port_specified(pinfo, 1);!
    snd_seq_port_info_set_name(pinfo, "aplaymidi");!
    snd_seq_port_info_set_capability(pinfo, 0);!
    snd_seq_port_info_set_type(pinfo,!
    SND_SEQ_PORT_TYPE_MIDI_GENERIC |!
    SND_SEQ_PORT_TYPE_APPLICATION);!
    err = snd_seq_create_port(seq, pinfo);!
    check_snd("create port", err);!
    }
    snd_seq_create_portͰ
    Sequencer ClientʹϙʔτΛ࡞੒͢Δ

    View full-size slide

  23. $ aconnect -l!
    …!
    ΫϥΠΞϯτ 128: 'hoge' [λΠϓ=Ϣʔβ]!
    0 'hoge port 0 '!
    1 'hoge port 1 ‘!
    …!
    $ aconnect 128:1 14:0
    1ͭͷSequencer Clientʹ͸
    ෳ਺ͷϙʔτΛઃ͚Δࣄ͕ग़དྷΔ
    Clientಉ࢜ͷ઀ଓ͸ϙʔτ୯ҐͰߦ͏
    ΫϥΠΞϯτͷϙʔτΛ
    ΫϥΠΞϯτͷϙʔτʹ઀ଓ

    View full-size slide

  24. typedef struct snd_seq_event {!
    snd_seq_event_type_t type; !
    unsigned char flags; !
    unsigned char tag; !
    unsigned char queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t source; !
    snd_seq_addr_t dest; !
    union {!
    snd_seq_ev_note_t note; !
    snd_seq_ev_ctrl_t control; !
    snd_seq_ev_raw8_t raw8; !
    snd_seq_ev_raw32_t raw32; !
    snd_seq_ev_ext_t ext; !
    snd_seq_ev_queue_control_t queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t addr; !
    snd_seq_connect_t connect; !
    snd_seq_result_t result; !
    } data; !
    } snd_seq_event_t;
    Ͳͷϙʔτ͔ΒΠϕϯτΛඈ͹͔͢͸
    event.source.portͰࢦఆ͢Δ
    typedef struct snd_seq_addr {!
    unsigned char client; !
    unsigned char port; !
    } snd_seq_addr_t;
    ͜Ε

    View full-size slide

  25. static void connect_ports(void)!
    {!
    int i, err;!
    !
    for (i = 0; i < port_count; ++i) {!
    err = snd_seq_connect_to(seq, 0, ports[i].client, ports[i].port);!
    if (err < 0)!
    fatal("Cannot connect to port %d:%d - %s",!
    ports[i].client, ports[i].port, snd_strerror(err));!
    }!
    }
    snd_seq_connect_to Ͱ
    2ͭͷϙʔτΛ઀ଓ

    View full-size slide

  26. /* this blocks when the output pool has been filled */!
    err = snd_seq_event_output(seq, &ev);!
    check_snd("output event", err);
    ͋ͱ͸snd_seq_event_outputͰ
    ΠϕϯτΛ౤͛Δ
    snd_seq_event_t *event;!
    err = snd_seq_event_input(seq, &event);!
    if (err < 0)!
    break;
    ͋Δ͍͸snd_seq_event_inputͰ
    ඈΜͰ͖ͨΠϕϯτΛड͚औΔ
    IUUQTHJUIVCDPNCFBSSXBMTBVUJMTCMPCNBTUFS
    TFRBTFREVNQBTFREVNQD

    View full-size slide

  27. snd_seq_event_t *event;!
    err = snd_seq_event_input(seq, &event);!
    if (err < 0)!
    break;
    ͋Δ͍͸snd_seq_event_inputͰ
    ඈΜͰ͖ͨΠϕϯτΛड͚औΔ
    snd_seq_event_input͸σϑΥϧτͰ͸
    Πϕϯτ͕ඈΜͰ͘Δ·ͰϒϩοΫ͢Δ͕
    snd_seq_nonblock( seq, 1 );
    snd_seq_nonblockͰ
    non-blockingϞʔυʹ͓ͯ͘͠ͱ
    Πϕϯτ͕ແ͍࣌͸௚ͪʹEAGAIN͕ฦΔΑ͏ʹͳΔ

    View full-size slide

  28. $ aplaymidi -p 14:0 hoge.mid &!
    $ aconnect -l!
    ΫϥΠΞϯτ 0: 'System' [λΠϓ=Χʔωϧ]!
    0 'Timer '!
    1 'Announce '!
    ΫϥΠΞϯτ 14: 'Midi Through' [λΠϓ=Χʔωϧ]!
    0 'Midi Through Port-0'!
    ઀ଓݩ: 128:0!
    ΫϥΠΞϯτ 128: 'aplaymidi' [λΠϓ=Ϣʔβ]!
    0 'aplaymidi '!
    ઀ଓઌ: 14:0
    ϢʔβΫϥΠΞϯτͱΧʔωϧΫϥΠΞϯτ

    View full-size slide

  29. Χʔωϧ
    Ϣʔβۭؒ
    Sequencer
    Core
    Client A Client B
    Client C
    Sequencer Client͸Χʔωϧ಺Ͱ΋
    ࡞Δ͜ͱ͕Ͱ͖Δ
    λΠϓ: Ϣʔβ
    λΠϓ: Ϣʔβ
    λΠϓ: Χʔωϧ

    View full-size slide

  30. Χʔωϧ
    ۭؒ
    Sequencer
    Core
    Client A Client B
    Client C
    σόΠευϥΠό
    γϯηαΠβʔ
    σόΠεͷυϥΠό
    ͱ͔͕࢖͏

    View full-size slide

  31. Կނ͔ALSAυϥΠόͷυΩϡϝϯτͰ
    Ұ੾৮ΕΒΕ͍ͯͳ͍
    IUUQXXXBMTBQSPKFDUPSHNBJOJOEFYQIQ
    "-4"@%SJWFS@%PDVNFOUBUJPO

    View full-size slide

  32. SequencerͷΠϕϯτΛड͚औΔͱ
    printkͰग़ྗ͢ΔclientΛॻ͍ͯΈΑ͏
    $ dmesg!
    …!
    [ 326.691419] NoteOn: 1 61 63!
    [ 326.691434] NoteOn: 1 66 63!
    [ 326.691436] NoteOn: 3 57 65!
    [ 326.691437] NoteOn: 4 49 78!
    [ 326.691438] NoteOn: 9 46 90!
    [ 326.692419] NoteOff: 9 46!
    [ 326.791411] NoteOff: 3 57!
    [ 326.791433] NoteOff: 4 49!
    [ 326.793413] NoteOn: 3 61 65!
    [ 326.793429] NoteOn: 4 49 78!
    $

    View full-size slide

  33. }!
    if (!platform_get_drvdata(device_)) {!
    platform_device_unregister(device_);!
    device = NULL;!
    #ifdef MODULE!
    printk(!
    KERN_ERR!
    "Card-printkMIDI soundcard not found or device busy\n”!
    );!
    #endif!
    snd_printkmidi_unregister_all();!
    return -ENODEV;!
    }!
    device = device_;!
    return 0;!
    }!
    !
    static void __exit alsa_card_printkmidi_exit(void) {!
    snd_printkmidi_unregister_all();!
    }!
    !
    module_init(alsa_card_printkmidi_init);!
    module_exit(alsa_card_printkmidi_exit);!
    !
    MODULE_AUTHOR("Naomasa Matsubayashi <>");!
    MODULE_DESCRIPTION("printk Client Driver");!
    MODULE_LICENSE("GPL");
    SequencerͷΠϕϯτΛड͚औΔͱ
    printkͰग़ྗ͢ΔclientΛॻ͍ͯΈΑ͏
    ιʔείʔυ͸ͪ͜Β
    https://github.com/Fadis/kernelvm_20150815_samples/blob/
    master/kernel/printkmidi.c

    View full-size slide

  34. static int __init alsa_card_printkmidi_init(void) {!
    int err;!
    struct platform_device *device_;!
    err = platform_driver_register(&snd_printkmidi_driver);!
    if (err < 0) return err;!
    device_ = platform_device_register_simple(!
    "snd_printk", 0, NULL, 0!
    ); !
    if (IS_ERR(device_)) {!
    device = NULL;!
    #ifdef MODULE!
    printk(!
    KERN_ERR!
    "Card-printkMIDI soundcard not found or device busy\n”!
    );!
    #endif!
    snd_printkmidi_unregister_all();!
    return -ENODEV;!
    }!
    if (!platform_get_drvdata(device_)) {!
    platform_device_unregister(device_);!
    device = NULL;!
    #ifdef MODULE!
    printk(!
    KERN_ERR!
    "Card-printkMIDI soundcard not found or device busy\n”!
    ιʔείʔυ͸ͪ͜Β
    https://github.com/Fadis/kernelvm_20150815_samples/blob/
    master/kernel/printkmidi.c

    View full-size slide

  35. !
    !
    !
    !
    !
    static int __init alsa_card_printkmidi_init(void) {!
    int err;!
    struct platform_device *device_;!
    err = platform_driver_register(&snd_printkmidi_driver);!
    if (err < 0) return err;!
    device_ = platform_device_register_simple(!
    "snd_printk", 0, NULL, 0!
    ); !
    if (IS_ERR(device_)) {!
    device = NULL;!
    #ifdef MODULE!
    printk(!
    KERN_ERR!
    "Card-printkMIDI soundcard not found or device busy\n”!
    );!
    #endif!
    snd_printkmidi_unregister_all();!
    return -ENODEV;!
    }!
    if (!platform_get_drvdata(device_)) {!
    platform_device_unregister(device_);!
    device = NULL;!
    platform busʹ৽͍͠σόΠε
    snd_printk Λ௥Ճ
    ιʔείʔυ͸ͪ͜Β
    https://github.com/Fadis/kernelvm_20150815_samples/blob/
    master/kernel/printkmidi.c

    View full-size slide

  36. static int snd_printkmidi_probe( struct platform_device *devptr ) {!
    struct snd_card *card;!
    struct snd_printk *printk_;!
    struct snd_seq_printk_dev *rdev;!
    int err;!
    int client;!
    card = NULL;!
    err = snd_card_new(!
    &devptr->dev, -1, NULL, THIS_MODULE,!
    sizeof(struct snd_printk ), &card!
    );!
    if( err < 0 ) {!
    dev_err( &devptr->dev, "Unable to create snd_card: %d\n", err );!
    return err;!
    }!
    printk_ = card->private_data;!
    printk_->card = card;!
    !
    /* create client */!
    client = snd_seq_create_kernel_client( card, 0, "printk" );!
    if ( client < 0 ) {!
    snd_card_free( card );!
    return client;!
    }!
    rdev = create_port( client, 0 );!
    if ( rdev == NULL ) {!

    View full-size slide

  37. static int snd_printkmidi_probe( struct platform_device *devptr ) {!
    struct snd_card *card;!
    struct snd_printk *printk_;!
    struct snd_seq_printk_dev *rdev;!
    int err;!
    int client;!
    card = NULL;!
    err = snd_card_new(!
    &devptr->dev, -1, NULL, THIS_MODULE,!
    sizeof(struct snd_printk ), &card!
    );!
    if( err < 0 ) {!
    dev_err( &devptr->dev, "Unable to create snd_card: %d\n", err );!
    return err;!
    }!
    printk_ = card->private_data;!
    printk_->card = card;!
    !
    /* create client */!
    client = snd_seq_create_kernel_client( card, 0, "printk" );!
    if ( client < 0 ) {!
    snd_card_free( card );!
    return client;!
    }!
    rdev = create_port( client, 0 );!
    if ( rdev == NULL ) {!
    snd_card_new Ͱ
    ৽͍͠α΢ϯυΧʔυΛ࡞੒
    ͜ͷลΓ͸
    PCMͷα΢ϯυΧʔυͷ৔߹ͱҰॹ

    View full-size slide

  38. }!
    printk_ = card->private_data;!
    printk_->card = card;!
    !
    /* create client */!
    client = snd_seq_create_kernel_client( card, 0, "printk" );!
    if ( client < 0 ) {!
    snd_card_free( card );!
    return client;!
    }!
    rdev = create_port( client, 0 );!
    if ( rdev == NULL ) {!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    return -ENOMEM;!
    }!
    printk_->rdev = rdev;!
    rdev->card = card;!
    rdev->chset = snd_midi_channel_alloc_set( 16 );!
    if ( rdev->chset == NULL ) {!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    err = -ENOMEM;!
    }!
    rdev->client = client;!
    snd_seq_create_kernel_client Ͱ
    Sequencer ClientΛ࡞੒
    Client͕ग़དྷͨΒϙʔτΛ࡞੒͢Δ

    View full-size slide

  39. static struct snd_seq_printk_dev *create_port( int client, int type )
    {!
    struct snd_seq_port_info pinfo;!
    struct snd_seq_port_callback pcb;!
    struct snd_seq_printk_dev *rdev;!
    if ((rdev = kzalloc(sizeof(*rdev), GFP_KERNEL)) == NULL)!
    return NULL;!
    rdev->client = client;!
    memset(&pinfo, 0, sizeof(pinfo));!
    pinfo.addr.client = client;!
    strcpy( pinfo.name, "printk" );!
    pinfo.capability = SNDRV_SEQ_PORT_CAP_READ |!
    SNDRV_SEQ_PORT_CAP_SUBS_READ;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE |!
    SNDRV_SEQ_PORT_CAP_SUBS_WRITE;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;!
    pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC!
    | SNDRV_SEQ_PORT_TYPE_SOFTWARE!
    | SNDRV_SEQ_PORT_TYPE_PORT;!
    pinfo.midi_channels = 16;!
    memset(&pcb, 0, sizeof(pcb));!
    pcb.owner = THIS_MODULE;!
    pcb.subscribe = printk_subscribe;!
    pcb.unsubscribe = printk_unsubscribe;!
    pcb.use = printk_use;!
    pcb.unuse = printk_unuse;!

    View full-size slide

  40. memset(&pinfo, 0, sizeof(pinfo));!
    pinfo.addr.client = client;!
    strcpy( pinfo.name, "printk" );!
    pinfo.capability = SNDRV_SEQ_PORT_CAP_READ |!
    SNDRV_SEQ_PORT_CAP_SUBS_READ;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE |!
    SNDRV_SEQ_PORT_CAP_SUBS_WRITE;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;!
    pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC!
    | SNDRV_SEQ_PORT_TYPE_SOFTWARE!
    | SNDRV_SEQ_PORT_TYPE_PORT;!
    pinfo.midi_channels = 16;!
    memset(&pcb, 0, sizeof(pcb));!
    pcb.owner = THIS_MODULE;!
    pcb.subscribe = printk_subscribe;!
    pcb.unsubscribe = printk_unsubscribe;!
    pcb.use = printk_use;!
    pcb.unuse = printk_unuse;!
    pcb.event_input = printk_input;!
    pcb.private_data = rdev;!
    pinfo.kernel = &pcb;!
    if ( snd_seq_kernel_client_ctl(!
    client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo!
    ) < 0 ) {!
    kfree(rdev);!
    return NULL;!
    ͜ͷϙʔτ͕ͲΜͳػೳΛ͍࣋ͬͯΔ͔ͱ

    View full-size slide

  41. memset(&pinfo, 0, sizeof(pinfo));!
    pinfo.addr.client = client;!
    strcpy( pinfo.name, "printk" );!
    pinfo.capability = SNDRV_SEQ_PORT_CAP_READ |!
    SNDRV_SEQ_PORT_CAP_SUBS_READ;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE |!
    SNDRV_SEQ_PORT_CAP_SUBS_WRITE;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;!
    pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC!
    | SNDRV_SEQ_PORT_TYPE_SOFTWARE!
    | SNDRV_SEQ_PORT_TYPE_PORT;!
    pinfo.midi_channels = 16;!
    memset(&pcb, 0, sizeof(pcb));!
    pcb.owner = THIS_MODULE;!
    pcb.subscribe = printk_subscribe;!
    pcb.unsubscribe = printk_unsubscribe;!
    pcb.use = printk_use;!
    pcb.unuse = printk_unuse;!
    pcb.event_input = printk_input;!
    pcb.private_data = rdev;!
    pinfo.kernel = &pcb;!
    if ( snd_seq_kernel_client_ctl(!
    client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo!
    ) < 0 ) {!
    kfree(rdev);!
    return NULL;!
    ϙʔτʹଞͷϙʔτ͕઀ଓ͖ͯͨ͠Γ
    Πϕϯτ͕ඈΜͰ͖ͨ࣌ʹ
    ݺͼग़͞ΕΔؔ਺Λઃఆ

    View full-size slide

  42. memset(&pinfo, 0, sizeof(pinfo));!
    pinfo.addr.client = client;!
    strcpy( pinfo.name, "printk" );!
    pinfo.capability = SNDRV_SEQ_PORT_CAP_READ |!
    SNDRV_SEQ_PORT_CAP_SUBS_READ;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE |!
    SNDRV_SEQ_PORT_CAP_SUBS_WRITE;!
    pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;!
    pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC!
    | SNDRV_SEQ_PORT_TYPE_SOFTWARE!
    | SNDRV_SEQ_PORT_TYPE_PORT;!
    pinfo.midi_channels = 16;!
    memset(&pcb, 0, sizeof(pcb));!
    pcb.owner = THIS_MODULE;!
    pcb.subscribe = printk_subscribe;!
    pcb.unsubscribe = printk_unsubscribe;!
    pcb.use = printk_use;!
    pcb.unuse = printk_unuse;!
    pcb.event_input = printk_input;!
    pcb.private_data = rdev;!
    pinfo.kernel = &pcb;!
    if ( snd_seq_kernel_client_ctl(!
    client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo!
    ) < 0 ) {!
    kfree(rdev);!
    return NULL;!
    snd_seq_kernel_client_ctl ʹ
    SND_SEQ_IOCTL_CREATE_PORT Λࢦఆͯ͠
    ϙʔτΛ࡞੒

    View full-size slide

  43. dev_err( &devptr->dev, "Unable to create snd_card: %d\n", err );!
    return err;!
    }!
    printk_ = card->private_data;!
    printk_->card = card;!
    !
    /* create client */!
    client = snd_seq_create_kernel_client( card, 0, "printk" );!
    if ( client < 0 ) {!
    snd_card_free( card );!
    return client;!
    }!
    rdev = create_port( client, 0 );!
    if ( rdev == NULL ) {!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    return -ENOMEM;!
    }!
    printk_->rdev = rdev;!
    rdev->card = card;!
    rdev->chset = snd_midi_channel_alloc_set( 16 );!
    if ( rdev->chset == NULL ) {!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    err = -ENOMEM;!

    View full-size slide

  44. if ( rdev == NULL ) {!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    return -ENOMEM;!
    }!
    printk_->rdev = rdev;!
    rdev->card = card;!
    rdev->chset = snd_midi_channel_alloc_set( 16 );!
    if ( rdev->chset == NULL ) {!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    err = -ENOMEM;!
    }!
    rdev->client = client;!
    rdev->chset->client = client;!
    rdev->chset->private_data = rdev;!
    !
    strcpy(card->driver, "printk");!
    strcpy(card->shortname, "printk");!
    sprintf(card->longname, "printk Card %i", devptr->id + 1);!
    err = snd_card_register(card);!
    if (!err) {!
    platform_set_drvdata(devptr, card);!
    return 0;!
    }!
    MIDIεςʔτΛอ࣋͢Δߏ଄ମΛ࡞੒

    View full-size slide

  45. MIDI emulation
    ϠϚϋͷ'.Իݯ౳͸ঢ়ଶΛຆͲ࣋ͨͣ
    /05&0/࣌ʹԻ৭ʹؔ͢Δ৘ใΛ
    ·ͱΊͯϨδελʹॻ͖ࠐΉ
    "-4"4FRVFODFSͷΠϕϯτ͸
    $MJFOU͕ঢ়ଶΛهԱ͍ͯ͠ΔࣄΛ
    લఏͱͯ͠ɺঢ়ଶͷมԽΛ௨஌ͯ͘͠Δ
    ͜ΕΒͷσόΠεͷυϥΠόΛॻ͘ҝʹ͸
    υϥΠό͕ঢ়ଶΛ͓֮͑ͯ͘ඞཁ͕͋Δ

    View full-size slide

  46. MIDI emulation
    ͜ΕΒͷσόΠεͷυϥΠόΛॻ͘ҝʹ͸
    υϥΠό͕ঢ়ଶΛ͓֮͑ͯ͘ඞཁ͕͋Δ
    .*%*γʔέϯαͷঢ়ଶΛ͓֮͑ͯ͘ߏ଄ମ
    snd_midi_channel_set
    struct snd_midi_channel_set {!
    void *private_data;!
    int client;!
    int port;!
    int max_channels;!
    struct snd_midi_channel *channels;!
    unsigned char midi_mode;!
    unsigned char gs_master_volume;!
    unsigned char gs_chorus_mode;!
    unsigned char gs_reverb_mode;!
    };

    View full-size slide

  47. MIDI emulation
    ֤νϟωϧͷঢ়ଶΛ͓֮͑ͯ͘ߏ଄ମ
    snd_midi_channel
    struct snd_midi_channel *channels;!
    unsigned char midi_mode;!
    unsigned char gs_master_volume;!
    unsigned char gs_chorus_mode;!
    unsigned char gs_reverb_mode;!
    };
    struct snd_midi_channel {!
    void *private;!
    int number;!
    int client;!
    int port;!
    unsigned char midi_mode;!
    unsigned int drum_channel:1, param_type:1;!
    unsigned char midi_aftertouch;!
    unsigned char midi_pressure;!
    unsigned char midi_program;!
    short midi_pitchbend;!
    unsigned char control[128];!
    unsigned char note[128];!
    short gm_rpn_pitch_bend_range;!
    short gm_rpn_fine_tuning;!
    short gm_rpn_coarse_tuning;!
    };
    Իྔ΍ύϯϙοτ
    ൃԻதͷԻ֊
    ϐονϕϯυͷ෯

    View full-size slide

  48. MIDI emulation
    ͜ΕΒͷσόΠεͷυϥΠόΛॻ͘ҝʹ͸
    υϥΠό͕ঢ়ଶΛ͓֮͑ͯ͘ඞཁ͕͋Δ
    .*%*γʔέϯαͷঢ়ଶΛ͓֮͑ͯ͘ߏ଄ମ
    snd_midi_channel_set
    snd_seq_midi_emulϞδϡʔϧ͕
    ड͚औͬͨΠϕϯτʹج͍ͮͯ
    ͜ͷߏ଄ମͷ஋Λߋ৽͢Δ࢓૊ΈΛఏڙ

    View full-size slide

  49. return client;!
    }!
    rdev = create_port( client, 0 );!
    if ( rdev == NULL ) {!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    return -ENOMEM;!
    }!
    printk_->rdev = rdev;!
    rdev->card = card;!
    rdev->chset = snd_midi_channel_alloc_set( 16 );!
    if ( rdev->chset == NULL ) {!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    err = -ENOMEM;!
    }!
    rdev->client = client;!
    rdev->chset->client = client;!
    rdev->chset->private_data = rdev;!
    !
    strcpy(card->driver, "printk");!
    strcpy(card->shortname, "printk");!
    sprintf(card->longname, "printk Card %i", devptr->id + 1);!
    err = snd_card_register(card);!
    if (!err) {!
    platform_set_drvdata(devptr, card);!
    TOE@TFR@NJEJ@FNVMͷؔ਺
    snd_midi_channel_alloc_set ͸
    MIDIγʔέϯαͷঢ়ଶΛอ࣋͢Δߏ଄ମ
    snd_midi_channel_setΛ࡞੒͢Δ
    Ҿ਺͸MIDIͷνϟωϧ਺

    View full-size slide

  50. }!
    printk_->rdev = rdev;!
    rdev->card = card;!
    rdev->chset = snd_midi_channel_alloc_set( 16 );!
    if ( rdev->chset == NULL ) {!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free( card );!
    err = -ENOMEM;!
    }!
    rdev->client = client;!
    rdev->chset->client = client;!
    rdev->chset->private_data = rdev;!
    !
    strcpy(card->driver, "printk");!
    strcpy(card->shortname, "printk");!
    sprintf(card->longname, "printk Card %i", devptr->id + 1);!
    err = snd_card_register(card);!
    if (!err) {!
    platform_set_drvdata(devptr, card);!
    return 0;!
    }!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free(card);!
    return err;!
    α΢ϯυΧʔυΛొ࿥

    View full-size slide

  51. !
    struct snd_midi_op printk_ops = {
    .note_on = snd_printk_note_on,
    .note_off = snd_printk_note_off,
    .key_press = snd_printk_key_press,
    .note_terminate = snd_printk_terminate_note,
    .control = snd_printk_control,
    .nrpn = snd_printk_nrpn,
    .sysex = snd_printk_sysex,
    };
    static int printk_input(
    struct snd_seq_event *ev, int direct,
    void *private_data, int atomic, int hop
    ) {
    struct snd_seq_printk_dev *rdev;
    rdev = private_data;
    if (!(rdev->flags & SNDRV_PRINTKMIDI_USE))
    return 0;
    snd_midi_process_event( &printk_ops, ev, rdev->chset );
    return 0;
    }

    View full-size slide

  52. !
    struct snd_midi_op printk_ops = {
    .note_on = snd_printk_note_on,
    .note_off = snd_printk_note_off,
    .key_press = snd_printk_key_press,
    .note_terminate = snd_printk_terminate_note,
    .control = snd_printk_control,
    .nrpn = snd_printk_nrpn,
    .sysex = snd_printk_sysex,
    };
    static int printk_input(
    struct snd_seq_event *ev, int direct,
    void *private_data, int atomic, int hop
    ) {
    struct snd_seq_printk_dev *rdev;
    rdev = private_data;
    if (!(rdev->flags & SNDRV_PRINTKMIDI_USE))
    return 0;
    snd_midi_process_event( &printk_ops, ev, rdev->chset );
    return 0;
    }
    ϙʔτʹΠϕϯτ͕དྷͨΒ
    snd_seq_midi_emulͷ
    snd_midi_process_eventʹ౉͢

    View full-size slide

  53. struct snd_midi_op printk_ops = {
    .note_on = snd_printk_note_on,
    .note_off = snd_printk_note_off,
    .key_press = snd_printk_key_press,
    .note_terminate = snd_printk_terminate_note,
    .control = snd_printk_control,
    .nrpn = snd_printk_nrpn,
    .sysex = snd_printk_sysex,
    };
    static int printk_input(
    struct snd_seq_event *ev, int direct,
    void *private_data, int atomic, int hop
    ) {
    struct snd_seq_printk_dev *rdev;
    rdev = private_data;
    if (!(rdev->flags & SNDRV_PRINTKMIDI_USE))
    return 0;
    snd_midi_process_event( &printk_ops, ev, rdev->chset );
    return 0;
    }
    !
    • 伴൫Λԡͨ͠
    • 伴൫Λ཭ͨ͠
    • ΩʔϓϨογϟʔ͕มԽͨ͠
    • ࠓ͙͢ԻΛࢭΊΔඞཁ͕͋Δ
    • ϐον౳͕มԽͨ͠
    • NRPNΛड͚औͬͨ
    • SysExΛड͚औͬͨ ʜ࣌ʹݺͼग़͞ΕΔؔ਺

    View full-size slide

  54. void snd_printk_note_on(
    void *p, int note, int vel,
    struct snd_midi_channel *chan
    ) {
    unsigned long flags;
    bool send_program_change = false;
    struct snd_seq_printk_dev *rdev;
    rdev = p;
    if( chan->midi_program !=
    rdev->current_program[ chan->number ] ) {
    rdev->current_program[ chan->number ] =
    chan->midi_program;
    send_program_change = true;
    }
    if( send_program_change )
    printk(
    "Prog: %d %d\n", chan->number, chan->midi_program
    );
    printk(
    "NoteOn: %d %d %d\n”, chan->number, note,

    View full-size slide

  55. void snd_printk_note_on(
    void *p, int note, int vel,
    struct snd_midi_channel *chan
    ) {
    unsigned long flags;
    bool send_program_change = false;
    struct snd_seq_printk_dev *rdev;
    rdev = p;
    if( chan->midi_program !=
    rdev->current_program[ chan->number ] ) {
    rdev->current_program[ chan->number ] =
    chan->midi_program;
    send_program_change = true;
    }
    if( send_program_change )
    printk(
    "Prog: %d %d\n", chan->number, chan->midi_program
    );
    printk(
    "NoteOn: %d %d %d\n”, chan->number, note,
    ୈ2Ҿ਺
    ԡ͞Εͨ伴൫ͷϊʔτφϯόʔ(Ի֊)
    ୈ3Ҿ਺
    ԡ͞Εͨ伴൫ͷϕϩγςΟ
    ୈ1Ҿ਺
    snd_midi_channel_set ʹઃఆͨ͠Ϣʔβσʔλ
    ୈ4Ҿ਺
    伴൫͕ԡ͞ΕͨMIDIνϟωϧͷঢ়ଶ

    View full-size slide

  56. ) {
    unsigned long flags;
    bool send_program_change = false;
    struct snd_seq_printk_dev *rdev;
    rdev = p;
    if( chan->midi_program !=
    rdev->current_program[ chan->number ] ) {
    rdev->current_program[ chan->number ] =
    chan->midi_program;
    send_program_change = true;
    }
    if( send_program_change )
    printk(
    "Prog: %d %d\n", chan->number, chan->midi_program
    );
    printk(
    "NoteOn: %d %d %d\n”, chan->number, note,
    vel * chan->gm_volume * chan->gm_expression / 128 / 128
    );
    }
    printkͰग़ྗ

    View full-size slide

  57. # modprobe snd_printkmidi!
    # aplaymidi -l!
    Port Client name Port name!
    14:0 Midi Through Midi Through Port-0!
    16:0 printk printk
    ৽͍͠ALSA Sequencer Client
    printk

    View full-size slide

  58. # aplaymidi --port 16:0 hoge.mid
    ͜ͷSequencer ClientͰద౰ͳSMFϑΝΠϧΛ࠶ੜ
    $ dmesg!
    …!
    [ 326.691419] NoteOn: 1 61 63!
    [ 326.691434] NoteOn: 1 66 63!
    [ 326.691436] NoteOn: 3 57 65!
    [ 326.691437] NoteOn: 4 49 78!
    [ 326.691438] NoteOn: 9 46 90!
    [ 326.692419] NoteOff: 9 46!
    [ 326.791411] NoteOff: 3 57!
    [ 326.791433] NoteOff: 4 49!
    [ 326.793413] NoteOn: 3 61 65!
    [ 326.793429] NoteOn: 4 49 78!
    $
    ΧʔωϧϩάʹΠϕϯτ͕ྲྀΕͯ͘Δ

    View full-size slide

  59. printk͡Όͳͯ͘
    ࣮ࡍʹσόΠε͔ΒԻΛग़ͯ͠Έ͍ͨ

    View full-size slide

  60. ALSA Sequencer͕αϙʔτ͍ͯ͠Δ
    ϋʔυ΢ΣΞ
    YAMAHA OPL3
    YAMAHA OPL4
    E-MU 8000
    E-MU 10k1
    1992೥ࠒ
    1995೥ࠒ
    1994೥ࠒ
    1998೥ࠒ
    α΢ϯυΧʔυʹγϯηαΠβʔ͕උΘ͍ͬͯΔͷ͸
    ਵ෼ੲͷ࿩ʹͳͬͯ͠·ͬͨ

    View full-size slide

  61. ୈճΧʔωϧVMؔ੢
    ৐ࢉ໋ྩ͕࢒೦ͳ
    ARM Cortex-M0+ͷϚΠίϯͰ
    ιϑτ΢ΣΞFMԻݯΛ࡞ͬͨ࿩

    View full-size slide

  62. ϫλγ
    '.Իݯ
    νϣοτσΩϧ
    !ͭ͘͹

    View full-size slide

  63. ϫλγ
    '.Իݯ
    ϞοτσΩϧ

    View full-size slide

  64. IUUQTIUMBCOFU
    1+ࢯ
    Arduinoͷ
    YM2413 shieldΛ
    ࡞ͬͨੌ͍ਓ
    ϫλγ
    '.Իݯ
    ϞοτσΩϧ

    View full-size slide

  65. ίϨΛ͋͛·͠ΐ͏

    View full-size slide

  66. YAMAHA YM2413
    FMԻݯ

    View full-size slide

  67. α΢ϯυσόΠεͩ
    ALSAυϥΠόΛॻ͜͏
    PJ͞Μ͋Γ͕ͱ͏͍͟͝·͢
    YAMAHA YM2413
    FMԻݯ

    View full-size slide

  68. ઀ଓ
    D0..D7
    A0
    8bitύϥϨϧ
    ϚϧνϓϨΫεόε

    View full-size slide

  69. Z80A
    ΞυϨεόε
    σʔλόε
    ηΨϚʔΫ***
    ೥୅౰࣌ͷ઀ଓྫ

    View full-size slide

  70. ͪΐ͏Ͳྑ͍ύϥϨϧόε͕֎·Ͱग़͍ͯͳ͍
    ΠϚυΩͷϚΠίϯϘʔυ
    GPIO/I2C/SPI/PWM/ADC/LCD/etc.
    UART
    USB
    Ethernet
    Z80A
    ΞυϨεόε
    σʔλόε

    View full-size slide

  71. GPIOͰ௨৴͠Α͏

    View full-size slide

  72. YM2413ͷσʔλγʔτ͸طʹ
    YAMAHAͷެࣜαΠτ͔Βແ͘ͳ͍ͬͯΔ͕
    MSX޲͚ʹॻ͔Εͨ৘ใ͕୔ࢁ࢒͍ͬͯΔ
    http://d4.princess.ne.jp/msx/datas/OPLL/YM2413AP.html
    YM2413ΞϓϦέʔγϣϯϚχϡΞϧ

    View full-size slide

  73. A0
    CS
    WE
    D0..D7
    10nsҎ্ 25nsҎ্
    nsҎ্
    nsҎ্
    nsҎ্
    nsҎ্ nsҎ্
    ஗͘ͱ΋1MHz͘Β͍ͰGPIOΛૢ࡞͍ͨ͠
    όΠτʹ͖ͭ0.2µs͘Β͍ͰૹΕΔ

    View full-size slide

  74. LinuxΧʔωϧͰ1MHzͰGPIOΛ͍͡Δʹ͸
    ࡞ઓ1
    udelay( 1 );
    ࡞ઓ2
    usleep_range( 1 );
    ࡞ઓ3
    1µsຖʹGPIOͷ஋Λॻ͖׵͑ΔϚΠίϯͱ
    I2CͰ઀ଓ
    ஗͘ͱ΋1MHz͘Β͍ͰGPIOΛૢ࡞͍ͨ͠

    View full-size slide

  75. SCL
    SDA
    "3.ϚΠίϯΛ࢖ͬͯ*$ʹม׵
    YM2413ͷIOిѹ͸5V͚ͩͲ
    2.7V͋Ε͹HIGHʹͳΔ͔Β
    IO3.3VͷLPC1114ʹ௚݁

    View full-size slide

  76. SCL
    SDA
    "3.ϚΠίϯΛ࢖ͬͯ*$ʹม׵
    BeagleBoneBlack
    EXT P8
    I2C2_SCL
    19
    I2C2_SDA
    20

    View full-size slide

  77. λΠϚʔׂΓࠐΈ
    main()
    I2CׂΓࠐΈ
    ड৴Ωϡʔ ૹ৴Ωϡʔ GPIO
    I2C
    ىಈ
    ίϚϯυΛड৴
    όεʹྲྀ͢஋ʹ
    ม׵
    όεͷ஋Λߋ৽

    View full-size slide

  78. i2c-devΛ࢖ͬͯ֬ೝ
    $ modprobe i2c-dev!
    $ i2cdetect!
    00: —-!
    20: 20 -- --
    $ i2cset -y 2 0x20 0x10!
    $ i2cset -y 2 0x20 0x60!
    $ i2cset -y 2 0x20 0x7F
    i2cdetectͰσόΠεͷଘࡏΛ֬ೝ
    i2csetͰԻ͕ग़Ε͹੒ޭ

    View full-size slide

  79. !
    !
    !
    !
    !
    !
    static int snd_ym2413_probe(!
    struct i2c_client *bus,!
    const struct i2c_device_id *id_!
    ) {!
    struct snd_card *card;!
    struct snd_ym2413 *ym2413;!
    struct snd_seq_ym2413_dev *rdev;!
    int err;!
    int client;!
    card = NULL;!
    err = snd_card_new(!
    &bus->dev, -1, NULL, THIS_MODULE,!
    sizeof(struct snd_ym2413 ), &card!
    );!
    if( err < 0 ) {!
    dev_err( &bus->dev, "Unable to create snd_card: %d\n", err );!
    return err;!
    }!
    ym2413 = card->private_data;!
    ym2413->card = card;!
    σόΠευϥΠό͸جຊతʹ
    snd_printkͱಉ͡

    View full-size slide

  80. !
    !
    !
    !
    !
    static struct i2c_device_id snd_ym2413_idtable[] = { !
    { "ym2413", 0 },!
    { } !
    };!
    !
    static struct i2c_driver snd_ym2413_driver = { !
    .driver = { !
    .name = "snd_ym2413",!
    }, !
    .probe = snd_ym2413_probe,!
    .remove = snd_ym2413_remove,!
    .id_table = snd_ym2413_idtable,!
    };!
    !
    module_i2c_driver( snd_ym2413_driver );!
    !
    MODULE_AUTHOR("Naomasa Matsubayashi <>");!
    i2cʹ෺ཧతͳσόΠε͕͋ΔͨΊ
    platform busͰ͸ͳ͘i2cʹͿΒԼ͛Δ

    View full-size slide

  81. rdev->client = client;!
    rdev->chset->client = client;!
    rdev->chset->private_data = rdev;!
    printk("debug2 %lx\n", (unsigned long)rdev->bus );!
    rdev->work_queue = create_workqueue("ym2413_queue");!
    INIT_WORK(!
    (struct work_struct *)rdev, snd_ym2413_worker!
    );!
    !
    strcpy(card->driver, "ym2413");!
    strcpy(card->shortname, "ym2413");!
    sprintf(card->longname, "ym2413 Card %i", card->number);!
    err = snd_card_register(card);!
    if (!err) {!
    bus->dev.platform_data = card;!
    return 0;!
    }!
    kfree( rdev );!
    snd_seq_delete_kernel_client( client );!
    snd_card_free(card);
    work queueΛ࡞੒
    Seuqncer ClientͷίʔϧόοΫ͸
    ALSA Sequencerͷεέδϡʔϥ͕࢖͏
    ׂΓࠐΈͷத͔Βݺ͹ΕΔ
    ׂΓࠐΈͷதͰׂΓࠐΈΛ࢖͏I2CΛୟ͘ͱ
    Χʔωϧ͕ࢮΜͰ͠·͏ͷͰ
    work queueʹI2Cͷૢ࡞Λ౤͛Δ

    View full-size slide

  82. static void snd_ym2413_worker( struct work_struct *work ) {!
    unsigned long flags;!
    struct snd_seq_ym2413_dev *rdev;!
    unsigned char buffer[128];!
    unsigned int buffer_size = 0;!
    rdev = (struct snd_seq_ym2413_dev*)work;!
    raw_spin_lock_irqsave(&rdev->bus_lock, flags);!
    buffer_size = rdev->buffer_head - rdev->buffer;!
    memcpy( buffer, rdev->buffer, buffer_size );!
    rdev->buffer_head = rdev->buffer;!
    raw_spin_unlock_irqrestore(&rdev->bus_lock, flags);!
    i2c_master_send( rdev->bus, buffer, buffer_size );!
    }
    work queueͷதͰi2cʹॻ͖ࠐΈ

    View full-size slide

  83. typedef struct snd_seq_event {!
    snd_seq_event_type_t type; !
    unsigned char flags; !
    unsigned char tag; !
    unsigned char queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t source; !
    snd_seq_addr_t dest; !
    union {!
    snd_seq_ev_note_t note; !
    snd_seq_ev_ctrl_t control; !
    snd_seq_ev_raw8_t raw8; !
    snd_seq_ev_raw32_t raw32; !
    snd_seq_ev_ext_t ext; !
    snd_seq_ev_queue_control_t queue; !
    snd_seq_timestamp_t time; !
    snd_seq_addr_t addr; !
    snd_seq_connect_t connect; !
    snd_seq_result_t result; !
    } data; !
    } snd_seq_event_t;
    ͜ͷΠϕϯτ͕
    ૹΒΕͨ࣌ࠁ
    Πϕϯτ͸࣌ࠁΛݟͯॲཧ͠Α͏

    View full-size slide

  84. ·ͱΊ
    ALSA SequencerͰ
    Իָͷԋ૗ΠϕϯτΛ΍ΓͱΓ
    ΠϕϯτΛड͚औΔClient͸
    ϢʔβۭؒͰ΋ΧʔωϧۭؒͰ΋࡞ΕΔ
    ΧʔωϧۭؒClient͕
    ϋʔυ΢ΣΞΛୟ͍ͯԻ৭Λ૗ͰΔ

    View full-size slide