天天看點

ALSA的ioctl - hwdep

hwdep子產品簡述

字元裝置驅動中,ioctl是一個很常見的IO裝置操作函數,可以自定義cmd指令字并實作對應的裝置IO控制。

音頻裝置的控制有所不同:驅動層大部分控制操作定義各種snd_kcontrol_new,然後注冊到SNDRV_DEV_CONTROL子產品中(sound\core\control.c),詳見snd_kcontrol探究;而上層調用alsa-lib的snd_ctl_open/snd_mixer_open來打開底層的SNDRV_DEV_CONTROL子產品,詳見DAPM之二:audio paths與dapm kcontrol。這方法常見于mixer-control,如音量調整、部件開關、通路連接配接等等。

除此之外,alsa還是可以實作類似于ioctl的函數的,隻不過它封裝成一個裝置子產品SNDRV_DEV_HWDEP,代碼sound\core\ hwdep.c。該子產品實作了read/write/ioctl/llseek/poll/mmap等接口。hwdep是Hardware Dependant Interface的簡稱。

題外話:如果想看自己闆上的alsa有什麼類型的裝置可以cat /proc/asound/devices,如

view plain print ?

  1. ~ # cat /proc/asound/devices   
  2.   0: [ 0]   : control  
  3.   4: [ 0- 0]: hardware dependent  
  4.  16: [ 0- 0]: digital audio playback  
  5.  24: [ 0- 0]: digital audio capture  
  6.  33:        : timer  

~ #

cat /proc/asound/devices

0: [ 0] : control

4: [ 0- 0]: hardware dependent

16: [ 0- 0]: digital audio playback

24: [ 0- 0]: digital audio capture

33: : timer裝置節點号minor=0是control,=4是hwdep,=16是pcm- playback,=24是pcm-capture,=33是timer。

如下簡單分析ioctl:

view plain print ?

  1. // 套接字接口函數集  
  2. static const struct file_operations snd_hwdep_f_ops =  
  3. {  
  4.     .owner =    THIS_MODULE,  
  5.     .llseek =   snd_hwdep_llseek,  
  6.     .read =     snd_hwdep_read,  
  7.     .write =    snd_hwdep_write,  
  8.     .open =     snd_hwdep_open,  
  9.     .release =  snd_hwdep_release,  
  10.     .poll =     snd_hwdep_poll,  
  11.     .unlocked_ioctl =   snd_hwdep_ioctl,  
  12.     .compat_ioctl = snd_hwdep_ioctl_compat,  
  13.     .mmap =     snd_hwdep_mmap,  
  14. };  
  15. static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,  
  16.                 unsigned long arg)  
  17. {  
  18.     struct snd_hwdep *hw = file->private_data;  
  19.     void __user *argp = (void __user *)arg;  
  20.     switch (cmd) {  
  21.     case SNDRV_HWDEP_IOCTL_PVERSION:  
  22.         return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);  
  23.     case SNDRV_HWDEP_IOCTL_INFO:  
  24.         return snd_hwdep_info(hw, argp);  
  25.     case SNDRV_HWDEP_IOCTL_DSP_STATUS:  
  26.         return snd_hwdep_dsp_status(hw, argp);  
  27.     case SNDRV_HWDEP_IOCTL_DSP_LOAD:  
  28.         return snd_hwdep_dsp_load(hw, argp);  
  29.     }  
  30.     if (hw->ops.ioctl)  
  31.         return hw->ops.ioctl(hw, file, cmd, arg);  
  32.     return -ENOTTY;  
  33. }  

//套接字接口函數集

static const struct file_operations snd_hwdep_f_ops =

{

.owner = THIS_MODULE,

.llseek = snd_hwdep_llseek,

.read = snd_hwdep_read,

.write = snd_hwdep_write,

.open = snd_hwdep_open,

.release = snd_hwdep_release,

.poll = snd_hwdep_poll,

.unlocked_ioctl = snd_hwdep_ioctl,

.compat_ioctl = snd_hwdep_ioctl_compat,

.mmap = snd_hwdep_mmap,

};

static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,

unsigned long arg)

{

struct snd_hwdep *hw = file->private_data;

void __user *argp = (void __user *)arg;

switch (cmd) {

case SNDRV_HWDEP_IOCTL_PVERSION:

return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp);

case SNDRV_HWDEP_IOCTL_INFO:

return snd_hwdep_info(hw, argp);

case SNDRV_HWDEP_IOCTL_DSP_STATUS:

return snd_hwdep_dsp_status(hw, argp);

case SNDRV_HWDEP_IOCTL_DSP_LOAD:

return snd_hwdep_dsp_load(hw, argp);

}

if (hw->ops.ioctl)

return hw->ops.ioctl(hw, file, cmd, arg);

return -ENOTTY;

}從snd_hwdep_ioctl可以看出,系統預設隻有4個cmd,功能主要是download dsp image。從return hw->ops.ioctl(hw, file, cmd, arg)語句可以看出,我們可自定義cmd和ioctl函數。

實作自定義的hwdep操作函數

1、 首先實作需要的操作函數:

view plain print ?

  1. static int my_hwdep_open(struct snd_hwdep * hw, struct file *file)  
  2. {  
  3.     printk(KERN_INFO "my_hwdep_open\n");  
  4.     return 0;  
  5. }  
  6. static int my_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)  
  7. {  
  8. #define MY_SOC_IOCTL_SET_CALL_PATH   _IOWR('H', 0x10, int)  
  9.     switch (cmd) {  
  10.     case MY_SOC_IOCTL_SET_CALL_PATH:  
  11.         //設定電話語音通路  
  12.         return 0;  
  13.         break;  
  14.     //......  
  15.     }  
  16.     err("Not supported ioctl for MY-HWDEP");  
  17.     return -ENOIOCTLCMD;  
  18. }  

static int

my_hwdep_open(struct snd_hwdep * hw, struct file *file)

{

printk(KERN_INFO "my_hwdep_open\n");

return 0;

}

static int my_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,

unsigned int cmd, unsigned long arg)

{

#define MY_SOC_IOCTL_SET_CALL_PATH _IOWR('H', 0x10, int)

switch (cmd) {

case MY_SOC_IOCTL_SET_CALL_PATH:

//設定電話語音通路

return 0;

break;

//......

}

err("Not supported ioctl for MY-HWDEP");

return -ENOIOCTLCMD;

}

2、 注冊操作函數到hwdep子產品: view plain print ?

  1. struct snd_hwdep *hwdep;  
  2. if (snd_hwdep_new(codec->card, "MY-HWDEP", 0, &hwdep) < 0) {  
  3.     printk(KERN_ERR "create MY-HWDEP fail");  
  4.     return;  
  5. }  
  6. sprintf(hwdep->name, "MY-HWDEP %d", 0);  
  7. hwdep->iface = SNDRV_HWDEP_IFACE_WMT;  
  8. hwdep->ops.open = wmt_hwdep_open;  
  9. hwdep->ops.ioctl = wmt_hwdep_ioctl;  

struct

snd_hwdep *hwdep;

if (snd_hwdep_new(codec->card, "MY-HWDEP", 0, &hwdep) < 0) {

printk(KERN_ERR "create MY-HWDEP fail");

return;

}

sprintf(hwdep->name, "MY-HWDEP %d", 0);

hwdep->iface = SNDRV_HWDEP_IFACE_WMT;

hwdep->ops.open = wmt_hwdep_open;

hwdep->ops.ioctl = wmt_hwdep_ioctl;這裡摘錄snd_hwdep_new的代碼注釋,讓大家更明白上面的注冊過程: view plain print ?

/**

* snd_hwdep_new - create a new hwdep instance

* @card: the card instance

* @id: the id string

* @device: the device index (zero-based)

* @rhwdep: the pointer to store the new hwdep instance

*

* Creates a new hwdep instance with the given index on the card.

* The callbacks (hwdep->ops) must be set on the returned instance

* after this call manually by the caller.

*

* Returns zero if successful, or a negative error code on failure.

*/按照以上實作hwdep ioctl後,上層可以通過alsa-lib的相關接口來調用。

上層調用範例

view plain print ?

  1. #include <fcntl.h>  
  2. #include <sys/ioctl.h>  
  3. #include <alsa/hwdep.h>  
  4. #include <alsa/error.h>  
  5. #include <stdio.h>  
  6. #define MY_SOC_IOCTL_SET_CALL_PATH   _IOWR('H', 0x10, int)  
  7. int main()  
  8. {  
  9.     const char *devicename = "hw:0,0";  
  10.     snd_hwdep_t *hwdep;  
  11.     int err;  
  12.     int enable = 1;  
  13.     if ((err = snd_hwdep_open(&hwdep, devicename, O_RDWR)) < 0) {  
  14.         printf("hwdep interface open error: %s \n", snd_strerror(err));  
  15.         return -1;  
  16.     }  
  17.     if ((err = snd_hwdep_ioctl(hwdep, MY_SOC_IOCTL_SET_CALL_PATH, &enable)) < 0) {  
  18.         printf("hwdep ioctl error: %s \n", snd_strerror(err));  
  19.     }  
  20.     snd_hwdep_close(hwdep);  
  21.     return 0;  
  22. }  

#include

<fcntl.h>

#include <sys/ioctl.h>

#include <alsa/hwdep.h>

#include <alsa/error.h>

#include <stdio.h>

#define MY_SOC_IOCTL_SET_CALL_PATH _IOWR('H', 0x10, int)

int main()

{

const char *devicename = "hw:0,0";

snd_hwdep_t *hwdep;

int err;

int enable = 1;

if ((err = snd_hwdep_open(&hwdep, devicename, O_RDWR)) < 0) {

printf("hwdep interface open error: %s \n", snd_strerror(err));

return -1;

}

if ((err = snd_hwdep_ioctl(hwdep, MY_SOC_IOCTL_SET_CALL_PATH,

&enable)) < 0) {

printf("hwdep ioctl error: %s \n", snd_strerror(err));

}

snd_hwdep_close(hwdep);

return 0;

}

總結

可以看出hwdep的本意主要是用于download dsp image,但通過它也可實作類似于其他字元裝置的ioctl。我說過音頻大多控制是通過snd_kcontrol,但有些功能如果使用這種方式會比較繁瑣且子產品太過耦合。

舉個例子:電話語音通路,它不同于音樂回放通路,通話時才需要打開。如果用snd_kcontrol,則上層需要調用多個control.set,并且更換CODEC晶片的話,上層也要跟着修改control name;如果使用hwdep ioctl的話,就沒有這個問題,隻需要保證指令字cmd一緻,底層如何管理通話通路的一系列部件開關,上層都不需要關心。

繼續閱讀