usb声卡驱动(五):声卡驱动的开始

您所在的位置:网站首页 济南废旧轮胎回收公司 usb声卡驱动(五):声卡驱动的开始

usb声卡驱动(五):声卡驱动的开始

2024-05-24 06:15| 来源: 网络整理| 查看: 265

usb声卡驱动(五)

我手上,刚好有高通msm8996的android 8.1源码。那么就直接进入它的kernel分析。

usb声卡驱动的源码位于sound/usb下面。查看对应的Makefile文件。可知相应的源码c文件。总共就那么几个文件,慢慢看来。

现在将部分makefile文件摘录如下:

snd-usb-audio-objs := card.o \ clock.o \ endpoint.o \ format.o \ helper.o \ mixer.o \ mixer_quirks.o \ pcm.o \ proc.o \ quirks.o \ stream.o snd-usbmidi-lib-objs := midi.o # Toplevel Module Dependency # obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o # separate midi and audio obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o obj-$(CONFIG_SND_USB_MIDI) += snd-usbmidi-lib.o

从经验上面来讲,这个声卡驱动一定得先是USB驱动,因此定位USB驱动的地方,全局搜素usb_driver.可以在card.c中找到

如下:

static struct usb_device_id usb_audio_ids [] = { #include "quirks-table.h" { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL }, { } /* Terminating entry */ }; /* * entry point for linux usb interface */ static struct usb_driver usb_audio_driver = { .name = "snd-usb-audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, .suspend = usb_audio_suspend, .resume = usb_audio_resume, .reset_resume = usb_audio_reset_resume, .id_table = usb_audio_ids, .supports_autosuspend = 1, }; module_usb_driver(usb_audio_driver);

因此,当一个USB设备是Audio设备,且该设备含有控制接口(见id_table)时,就会调用probe回调。

可得,usb_audio_probe是整个驱动的起始点。从上一篇笔记中,可知,要创建audio驱动,需要分别创建对应的card对象,和component对象。再再从上上一篇笔记可知,还需要解析对应的usb audio的各种描述符。

整个usb 声卡驱动,使用了struct snd_usb_audio来代替上一篇我们提及的chip对象。如下:

struct snd_usb_audio { int index; struct usb_device *dev; struct snd_card *card; struct usb_interface *pm_intf; u32 usb_id; struct mutex mutex; struct rw_semaphore shutdown_rwsem; unsigned int shutdown:1; unsigned int probing:1; unsigned int in_pm:1; unsigned int autosuspended:1; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ int num_interfaces; int num_suspended_intf; struct list_head pcm_list; /* list of pcm streams */ struct list_head ep_list; /* list of audio-related endpoints */ int pcm_devs; struct list_head midi_list; /* list of midi interfaces */ struct list_head mixer_list; /* list of mixer interfaces */ int setup; /* from the 'device_setup' module param */ bool autoclock; /* from the 'autoclock' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ }; index:驱动内部使用一个数组来保存所有的chip对象,index是这个数组的索引dev:usb_devicecard:这就是真正的card对象pm_intf:当前接口usb_id:usb 设备idmutex,shutdown_rwsem:各种锁shutdown:处于shutdown状态probing:处于probing状态in_pm:从suspend状态到resume状态autosuspended:自动suspend状态txfr_quirk:不懂这个的用法num_interfaces:使用这个驱动的usb接口数,如果为0,则会调用snd_card_free_when_closed释放card对象num_suspended_intf:进入suspend状态的次数pcm_list:pcm这个componet的链表,因为一个声卡,可能有多个pcm对象,将多个pcm对象链接在一起,放入此处ep_list:audio相关的端点的链表,可见后面的端点操作midi_list:midi这个component的链表,同pcm_list类似。mixer_list:mixer链表,同pcm_listsetup:不懂autoclock:不懂ctrl_intf:audio control对应的usb接口 snd_usb_audio对象的创建

snd_usb_audio对象的创建通过

static struct snd_usb_audio * snd_usb_audio_probe(struct usb_device *dev, struct usb_interface *intf, const struct usb_device_id *usb_id);

在前面的笔记中,提到,Audio Control ,Audio Streaming都分别对应USB的Interface而这个驱动的匹配条件为Audio Control.因此,每检查到一个Audio Control这个snd_usb_audio_probe会被调用一次,所以需要注意,不要过多的创建card对象。

static struct snd_usb_audio * snd_usb_audio_probe(struct usb_device *dev, struct usb_interface *intf, const struct usb_device_id *usb_id) { //.... //首先选取可选设置0.注意注意,在某些设备上,可选设置0,表示的是一种0带宽的设置 alts = &intf->altsetting[0]; ifnum = get_iface_desc(alts)->bInterfaceNumber; id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); //接下来就是读取配置,然后创建alsa世界需要的对象 /* check whether it's already registered */ chip = NULL; mutex_lock(®ister_mutex); //防止多个Audio Control情况下重复创建chip对象 for (i = 0; i if (usb_chip[i]->shutdown) { dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); goto __error; } chip = usb_chip[i]; chip->probing = 1; break; } } if (! chip) { /* it's a fresh one. * now look for an empty slot and create a new card instance */ //下面的逻辑,创建一个新的chip对象,创建对象的函数,为snd_usb_audio_create函数 for (i = 0; i goto __error; } chip->pm_intf = intf; break; } if (!chip) { dev_err(&dev->dev, "no available usb audio device\n"); goto __error; } } //... //下面的代码,负责调用snd_usb_create_streams创建audio stream,它包括midi stream //调用snd_usb_create_mixer,创建mixer if (err > 0) { /* create normal USB audio interfaces */ if (snd_usb_create_streams(chip, ifnum) goto __error; } usb_chip[chip->index] = chip; chip->num_interfaces++; chip->probing = 0; intf->needs_remote_wakeup = 1; mutex_unlock(®ister_mutex); return chip; //... }

上面的函数很简单,主要分成了三个步骤:

判断是不是已经创建了chip对象,这是因为某些声卡,具有多个Audio Control接口如果没有chip对象,则调用snd_usb_audio_create,创建然后调用snd_usb_create_streams,snd_usb_create_mixer创建对应的audio stream和audio mixer将创建的card对象,注册到alsa世界中。至此,alsa驱动注册完毕,可以被外部的访问了

接下来看看snd_usb_audio_create函数。

static int snd_usb_audio_create(struct usb_interface *intf, struct usb_device *dev, int idx, const struct snd_usb_audio_quirk *quirk, struct snd_usb_audio **rchip) { struct snd_card *card; struct snd_usb_audio *chip; int err, len; char component[14]; //当释放card对象时,card对象里面的component也会被释放掉,为了能够释放为component分配的资源,所以传递下面的回调操作。 static struct snd_device_ops ops = { .dev_free = snd_usb_audio_dev_free, }; *rchip = NULL; //速度判断 switch (snd_usb_get_speed(dev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: break; default: dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev)); return -ENXIO; } //这里直接调用snd_card_new来创建一个card对象,注意此处的第五个参数0 //表示card对象的私有数据不需要这个函数分配。 //在usb声卡驱动中,为了管理chip对象,使用了《usb声卡驱动(四)》中的"chip对象的管理通常有两种方法"第二种方法 //即创建一个虚拟的component对象。并为其传递snd_device_ops err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, 0, &card); if (err snd_card_free(card); return -ENOMEM; } //初始化chip对象 mutex_init(&chip->mutex); init_rwsem(&chip->shutdown_rwsem); chip->index = idx; chip->dev = dev; chip->card = card; chip->setup = device_setup[idx]; chip->autoclock = autoclock; chip->probing = 1; chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); INIT_LIST_HEAD(&chip->pcm_list); INIT_LIST_HEAD(&chip->ep_list); INIT_LIST_HEAD(&chip->midi_list); INIT_LIST_HEAD(&chip->mixer_list); //将这个chip对象,当做一个虚拟的component对象进行管理 if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) //首先第一步是,确认使用的版本 //参照《usb声卡驱动二》的描述符层级图,可以知道,版本信息保存在header中 //版本1,有bInCollection个audiostream,各个stream保存在baInterfaceNr数组中 //版本2,不清楚,没有翻过相应的规范 control_header = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, NULL, UAC_HEADER); protocol = altsd->bInterfaceProtocol; switch (protocol) { case UAC_VERSION_1: { struct uac1_ac_header_descriptor *h1 = control_header; for (i = 0; i bInCollection; i++) snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); break; } case UAC_VERSION_2: { //母鸡呀··· } } return 0; }

上面的函数,超级简单,提取header描述符,然后判断版本,根据stream个数,调用 snd_usb_create_stream函数,来创建相应的对象。

接下来进入snd_usb_create_stream函数。

//注意第二参数,是控制接口,第三个参数是streaming接口 static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface) { //判断这个是不是midiStreaming 接口,如果是,则调用snd_usbmidi_create创建midi if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { int err = snd_usbmidi_create(chip->card, iface, &chip->midi_list, NULL); if (err dev_dbg(&dev->dev, "%u:%d: skipping non-supported interface %d\n", ctrlif, interface, altsd->bInterfaceClass); /* skip non-supported classes */ return -EINVAL; } //如果是audio streaming ,则调用snd_usb_parse_audio_interface if (! snd_usb_parse_audio_interface(chip, interface)) { usb_set_interface(dev, interface, 0); /* reset the current interface */ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); return -EINVAL; } return 0; }

上面的函数,也很简单,根据描述符里面的信息,判断是不是midi,或者audio。如果是midi则调用snd_usbmidi_create函数。

如果是audio streaming,则将后续逻辑转交给snd_usb_parse_audio_interface函数。

int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { /* parse the interface's altsettings */ iface = usb_ifnum_to_if(dev, iface_no); //获取可选设置数 num = iface->num_altsetting; //遍历所有可选设置 for (i = 0; i case UAC_VERSION_1: { //接下来获取音频相关的AS接口描述符 struct uac1_as_header_descriptor *as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); struct uac_input_terminal_descriptor *iterm; if (as->bLength num_channels = iterm->bNrChannels; chconfig = le16_to_cpu(iterm->wChannelConfig); } break; } case UAC_VERSION_2: { //省略,不懂 } } //FORMAT_TYPE描述符 fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE); fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (! fp) { dev_err(&dev->dev, "cannot malloc\n"); return -ENOMEM; } //初始化fp对象,该对象专门用于保存格式信息。 fp->iface = iface_no; fp->altsetting = altno; fp->altset_idx = i; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->protocol = protocol; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); fp->channels = num_channels; if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; INIT_LIST_HEAD(&fp->list); //调用snd_usb_parse_audio_format进一步解析,它的功效依然是对fp对象的进一步填充,看过《usb声卡驱动(二)》的这个就没有什么可叙述的了 if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) struct snd_usb_stream *as; struct snd_usb_substream *subs; struct snd_pcm *pcm; int err; //遍历chip对象的pcm链表,如果存在一个空的stream,则使用这个stream list_for_each_entry(as, &chip->pcm_list, list) { if (as->fmt_type != fp->fmt_type) continue; subs = &as->substream[stream]; if (subs->ep_num) continue; //创建stream err = snd_pcm_new_stream(as->pcm, stream, 1); if (err pcm, stream, subs); } //遍历之后没有找到,则创建新的,并加入链表中 as = kzalloc(sizeof(*as), GFP_KERNEL); if (!as) return -ENOMEM; as->pcm_index = chip->pcm_devs; as->chip = chip; as->fmt_type = fp->fmt_type; //啦啦啦,终于找到,我们辛辛苦苦期待的创建pcm对象啦 err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, &pcm); if (err struct snd_usb_substream *subs = &as->substream[stream]; INIT_LIST_HEAD(&subs->fmt_list); spin_lock_init(&subs->lock); subs->stream = as; subs->direction = stream; subs->dev = as->chip->dev; subs->txfr_quirk = as->chip->txfr_quirk; subs->speed = snd_usb_get_speed(subs->dev); subs->pkt_offset_adj = 0; snd_usb_set_pcm_ops(as->pcm, stream); list_add_tail(&fp->list, &subs->fmt_list); subs->formats |= fp->formats; subs->num_formats++; subs->fmt_type = fp->fmt_type; subs->ep_num = fp->endpoint; if (fp->channels > subs->channels_max) subs->channels_max = fp->channels; }

上面的代码那么的显眼,snd_usb_set_pcm_ops(as->pcm, stream);不用看也知道,是为stream对象里面的pcm对象,设置相应的回调接口。来看看

void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) { snd_pcm_set_ops(pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); }

果然,使用标准的alsa函数来注册相应的回调接口。

至此,整个alsa驱动都创建完成了。

现在来总结一下整个usb 音频驱动的数据结构:

chip对象为:struct snd_usb_audio. 它持有,snd_card对象,这个对象是alsa世界里面的声卡对象。

chip对象有一个链表pcm_list,它是所有snd_usb_stream对象的一个链表。

而snd_usb_stream对象,持有一个alsa世界的snd_pcm对象,即表示alsa世界里面的pcm

snd_usb_stream对象,还有一个数组substream[2]。它的类型为snd_usb_substream。表示的是打开的子流。在打开的时候,进行赋值,详见后面的pcm的打开和关闭。

在《usb声卡驱动二》中可以知道,一个流对应一个接口(输出一个interface,输入一个interface)。而在snd_usb_add_audio_stream函数中,已经将相同格式的接口统一在了一个snd_usb_stream下面了。

现在已经知道了,整个usb声卡驱动的主要数据结构,以及alsa世界里面的card对象,pcm对象的创建和赋值。接下来就是看它支持的各种功能啦。

下一篇见



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3