Reference learning address: https://www.cnblogs.com/jason-lu/archive/2013/06/07/3123750.html
1, Introduction to pcm
PCM is the abbreviation of pulse code modulation in English and pulse code modulation in Chinese We know that in real life, the sound heard by human ears is an analog signal. PCM is a technology to convert sound from analog to digital signal. Its principle is simply to sample the analog signal with a fixed frequency. The sampled signal looks like a series of continuous pulses with different amplitude in waveform, The amplitudes of these pulses are quantized with a certain accuracy, and these quantized values are continuously output, transmitted, processed or recorded into the storage medium. All these constitute the generation process of digital audio
The two important indexes of PCM signal are sampling frequency and quantization accuracy. At present, the sampling frequency of CD audio is usually 44100Hz and the quantization accuracy is 16bit
The higher your sampling frequency, the higher the degree of sound restoration you play through the ad module.
The process is probably
capture - > ad module - > playback
1. How does playback convert PCM data sent by user space applications into analog audio that can be recognized by human ears
2. capture converts the analog signal picked up by mic into PCM signal after sampling and quantization, and sends it back to the application program in user space
2, Detailed explanation of pcm structure
2.1 pcm block diagram
To access the middle layer code of PCM, you must first include the header file < sound / PCM h> In addition, if you need to access some information related to hw_param related functions may also include < sound / PCM_ params. h>.
Each sound card can contain up to 4 instances of pcm, and each instance of pcm corresponds to a pcm device file This limitation on the number of pcm instances stems from the bit size occupied by the linux device number. If we use the 64 bit device number in the future, we can create more pcm instances However, in most cases, in embedded devices, a pcm instance is enough.
A pcm instance consists of a playback stream and a capture stream, which are respectively composed of one or more substreams (compression layers).
2.2 pcm code framework
In kernel / include / sound / PCM H code has the corresponding source code. A block diagram is pasted here to help you understand.
Let's start in the middle
1,snd_pcm_substream is the core of PCM middle layer. Most tasks are processed in substream, especially its OPS (snd_pcm_ops) field. Many user space applications' requests for drivers through alsa lib are processed by the functions in this structure. Its runtime field points to snd_pcm_runtime structure, snd_pcm_runtime records some important software and hardware operating environments and parameters of this substream. (analysis will follow)
2,snd_pcm is attached to SND_ A snd under the card_ device
3, snd_pcm_runtime structure, snd_pcm_runtime records some important software and hardware operating environment and parameters of this substream
4,snd_pcm_substream field in str, pointing to snd_pcm_substream structure.
5,snd_pcm_substream is the core of PCM middle layer. Most tasks are processed in substream, especially its ops(snd_pcm_ops) field. Many user space applications' requests for drivers through alsa lib are processed by the functions in this structure Its runtime field points to snd_pcm_runtime structure, snd_pcm_runtime records some important software and hardware operating environments and parameters of this substream.
The following is the corresponding code of pcm of rk3288. In fact, this structure contains many interfaces.
struct snd_pcm_substream { struct snd_pcm *pcm; struct snd_pcm_str *pstr; void *private_data; /* copied from pcm->private_data */ int number; char name[32]; /* substream name */ int stream; /* stream (direction) */ struct pm_qos_request latency_pm_qos_req; /* pm_qos request */ size_t buffer_bytes_max; /* limit ring buffer size */ struct snd_dma_buffer dma_buffer; size_t dma_max; /* -- hardware operations -- */ const struct snd_pcm_ops *ops; /* -- runtime information -- */ struct snd_pcm_runtime *runtime; /* -- timer section -- */ struct snd_timer *timer; /* timer */ unsigned timer_running: 1; /* time is running */ long wait_time; /* time in ms for R/W to wait for avail */ /* -- next substream -- */ struct snd_pcm_substream *next; /* -- linked substreams -- */ struct list_head link_list; /* linked list member */ struct snd_pcm_group self_group; /* fake group for non linked substream (with substream lock inside) */ struct snd_pcm_group *group; /* pointer to current group */ /* -- assigned files -- */ void *file; int ref_count; atomic_t mmap_count; unsigned int f_flags; void (*pcm_release)(struct snd_pcm_substream *); struct pid *pid; #if IS_ENABLED(CONFIG_SND_PCM_OSS) /* -- OSS things -- */ struct snd_pcm_oss_substream oss; #endif #ifdef CONFIG_SND_VERBOSE_PROCFS struct snd_info_entry *proc_root; struct snd_info_entry *proc_info_entry; struct snd_info_entry *proc_hw_params_entry; struct snd_info_entry *proc_sw_params_entry; struct snd_info_entry *proc_status_entry; struct snd_info_entry *proc_prealloc_entry; struct snd_info_entry *proc_prealloc_max_entry; #ifdef CONFIG_SND_PCM_XRUN_DEBUG struct snd_info_entry *proc_xrun_injection_entry; #endif #endif /* CONFIG_SND_VERBOSE_PROCFS */ /* misc flags */ unsigned int hw_opened: 1; };
Look at the arrow pointing down in the block diagram. The most important snd_pcm_ops, there are many operation functions, open, close and hw_free,hw_params and trigger.
struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); int (*ioctl)(struct snd_pcm_substream * substream, unsigned int cmd, void *arg); int (*hw_params)(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); int (*hw_free)(struct snd_pcm_substream *substream); int (*prepare)(struct snd_pcm_substream *substream); int (*trigger)(struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); int (*get_time_info)(struct snd_pcm_substream *substream, struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report); int (*fill_silence)(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long bytes); int (*copy_user)(struct snd_pcm_substream *substream, int channel, unsigned long pos, void __user *buf, unsigned long bytes); int (*copy_kernel)(struct snd_pcm_substream *substream, int channel, unsigned long pos, void *buf, unsigned long bytes); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); int (*ack)(struct snd_pcm_substream *substream); };
- The open function sets the supported transmission mode, data format, number of channels, period and other parameters for the PCM module, and allocates corresponding DMA channels for the playback/capture stream.
- hw_params function sets the source (destination) address of DMA and the size of DMA buffer for substream (ALSA core generates a corresponding substream every time a playback or capture is opened).
- This function is called when PCM is "ready". Here, according to channels and buffer_bytes to set DMA transmission parameters, which is related to the specific hardware platform. Note: snd is called every time_ pcm_ The prepare function will be called when preparing ().
- trigger function will be called when pcm starts, stops and pauses.
- snd_pcm_runtime Function Description: we will notice that each member function of ops needs to obtain an SND_ pcm_ The pointer to the runtime structure, which can be obtained through substream - > runtime. snd_pcm_runtime is the information of PCM runtime. When a PCM subflow is opened, the PCM runtime instance is assigned to the subflow. It has a lot of information: hw_params and sw_params configuration copy, buffer pointer, mmap record, spin lock, etc. snd_ pcm_ The runtime is read-only for driver operation set functions, and only the PCM middle layer can change or update this information
hw_ Code corresponding to params
static int s3c_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; int err = 0; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = params_buffer_bytes(params); return err; }
trigger corresponding code
static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct runtime_data *prtd = substream->runtime->private_data; int ret = 0; spin_lock(&prtd->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: prtd->state |= ST_RUNNING; dma_ctrl(prtd->params->channel, DMAOP_START); //DMA on break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: prtd->state &= ~ST_RUNNING; dma_ctrl(prtd->params->channel, DMAOP_STOP); //DMA stop break; default: ret = -EINVAL; break; } spin_unlock(&prtd->lock); return ret; }
DMA in code_ Buffer is a DMA buffer, which is defined by four fields: dma_area,dma_addr,dma_bytes and dma_private. Including dma_area is the logical address of the buffer, dma_addr is the physical address of the buffer, dma_bytes is the size of the buffer, dma_private is used in the DMA management of ALSA. dma_buffer is in PCM_ Initialized in new(); Of course, the allocation of DMA buffer can also be realized in this part, but considering the reduction of fragmentation, it is still in pcm_new is allocated with the maximum size (i.e. buffer_bytes_max).
snd_ There is a struct snd in PCM_ pcm_ str streams[2]; These two elements represent playback stream and capture stream respectively, as shown in the figure above. There are also sound cards, equipment numbers and so on.
snd_pcm corresponding code:
struct snd_pcm { struct snd_card *card; struct list_head list; int device; /* device number */ unsigned int info_flags; unsigned short dev_class; unsigned short dev_subclass; char id[64]; char name[80]; struct snd_pcm_str streams[2]; struct mutex open_mutex; wait_queue_head_t open_wait; void *private_data; void (*private_free) (struct snd_pcm *pcm); bool internal; /* pcm is for internal use only */ bool nonatomic; /* whole PCM operations are in non-atomic context */ #if IS_ENABLED(CONFIG_SND_PCM_OSS) struct snd_pcm_oss oss; #endif };
snd_pcm_file corresponding code:
struct snd_pcm_file { struct snd_pcm_substream *substream; int no_compat_mmap; unsigned int user_pversion; /* supported protocol version */ };
snd_pcm_runtime code
struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; struct timespec trigger_tstamp; /* trigger timestamp */ bool trigger_tstamp_latched; /* trigger timestamp latched in low-level driver/hardware */ int overrange; snd_pcm_uframes_t avail_max; snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ u64 hw_ptr_wrap; /* offset for hw_ptr due to boundary wrap-around */ /* -- HW params -- */ snd_pcm_access_t access; /* access mode */ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ snd_pcm_subformat_t subformat; /* subformat */ unsigned int rate; /* rate in Hz */ unsigned int channels; /* channels */ snd_pcm_uframes_t period_size; /* period size */ unsigned int periods; /* periods */ snd_pcm_uframes_t buffer_size; /* buffer size */ snd_pcm_uframes_t min_align; /* Min alignment for the format */ size_t byte_align; unsigned int frame_bits; unsigned int sample_bits; unsigned int info; unsigned int rate_num; unsigned int rate_den; unsigned int no_period_wakeup: 1; /* -- SW params -- */ int tstamp_mode; /* mmap timestamp is updated */ unsigned int period_step; snd_pcm_uframes_t start_threshold; snd_pcm_uframes_t stop_threshold; snd_pcm_uframes_t silence_threshold; /* Silence filling happens when noise is nearest than this */ snd_pcm_uframes_t silence_size; /* Silence filling size */ snd_pcm_uframes_t boundary; /* pointers wrap point */ snd_pcm_uframes_t silence_start; /* starting pointer to silence area */ snd_pcm_uframes_t silence_filled; /* size filled with silence */ union snd_pcm_sync_id sync; /* hardware synchronization ID */ /* -- mmap -- */ struct snd_pcm_mmap_status *status; struct snd_pcm_mmap_control *control; /* -- locking / scheduling -- */ snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */ wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t tsleep; /* transfer sleep */ struct fasync_struct *fasync; /* -- private section -- */ void *private_data; void (*private_free)(struct snd_pcm_runtime *runtime); /* -- hardware description -- */ struct snd_pcm_hardware hw; struct snd_pcm_hw_constraints hw_constraints; /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ int tstamp_type; /* timestamp type */ /* -- DMA -- */ unsigned char *dma_area; /* DMA area */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ size_t dma_bytes; /* size of DMA area */ struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ /* -- audio timestamp config -- */ struct snd_pcm_audio_tstamp_config audio_tstamp_config; struct snd_pcm_audio_tstamp_report audio_tstamp_report; struct timespec driver_tstamp; #if IS_ENABLED(CONFIG_SND_PCM_OSS) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif };
3, pcm sound card creation
pcm sound card creation block diagram
From this block diagram, we can understand the logic of the whole pcm creation.
- When my driver wants to create a sound card, I go back and call snd in init function_ card_ Create function (snd_card_new) this is named according to the linux kernel version, which will generate a sound card
- Create PCM device and add sound card to call PCM Snd in C_ pcm_ New function,
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm) { return _snd_pcm_new(card, id, device, playback_count, capture_count, false, rpcm); } EXPORT_SYMBOL(snd_pcm_new);
Colleagues will also call the functions inside to create pcm streams and work related to pcm devices.
- Then go to snd_pcm_set_ops sets the control / operation interface function that operates the PCM, snd in the parameter_ pcm_ The function in OPS structure is usually the function we drive to implement.
- Finally, register the sound card and create the device node snd_card_register() will traverse all logical devices under the sound card at this stage and call the registration callback function of each device. The callback function establishes the device file nodes: / dev/snd/pcmCxxDxxp and / dev/snd/pcmCxxDxxc used for communication with user space application (alsa LIB)
- Then the module of the sound card will be loaded in the kernel loading.
The function to create the sound card is already in kernel / include / sound / PCM H. interfaces are reserved:
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm);
- I understand the first two as the name and id number of the sound card
- The parameter device indicates the number of pcm under the sound card currently created, and the first pcm device starts from 0
- Parameter playback_count indicates that the pcm will have several playback substream s and captures_ Count indicates that the pcm will have several capture substream s.
- I don't know what use this secondary pointer is for the time being.
At the same time, a function interface for setting pcm is also set up
void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, const struct snd_pcm_ops *ops);
4, Establishment of equipment file node
We talked about how to create a pcm sound card. Here we talk about how to create a device node under the sound card.
In fact, the snd at the end of the block diagram above_ pcm_ dev_ The register function is already creating a sound card
Function prototype:
static int snd_pcm_dev_register(struct snd_device *device) { int cidx, err; struct snd_pcm_substream *substream; struct snd_pcm *pcm; if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); if (err) goto unlock; for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; if (pcm->streams[cidx].substream == NULL) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; break; case SNDRV_PCM_STREAM_CAPTURE: devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } /* register pcm */ err = snd_register_device(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, &pcm->streams[cidx].dev); if (err < 0) { list_del_init(&pcm->list); goto unlock; } for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } pcm_call_notify(pcm, n_register); unlock: mutex_unlock(®ister_mutex); return err; }
4.1 snd_minor save information
snd_ The minor structure saves the context information of a logical device under the sound card. It is filled in at the establishment stage of the logical device. When the logical device is used, the corresponding information can be obtained from the structure. pcm equipment is no exception, also need to use this structure. The structure is in include / sound / core Defined in H.
struct snd_minor { int type; /* SNDRV_DEVICE_TYPE_XXX */ int card; /* card number */ int device; /* device number */ const struct file_operations *f_ops; /* file operations */ void *private_data; /* private data for f_ops->open */ struct device *dev; /* device for sysfs */ struct snd_card *card_ptr; /* assigned card instance */ };
A function has been mentioned in the above sound card registration stage for calling
/* register pcm */ err = snd_register_device(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, &pcm->streams[cidx].dev);
Let's go back to snd_register_device (kernel/sound/core/sound.c)
int snd_register_device(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, void *private_data, struct device *device) { int minor; int err = 0; struct snd_minor *preg; if (snd_BUG_ON(!device)) return -EINVAL; preg = kmalloc(sizeof *preg, GFP_KERNEL); if (preg == NULL) return -ENOMEM; preg->type = type; preg->card = card ? card->number : -1; preg->device = dev; preg->f_ops = f_ops; preg->private_data = private_data; preg->card_ptr = card; mutex_lock(&sound_mutex); minor = snd_find_free_minor(type, card, dev); if (minor < 0) { err = minor; goto error; } preg->dev = device; device->devt = MKDEV(major, minor); err = device_add(device); if (err < 0) goto error; snd_minors[minor] = preg; error: mutex_unlock(&sound_mutex); if (err < 0) kfree(preg); return err; } EXPORT_SYMBOL(snd_register_device);
-
First, allocate and initialize a snd_minor Fields in structure type: SNDRV_DEVICE_TYPE_PCM_PLAYBACK/SNDRV_DEVICE_TYPE_PCM_CAPTURE card: card Number of device: pcm The number of the instance, which is 0 in most cases f_ops: snd_pcm_f_ops private_data: Point to this pcm Examples of
- Determine the index value minor of the array according to the number of type, card and pcm. Minor is also used as the secondary equipment number of pcm equipment
- Put the SND_ The address of the minor structure is put into the global array snd_minors[minor], in / kernel / sound / core / sound A snd is defined in C_ Global array of minor pointers
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
- Finally, call device_. Create create device node