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 ?
- ~ # 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
~ #
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 ?
- // 套接字接口函數集
- 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;
- }
//套接字接口函數集
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 ?
- 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;
- }
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 ?
- 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;
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 ?
- #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;
- }
#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一緻,底層如何管理通話通路的一系列部件開關,上層都不需要關心。