17.6实例2:SA1100+ UDA1341 ALSA驱动
17.6.1 card注册与注销 同样是UDA1341芯片,如果以ALSA体系结构来实现它的驱动,会和OSS大不一样。如17.4.1节所言,在模块初始化和卸载的时候,需要注册和注销card,另外在模块加载的时候,也会注册mixer和pcm组件,如代码清单17.33。 代码清单17.33 UDA1341 ALSA驱动模块初始化与卸载 1 static int __init sa11xx_uda1341_probe(struct platform_device *devptr) 2 { 3 int err; 4 struct snd_card *card; 5 struct sa11xx_uda1341 *chip; 6 7 /* 新建card */ 8 card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct sa11xx_uda1341)); 9 if (card == NULL) 10 return -ENOMEM; 11 12 chip = card->private_data; 13 spin_lock_init(&chip->s[0].dma_lock); 14 spin_lock_init(&chip->s[1].dma_lock); 15 16 card->private_free = snd_sa11xx_uda1341_free;//card私有数据释放 17 chip->card = card; 18 chip->samplerate = AUDIO_RATE_DEFAULT; 19 20 // 注册control(mixer)接口 21 if ((err = snd_chip_uda1341_mixer_new(card, &chip->uda1341))) 22 goto nodev; 23 24 // 注册PCM接口 25 if ((err = snd_card_sa11xx_uda1341_pcm(chip, 0)) < 0) 26 goto nodev; 27 28 strcpy(card->driver, "UDA1341"); 29 strcpy(card->shortname, "H3600 UDA1341TS"); 30 sprintf(card->longname, "Compaq iPAQ H3600 with Philips UDA1341TS"); 31 32 snd_card_set_dev(card, &devptr->dev); 33 //注册card 34 if ((err = snd_card_register(card)) == 0) { 35 printk( KERN_INFO "iPAQ audio support initialized/n" ); 36 platform_set_drvdata(devptr, card); 37 return 0; 38 } 39 40 nodev: 41 snd_card_free(card); 42 return err; 43 } 44 45 static int __devexit sa11xx_uda1341_remove(struct platform_device *devptr) 46 { 47 //释放card 48 snd_card_free(platform_get_drvdata(devptr)); 49 platform_set_drvdata(devptr, NULL); 50 return 0; 51 } 17.6.2 PCM设备的实现 PCM组件直接对应着ALSA驱动的录音和放音,从17.4.2节的描述可知,驱动从需要定义对应相应的snd_pcm_hardware结构体进行PCM设备硬件描述,如代码清单17.34。 代码清单17.34 UDA1341 ALSA驱动PCM接口snd_pcm_hardware结构体 1 static struct snd_pcm_hardware snd_sa11xx_uda1341_capture = 2 { 3 .info = (SNDRV_PCM_INFO_INTERLEAVED | 4 SNDRV_PCM_INFO_BLOCK_TRANSFER | 5 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 6 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 7 .formats = SNDRV_PCM_FMTBIT_S16_LE, 8 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |/ 9 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |/ 10 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |/ 11 SNDRV_PCM_RATE_KNOT), 12 .rate_min = 8000, 13 .rate_max = 48000, 14 .channels_min = 2, 15 .channels_max = 2, 16 .buffer_bytes_max = 64*1024, 17 .period_bytes_min = 64, 18 .period_bytes_max = DMA_BUF_SIZE, 19 .periods_min = 2, 20 .periods_max = 255, 21 .fifo_size = 0, 22 }; 23 24 static struct snd_pcm_hardware snd_sa11xx_uda1341_playback = 25 { 26 .info = (SNDRV_PCM_INFO_INTERLEAVED | 27 SNDRV_PCM_INFO_BLOCK_TRANSFER | 28 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 29 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), 30 .formats = SNDRV_PCM_FMTBIT_S16_LE, 31 .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |/ 32 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |/ 33 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |/ 34 SNDRV_PCM_RATE_KNOT), 35 .rate_min = 8000, 36 .rate_max = 48000, 37 .channels_min = 2, 38 .channels_max = 2, 39 .buffer_bytes_max = 64*1024, 40 .period_bytes_min = 64, 41 .period_bytes_max = DMA_BUF_SIZE, 42 .periods_min = 2, 43 .periods_max = 255, 44 .fifo_size = 0, 45 }; PCM接口的主要函数被封装在snd_pcm_ops结构体内,UDA1341 ALSA驱动对snd_pcm_ops结构体的定义如代码清单17.35。 代码清单17.35 UDA1341 ALSA驱动PCM接口snd_pcm_ops结构体 1 static struct snd_pcm_ops snd_card_sa11xx_uda1341_playback_ops = 2 { 3 .open = snd_card_sa11xx_uda1341_open, 4 .close = snd_card_sa11xx_uda1341_close, 5 .ioctl = snd_pcm_lib_ioctl, 6 .hw_params = snd_sa11xx_uda1341_hw_params, 7 .hw_free = snd_sa11xx_uda1341_hw_free, 8 .prepare = snd_sa11xx_uda1341_prepare, 9 .trigger = snd_sa11xx_uda1341_trigger, 10 .pointer = snd_sa11xx_uda1341_pointer, 11 }; 12 13 static struct snd_pcm_ops snd_card_sa11xx_uda1341_capture_ops = 14 { 15 .open = snd_card_sa11xx_uda1341_open, 16 .close = snd_card_sa11xx_uda1341_close, 17 .ioctl = snd_pcm_lib_ioctl, 18 .hw_params = snd_sa11xx_uda1341_hw_params, 19 .hw_free = snd_sa11xx_uda1341_hw_free, 20 .prepare = snd_sa11xx_uda1341_prepare, 21 .trigger = snd_sa11xx_uda1341_trigger, 22 .pointer = snd_sa11xx_uda1341_pointer, 23 }; 代码清单17.33第25行调用的snd_card_sa11xx_uda1341_pcm()即是PCM组件的“构造函数”,其实现如代码清单17.36。 代码清单17.36 UDA1341 ALSA驱动PCM组件构造函数 1 static int __init snd_card_sa11xx_uda1341_pcm(struct sa11xx_uda1341 *sa11xx_uda1341, int device) 2 { 3 struct snd_pcm *pcm; 4 int err; 5 /* 新建pcm设备,playback和capture都为1个 */ 6 if ((err = snd_pcm_new(sa11xx_uda1341->card, "UDA1341 PCM", device, 1, 1, &pcm)) < 0) 7 return err; 8 9 /* 建立初始缓冲区并设置dma_type为isa */ 10 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 11 snd_dma_isa_data(), 12 64*1024, 64*1024); 13 /* 设置pcm的操作 */ 14 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, snd_card_sa11xx_uda1341_playback_ops); 15 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops); 16 pcm->private_data = sa11xx_uda1341; 17 pcm->info_flags = 0; 18 strcpy(pcm->name, "UDA1341 PCM"); 19 20 sa11xx_uda1341_audio_init(sa11xx_uda1341); 21 22 /* 设置DMA控制器 */ 23 audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_PLAYBACK], audio_dma_callback); 24 audio_dma_request(&sa11xx_uda1341->s[SNDRV_PCM_STREAM_CAPTURE], audio_dma_callback); 25 26 sa11xx_uda1341->pcm = pcm; 27 28 return 0; 29 } 在snd_pcm_ops结构体的打开成员函数中,需要根据具体的子流赋值snd_pcm_runtime的hw,如代码清单17.37。 代码清单17.37 UDA1341 ALSA驱动snd_pcm_ops结构体open/close成员函数 1 static int snd_card_sa11xx_uda1341_open(struct snd_pcm_substream *substream) 2 { 3 struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream); 4 struct snd_pcm_runtime *runtime = substream->runtime; 5 int stream_id = substream->pstr->stream; 6 int err; 7 8 chip->s[stream_id].stream = substream; 9 10 if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) //播放子流 11 runtime->hw = snd_sa11xx_uda1341_playback; 12 else //录音子流 13 runtime->hw = snd_sa11xx_uda1341_capture; 14 if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 15 return err; 16 if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,&hw_constraints_rates)) < 0) 17 return err; 18 19 return 0; 20 } 21 22 static int snd_card_sa11xx_uda1341_close(struct snd_pcm_substream *substream) 23 { 24 struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream); 25 26 chip->s[substream->pstr->stream].stream = NULL; 27 return 0; 28 } 在snd_pcm_ops结构体的trigger()成员函数中,控制播放和录音的启/停、挂起/恢复,如代码清单17.38。 代码清单17.38 UDA1341 ALSA驱动snd_pcm_ops结构体trigger成员函数 1 static int snd_sa11xx_uda1341_trigger(struct snd_pcm_substream *substream, int 2 cmd) 3 { 4 struct sa11xx_uda1341 *chip = snd_pcm_substream_chip(substream); 5 int stream_id = substream->pstr->stream; 6 struct audio_stream *s = &chip->s[stream_id]; 7 struct audio_stream *s1 = &chip->s[stream_id ^ 1]; 8 int err = 0; 9 10 /* 注意本地中断已经被中间层代码禁止 */ 11 spin_lock(&s->dma_lock); 12 switch (cmd) 13 { 14 case SNDRV_PCM_TRIGGER_START://开启PCM 15 if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) //开启录音,不在播放 16 { 17 s1->tx_spin = 1; 18 audio_process_dma(s1); 19 } 20 else 21 { 22 s->tx_spin = 0; 23 } 24 25 /* 被请求的流启动 */ 26 s->active = 1; 27 audio_process_dma(s); 28 break; 29 case SNDRV_PCM_TRIGGER_STOP: 30 /* 被请求的流关闭 */ 31 audio_stop_dma(s); 32 if (stream_id == SNDRV_PCM_STREAM_PLAYBACK && s1->active) //在录音时开启播放 33 { 34 s->tx_spin = 1; 35 audio_process_dma(s); //启动DMA 36 } 37 else 38 { 39 if (s1->tx_spin) 40 { 41 s1->tx_spin = 0; 42 audio_stop_dma(s1); //停止DMA 43 } 44 } 45 46 break; 47 case SNDRV_PCM_TRIGGER_SUSPEND: //挂起 48 s->active = 0; 49 #ifdef HH_VERSION 50 sa1100_dma_stop(s->dmach); //停止DMA 51 #endif 52 s->old_offset = audio_get_dma_pos(s) + 1; 53 #ifdef HH_VERSION 54 sa1100_dma_flush_all(s->dmach); 55 #endif 56 s->periods = 0; 57 break; 58 case SNDRV_PCM_TRIGGER_RESUME: //恢复 59 s->active = 1; 60 s->tx_spin = 0; 61 audio_process_dma(s); //开启DMA 62 if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) 63 { 64 s1->tx_spin = 1; 65 audio_process_dma(s1); 66 } 67 break; 68 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: //暂停 69 #ifdef HH_VERSION 70 sa1100_dma_stop(s->dmach); //停止DMA 71 #endif 72 s->active = 0; 73 if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) 74 { 75 if (s1->active) 76 { 77 s->tx_spin = 1; 78 s->old_offset = audio_get_dma_pos(s) + 1; 79 #ifdef HH_VERSION 80 sa1100_dma_flush_all(s->dmach); 81 #endif 82 audio_process_dma(s); //开启DMA 83 } 84 } 85 else 86 { 87 if (s1->tx_spin) 88 { 89 s1->tx_spin = 0; 90 #ifdef HH_VERSION 91 sa1100_dma_flush_all(s1->dmach); 92 #endif 93 } 94 } 95 break; 96 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: //暂停释放 97 s->active = 1; 98 if (s->old_offset) 99 { 100 s->tx_spin = 0; 101 audio_process_dma(s); 102 break; 103 } 104 if (stream_id == SNDRV_PCM_STREAM_CAPTURE && !s1->active) 105 { 106 s1->tx_spin = 1; 107 audio_process_dma(s1); 108 } 109 #ifdef HH_VERSION 110 sa1100_dma_resume(s->dmach); 111 #endif 112 break; 113 default: 114 err = - EINVAL; 115 break; 116 } 117 spin_unlock(&s->dma_lock); 118 return err; 119 } snd_pcm_ops结构体中其它的hw_params()、prepare()、pointer()等成员函数实现较为简单,这里不再赘述。 |