Linux音频设备驱动-5

4/27/2010 11:45:36 PM
Linux音频设备驱动-5
2008-10-03 19:07
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()等成员函数实现较为简单,这里不再赘述。

你可能感兴趣的:(linux)