Statement: the content of this blog is composedHttp://blog.csdn.net/droidphoneOriginal, reprinted please indicate the source, thank you!
1. struct snd_card
1.1. What is snd_card?
Snd_card is a top-level structure of the entire ALSA audio driver. The software logical structure of the Sound Card begins with this structure. Almost all audio-related logical devices are under the management of snd_card, the first action of the sound card driver is to create an snd_card structure. Because of this, we also start with struct cnd_card in this section.
1.2. snd_card Definition
Snd_card is defined in the header file: Include/sound/CORE. h
/* Main Structure for Soundcard */</P> <p> struct snd_card {<br/> int number;/* Number of Soundcard (index to <br/> snd_cards) */</P> <p> char ID [16];/* ID string of this card */<br/> char driver [16]; /* driver name */<br/> char shortname [32];/* short name of this Soundcard */<br/> char longname [80]; /* Name of this Soundcard */<br/> char mixername [80];/* mixer name */<br/> char components [128]; /* card components delimited with <br/> space */<br/> struct module * module; /* top-level module */</P> <p> void * private_data;/* private data for Soundcard */<br/> void (* private_free) (struct snd_card * card);/* callback for freeing of <br/> private data */<br/> struct list_head devices; /* devices */</P> <p> unsigned int last_numid;/* last used numeric ID */<br/> struct rw_semaphore controls_rwsem; /* Controls List lock */<br/> rwlock_t ctl_files_rwlock;/* ctl_files list lock */<br/> int controls_count; /* count of all controls */<br/> int user_ctl_count;/* count of all user controls */<br/> struct list_head controls; /* all controls for this card */<br/> struct list_head ctl_files;/* Active Control Files */</P> <p> struct snd_info_entry * proc_root; /* root for Soundcard specific files */<br/> struct snd_info_entry * proc_id;/* The card ID */<br/> struct proc_dir_entry * proc_root_link; /* Number link to real ID */</P> <p> struct list_head files_list;/* all files associated to this card */<br/> struct snd_shutdown_f_ops * s_f_ops; /* file operations in the shutdown <br/> state */<br/> spinlock_t files_lock;/* lock the files for this card */<br/> int shutdown; /* this card is going down */<br/> int free_on_last_close;/* free in context of file_release */<br/> wait_queue_head_t shutdown_sleep; <br/> struct device * dev;/* Device assigned to this card */<br/> # ifndef config_sysfs_deprecated <br/> struct device * card_dev; /* cardx object for sysfs */<br/> # endif </P> <p> # ifdef config_pm <br/> unsigned int power_state; /* power state */<br/> struct mutex power_lock;/* power lock */<br/> wait_queue_head_t power_sleep; <br/> # endif </P> <p> # If defined (config_snd_mixer_oss) | defined (config_snd_mixer_oss_module) <br/> struct snd_mixer_oss * mixer_oss; <br/> int mixer_oss_change_count; <br/> # endif <br/> };
- Struct list_head devices records the linked list of all logical devices under the sound card
- Struct list_head controls records the linked list of all control units under the sound card
- Void * private_data private data of the sound card. You can specify the data size through parameters when creating the sound card.
2. Sound Card creation process
2.1.1. Step 1: Create an instance of snd_card
Struct snd_card * card; <br/> int err; <br/>... <br/> err = snd_card_create (index, ID, this_module, 0, & card );
- Index is an integer. the ID of the sound card.
- Id string, the ID of the Sound Card
- The fourth parameter determines the size of the private data that needs to be allocated at the same time when the snd_card instance is created. The data pointer will be assigned to the private_data member of snd_card.
- Card returns the pointer to the created snd_card instance.
2.1.2. Step 2: Create dedicated chip data for the Sound Card
The dedicated data of the sound card is mainly used to store some resource information of the sound card, such as interrupted resources, Io resources, and DMA resources. There can be two ways to create:
- Use the fourth parameter in snd_card_create () in the previous step to create snd_card_create
// Struct mychip is used to save dedicated data <br/> err = snd_card_create (index, ID, this_module, <br/> sizeof (struct mychip), & card ); <br/> // retrieve from private_data <br/> struct mychip * chip = card-> private_data;
Struct mychip {<br/> struct snd_card * card; <br/> .... <br/>}; <br/> struct snd_card * card; <br/> struct mychip * chip; </P> <p> chip = kzarloc (sizeof (* chip), gfp_kernel); <br/> ...... <br/> err = snd_card_create (index [Dev], Id [Dev], this_module, 0, & card ); <br/> // Private Data Record snd_card instance <br/> chip-> card = card; <br/> ..... <br/>
Then, register the proprietary data of the chip as a low-level device of the Sound Card:
Static int snd_mychip_dev_free (struct snd_device * Device) <br/>{< br/> return snd_mychip_free (device-> device_data ); <br/>}</P> <p> static struct snd_device_ops ops ={< br/>. dev_free = snd_mychip_dev_free, <br/>}; <br/> .... <br/> snd_device_new (card, sndrv_dev_lowlevel, Chip, & OPS );
A low-level device is registered to automatically release memory occupied by dedicated chip data when the sound card is canceled.
2.1.3. Step 3: Set the driver ID and name
Strcpy (card-> driver, "my chip"); <br/> strcpy (card-> shortname, "my own chip 123 "); <br/> sprintf (card-> longname, "% s at 0x % lx IRQ % I", <br/> card-> shortname, chip-> ioport, chip-> IRQ );
The driver field of snd_card stores the chip ID string, which is used by ALSA-Lib in the user space. Therefore, the uniqueness of the ID must be ensured. Shortname fields are used to print more information. longname fields appear in/proc/asound/cards.
2.1.4. Step 4: create functional components (logical devices) of the Sound Card, such as PCM, mixer, and Midi.
At this time, you can create various functional components of the sound card. Do you still remember the devices field of the snd_card struct at the beginning? The creation of each component will eventually call snd_device_new () to generate an snd_device instance and link the instance to the snd_card devices linked list.
Generally, the Alsa-driver has provided some common component creation functions, instead of directly calling snd_device_new (), such:
PCM ---- snd_pcm_new ()
Rawmidi -- snd_rawmidi_new ()
Control -- snd_ctl_create ()
Timer -- snd_timer_new ()
Info -- snd_card_proc_new ()
Jack -- snd_jack_new ()
2.1.5. Step 5: register the sound card
Err = snd_card_register (card); <br/> If (ERR <0) {<br/> snd_card_free (card); <br/> return err; <br/>}
2.2. An actual example
I put the/sound/ARM/pxa2xx-ac97.c partCodeAttach:
Static int _ devinit pxa2xx_ac97_probe (struct platform_device * Dev) <br/>{< br/> struct snd_card * card; <br/> struct snd_ac97_bus * ac97_bus; <br/> struct snd_ac97_template ac97_template; <br/> int ret; <br/> pxa2xx_audio_ops_t * pdata = Dev-> Dev. platform_data; </P> <p> If (Dev-> ID> = 0) {<br/> dev_err (& Dev-> Dev, "pxa2xx has only one ac97 port. /n "); <br/> ret =-enxio; <br/> goto err_dev; <br/>}< br/> // (1) /// <br/> ret = snd_card_create (sndrv_default_idx1, sndrv_default_str1, <br/> this_module, 0, & card); <br/> If (Ret <0) <br/> goto err; </P> <p> card-> Dev = & Dev-> dev; <br/> /// (3) //// <br/> strncpy (card-> driver, Dev-> Dev. driver-> name, sizeof (card-> driver); </P> <p> // (4) /// <br/> ret = pxa2xx_pcm_new (card, & pxa2xx_ac97_pcm_client, & pxa2xx_ac97_pcm); <br/> If (RET) <br/> goto err; <br/> /// (2) /// <br/> ret = pxa2xx_ac97_hw_probe (Dev); <br/> If (RET) <br/> goto err; </P> <p> /// (4) /// <br/> ret = snd_ac97_bus (card, 0, & pxa2xx_ac97_ops, null, & ac97_bus); <br/> If (RET) <br/> goto err_remove; <br/> memset (& ac97_template, 0, sizeof (ac97_template )); <br/> ret = snd_ac97_mixer (ac97_bus, & ac97_template, & pxa2xx_ac97_ac97); <br/> If (RET) <br/> goto err_remove; <br/> /// (3) /// <br/> snprintf (card-> shortname, sizeof (card-> shortname ), <br/> "% s", snd_ac97_get_short_name (pxa2xx_ac97_ac97); <br/> snprintf (card-> longname, sizeof (card-> longname ), <br/> "% s (% s)", Dev-> Dev. driver-> name, card-> mixername); </P> <p> If (pdata & pdata-> codec_pdata [0]) <br/> snd_ac97_dev_add_pdata (ac97_bus-> codec [0], pdata-> codec_pdata [0]); <br/> snd_card_set_dev (card, & Dev-> Dev ); <br/> /// (5) /// <br/> ret = snd_card_register (card); <br/> If (ret = 0) {<br/> platform_set_drvdata (Dev, card); <br/> return 0; <br/>}</P> <p> err_remove: <br/> pxa2xx_ac97_hw_remove (Dev); <br/> err: <br/> If (card) <br/> snd_card_free (card); <br/> err_dev: <br/> return ret; <br/>}</P> <p> static int _ devexit pxa2xx_ac97_remove (struct platform_device * Dev) <br/>{< br/> struct snd_card * card = platform_get_drvdata (Dev); </P> <p> If (card) {<br/> snd_card_free (card ); <br/> platform_set_drvdata (Dev, null); <br/> pxa2xx_ac97_hw_remove (Dev); <br/>}</P> <p> return 0; <br/>}</P> <p> static struct platform_driver pxa2xx_ac97_driver ={< br/>. probe = pxa2xx_ac97_probe, <br/>. remove = _ devexit_p (pxa2xx_ac97_remove), <br/>. driver ={< br/>. name = "pxa2xx-ac97", <br/>. owner = this_module, <br/> # ifdef config_pm <br/>. PM = & pxa2xx_ac97_pm_ops, <br/> # endif <br/>}, <br/>}; </P> <p> static int _ init pxa2xx_ac97_init (void) <br/>{< br/> return platform_driver_register (& pxa2xx_ac97_driver); <br/>}</P> <p> static void _ exit pxa2xx_ac97_exit (void) <br/>{< br/> platform_driver_unregister (& pxa2xx_ac97_driver); <br/>}</P> <p> module_init (pxa2xx_ac97_init ); <br/> module_exit (pxa2xx_ac97_exit); </P> <p> module_author ("Nicolas Pitre "); <br/> module_description ("ac97 driver for the Intel pxa2xx chip ");
DriverProgramGenerally, it starts with the probe callback function. Are there any similarities between the steps in Step 2.1?
After the above creation steps, the logical structure of the sound card is shown in:
Figure 2.2.1 logical structure of sound card software
In the following sections, we will discuss the functions snd_card_create () and snd_card_register () respectively.
3. snd_card_create ()
Snd_card_create () is defined in/sound/CORE/init. C.
/** <Br/> * snd_card_create-create and initialize a Soundcard structure <br/> * @ idx: card index (Address) [0... (SNDRV_CARDS-1)] <br/> * @ Xid: Card identification (ASCII string) <br/> * @ module: Top Level module for locking <br/> * @ extra_size: allocate this extra size after the main Soundcard structure <br/> * @ card_ret: the pointer to store the created card instance <br/> * creates and initializes a Soundcard structure. <br/> * The function allocates snd_card instance via kzarloc with the given <br/> * space for the driver to use freely. the allocated struct is stored <br/> * in the given card_ret pointer. <br/> * returns zero if successful or a negative error code. <br/> */<br/> int snd_card_create (INT idx, const char * Xid, <br/> struct module * module, int extra_size, <br/> struct snd_card ** card_ret)
First, allocate the memory according to the size of the extra_size parameter. The memory zone can be used as the proprietary data of the chip (see the previous introduction ):
Card = kzarloc (sizeof (* card) + extra_size, gfp_kernel); <br/> If (! Card) <br/> return-enomem;
Copy the ID string of the Sound Card:
If (Xid) <br/> strlcpy (card-> ID, Xid, sizeof (card-> ID); <br/>
If the input sound card number is-1, an index number is automatically assigned:
If (idx <0) {<br/> for (idx2 = 0; idx2 <sndrv_cards; idx2 ++) <br/>/* idx =-1 = 0 xFFFF means: Take any free slot */<br/> If (~ Snd_cards_lock & idx & 1 <idx2) {<br/> If (module_slot_match (module, idx2) {<br/> idx = idx2; <br/> break; <br/>}< br/> If (idx <0) {<br/> for (idx2 = 0; idx2 <sndrv_cards; idx2 ++) <br/>/* idx =-1 = 0 xFFFF means: Take any free slot */<br/> If (~ Snd_cards_lock & idx & 1 <idx2) {<br/> If (! Slots [idx2] |! * Slots [idx2]) {<br/> idx = idx2; <br/> break; <br/>}< br/>
Required Fields in snd_card structure initialization:
Card-> Number = idx; <br/> card-> module = module; <br/> init_list_head (& Card-> devices ); <br/> init_rwsem (& Card-> controls_rwsem); <br/> rwlock_init (& Card-> ctl_files_rwlock); <br/> init_list_head (& Card-> controls ); <br/> init_list_head (& Card-> ctl_files); <br/> spin_lock_init (& Card-> files_lock); <br/> init_list_head (& Card-> files_list ); <br/> init_waitqueue_head (& Card-> shutdown_sleep); <br/> # ifdef config_pm <br/> mutex_init (& Card-> power_lock ); <br/> init_waitqueue_head (& Card-> power_sleep); <br/> # endif <br/>
Create a logical device: Control
/* The control interface cannot be accessed from the user space until */<br/>/* snd_cards_bitmask and snd_cards are set with snd_card_register */<br/> err = snd_ctl_create (card); <br/>
Create the info node in the proc file: Usually/proc/asound/card0
Err = snd_info_card_create (card); <br/>
Put the memory pointer allocated in step 1 into the private_data field:
If (extra_size> 0) <br/> card-> private_data = (char *) card + sizeof (struct snd_card); <br/>
4. snd_card_register ()
Snd_card_create () is defined in/sound/CORE/init. C.
/** <Br/> * snd_card_register-register the Soundcard <br/> * @ card: soundcard structure <br/> * This function registers all the devices assigned to the Soundcard. <br/> * Until calling this, the Alsa control interface is blocked from the <br/> * external accesses. thus, you shocould call this function at the end <br/> * of the initialization of the card. <br/> * returns zero otherwise a negative error code if the registrain failed. <br/> */<br/> int snd_card_register (struct snd_card * card) <br/>
First, create a device under sysfs:
If (! Card-> card_dev) {<br/> card-> card_dev = device_create (sound_class, card-> Dev, <br/> mkdev (0, 0), card, <br/> "card % I", card-> Number); <br/> If (is_err (card-> card_dev )) <br/> card-> card_dev = NULL; <br/>}< br/>
Sound_class is created in/sound/sound_core.c:
Static char * sound_devnode (struct device * Dev, mode_t * mode) <br/>{< br/> If (Major (Dev-> devt) = sound_major) <br/> return NULL; <br/> return kasprintf (gfp_kernel, "snd/% s", dev_name (Dev )); <br/>}< br/> static int _ init init_soundcore (void) <br/>{< br/> int RC; </P> <p> rc = init_oss_soundcore (); <br/> If (RC) <br/> return RC; </P> <p> sound_class = class_create (this_module, "sound"); <br/> If (is_err (sound_class) {<br/> cleanup_oss_soundcore (); <br/> return ptr_err (sound_class); <br/>}</P> <p> sound_class-> devnode = sound_devnode; </P> <p> return 0; <br/>}< br/>
It can be seen that the sound card class will appear under/sys/class/sound/of the file system, and sound_devnode () it also determines that the corresponding device node will also appear under/dev/snd.
In the next step, register all the logical devices mounted to the sound card through snd_device_register_all (). snd_device_register_all () actually traverses all the snd_devices through the snd_card devices linked list, and call OPS-> dev_register () of snd_device to register the devices.
If (ERR = snd_device_register_all (card) <0) <br/> return err; <br/>
Finally, you can create some corresponding files or attribute nodes under proc and sysfs, and the code will not be pasted.
So far, the sound card has been established.