Linux音频设备驱动-5

来源:互联网 发布:java读取xls xlsx文件 编辑:程序博客网 时间:2024/04/29 03:00
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()等成员函数实现较为简单,这里不再赘述。
原创粉丝点击