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. #include <cstdint>! #include <cmath>! #include <array>! #include <alsa/asoundlib.h>! 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
  2. #include <cstdint>! #include <cmath>! #include <array>! #include <alsa/asoundlib.h>! 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
  3. #include <cstdint>! #include <cmath>! #include <array>! #include <alsa/asoundlib.h>! 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
  4. 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 Χʔωϧ Ϣʔβۭؒ
  5. 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 Χʔωϧ Ϣʔβۭؒ Իྔ΍࿥Իιʔεͷબ୒౳ ϋʔυ΢ΣΞશମͷઃఆΛߦ͏
  6. 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σʔλΛૹͬͯԻΛग़͢
  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 Χʔωϧ Ϣʔβۭؒ γϯηαΠβʔʹίϚϯυΛૹͬͯ MIDI౳Λԋ૗͢Δ
  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 Χʔωϧ Ϣʔβۭؒ Ϣʔβۭ͔ؒΒLinuxΧʔωϧʹ ԋ૗ΠϕϯτΛ఻͑Δํ๏͸2ͭ͋Δ
  9. 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 ΄΅.*%*ͱಉ͡৘ใΛදݱ ݻఆ௕ λΠϜελϯϓͱ ૹ৴ݩɺૹ৴ઌͷ৘ใ͕ ௥Ճ͞Ε͍ͯΔ
  10. 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 ԡ͞Εͨ伴൫͸ͲΕ  ָثͷछྨ͸ͲΕ
  11. Χʔωϧ Ϣʔβۭؒ Sequencer API Sequencer Core Client A Client B

    ♪ ♪ ⽃ snd_seq_eventΛ౤͛ͨΓड͚औͬͨΓ͢Δ ΞϓϦέʔγϣϯ͸clientͱݺ͹ΕΔ ♪ ♪ ⽃ client͔ΒૹΒΕͨsnd_seq_event͸ ΧʔωϧͷSequencer Core͕ εέδϡʔϦϯά͠ɺద੾ͳclientʹ഑ૹ͢Δ
  12. $ 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ͷ઀ଓͷ ֬ೝͱมߋΛߦ͏ίϚϯυ
  13. $ 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ʹ޲͔ͬͯΠϕϯτΛ౤͛Δ
  14. $ 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͕઀ଓ͞Ε͍ͯΔ
  15. 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
  16. 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
  17. 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ʹϙʔτΛ࡞੒͢Δ
  18. $ aconnect -l! …! ΫϥΠΞϯτ 128: 'hoge' [λΠϓ=Ϣʔβ]! 0 'hoge

    port 0 '! 1 'hoge port 1 ‘! …! $ aconnect 128:1 14:0 1ͭͷSequencer Clientʹ͸ ෳ਺ͷϙʔτΛઃ͚Δࣄ͕ग़དྷΔ Clientಉ࢜ͷ઀ଓ͸ϙʔτ୯ҐͰߦ͏ ΫϥΠΞϯτͷϙʔτΛ ΫϥΠΞϯτͷϙʔτʹ઀ଓ
  19. 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; ͜Ε
  20. 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ͭͷϙʔτΛ઀ଓ
  21. /* 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
  22. 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͕ฦΔΑ͏ʹͳΔ
  23. $ 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 ϢʔβΫϥΠΞϯτͱΧʔωϧΫϥΠΞϯτ
  24. Χʔωϧ Ϣʔβۭؒ Sequencer Core Client A Client B Client C

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

    σόΠευϥΠό γϯηαΠβʔ σόΠεͷυϥΠό ͱ͔͕࢖͏
  26. 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! $
  27. }! 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
  28. 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
  29. ! ! ! ! ! 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
  30. 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 ) {!
  31. 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ͷα΢ϯυΧʔυͷ৔߹ͱҰॹ
  32. }! 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͕ग़དྷͨΒϙʔτΛ࡞੒͢Δ
  33. 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;!
  34. 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;! ͜ͷϙʔτ͕ͲΜͳػೳΛ͍࣋ͬͯΔ͔ͱ
  35. 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;! ϙʔτʹଞͷϙʔτ͕઀ଓ͖ͯͨ͠Γ Πϕϯτ͕ඈΜͰ͖ͨ࣌ʹ ݺͼग़͞ΕΔؔ਺Λઃఆ
  36. 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 Λࢦఆͯ͠ ϙʔτΛ࡞੒
  37. 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;!
  38. 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εςʔτΛอ࣋͢Δߏ଄ମΛ࡞੒
  39. 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;! };
  40. 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;! }; Իྔ΍ύϯϙοτ ൃԻதͷԻ֊ ϐονϕϯυͷ෯
  41. 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ͷνϟωϧ਺
  42. }! 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;! α΢ϯυΧʔυΛొ࿥
  43. ! 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; }
  44. ! 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ʹ౉͢
  45. 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Λड͚औͬͨ ʜ࣌ʹݺͼग़͞ΕΔؔ਺
  46. 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,
  47. 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νϟωϧͷঢ়ଶ
  48. ) { 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Ͱग़ྗ
  49. # 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
  50. # 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! $ ΧʔωϧϩάʹΠϕϯτ͕ྲྀΕͯ͘Δ
  51. ALSA Sequencer͕αϙʔτ͍ͯ͠Δ ϋʔυ΢ΣΞ YAMAHA OPL3 YAMAHA OPL4 E-MU 8000 E-MU

    10k1 1992೥ࠒ 1995೥ࠒ 1994೥ࠒ 1998೥ࠒ α΢ϯυΧʔυʹγϯηαΠβʔ͕උΘ͍ͬͯΔͷ͸ ਵ෼ੲͷ࿩ʹͳͬͯ͠·ͬͨ
  52. A0 CS WE D0..D7 10nsҎ্ 25nsҎ্ nsҎ্ nsҎ্ nsҎ্ nsҎ্

    nsҎ্ ஗͘ͱ΋1MHz͘Β͍ͰGPIOΛૢ࡞͍ͨ͠ όΠτʹ͖ͭ0.2µs͘Β͍ͰૹΕΔ
  53. LinuxΧʔωϧͰ1MHzͰGPIOΛ͍͡Δʹ͸ ࡞ઓ1 udelay( 1 ); ࡞ઓ2 usleep_range( 1 ); ࡞ઓ3

    1µsຖʹGPIOͷ஋Λॻ͖׵͑ΔϚΠίϯͱ I2CͰ઀ଓ ஗͘ͱ΋1MHz͘Β͍ͰGPIOΛૢ࡞͍ͨ͠
  54. 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ͰԻ͕ग़Ε͹੒ޭ
  55. ! ! ! ! ! ! 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ͱಉ͡
  56. ! ! ! ! ! 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ʹͿΒԼ͛Δ
  57. 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ͷૢ࡞Λ౤͛Δ
  58. 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ʹॻ͖ࠐΈ
  59. 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; ͜ͷΠϕϯτ͕ ૹΒΕͨ࣌ࠁ Πϕϯτ͸࣌ࠁΛݟͯॲཧ͠Α͏