一.tinymix調用,主要是控制接口,調用到底層的control
操作方法:tinymix 0 SPK
Tinymix.c (external\tinyalsa)
int main(int argc, char **argv)
mixer = mixer_open(card);
snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
mixer = mixer_open(card); //單獨分析1,打開control,並獲取它們的信息
if (argc == 1)
tinymix_list_controls(mixer);
num_ctls = mixer_get_num_ctls(mixer); //得到mixer_ctl的個數
for (i = 0; i < num_ctls; i++) //獲取所有的ctl 的名字,類型,值,打印出來
ctl = mixer_get_ctl(mixer, i);
name = mixer_ctl_get_name(ctl);
type = mixer_ctl_get_type_string(ctl);
num_values = mixer_ctl_get_num_values(ctl);
printf("%d\t%s\t%d\t%-40s", i, type, num_values, name);
tinymix_detail_control(mixer, name, 0); //獲取更詳細的信息
else if (argc == 2)
tinymix_detail_control(mixer, argv[1], 1); //獲取詳細信息
else //給control賦值
tinymix_set_value(mixer, argv[1], &argv[2], argc - 2);
type = mixer_ctl_get_type(ctl); //得到control的類型
num_ctl_values = mixer_ctl_get_num_values(ctl); //值得個數
if (isdigit(values[0][0]))
f (num_values == 1)
///* Set all values the same */
for (i = 0; i < num_ctl_values; i++)
mixer_ctl_set_value(ctl, i, value) //設置一樣的值
ev.id.numid = ctl->info->id.numid;
//讀取control的值,驅動分析五
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
//修改control的值,驅動分析六
ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
else
/* Set multiple values */
for (i = 0; i < num_values; i++)
mixer_ctl_set_value(ctl, i, atoi(values[i]))
else
if (type == MIXER_CTL_TYPE_ENUM) //如果是枚舉
mixer_ctl_set_enum_by_string(ctl, values[0])
單獨分析1
struct mixer *mixer_open(unsigned int card)
fd = open(fn, O_RDWR); //打開/dev/snd/controlC0,驅動分析一
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) //獲取snd_ctl_elem_list結構體,主要是個數,驅動分析二
mixer = calloc(1, sizeof(*mixer));
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); //分配mixer_ctl
mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); //snd_ctl_elem_info
ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) ///驅動分析三.主要是獲取card的信息,主要是名字一類的
eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); //分配snd_ctl_elem_id
mixer->count = elist.count;
mixer->fd = fd;
elist.space = mixer->count;
elist.pids = eid;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) //這里才是真正的獲取snd_ctl_elem_list,前面只是獲取個數,已經分析
for (n = 0; n < mixer->count; n++)
struct snd_ctl_elem_info *ei = mixer->elem_info + n;
ei->id.numid = eid[n].numid;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) //驅動分析四,獲取control的信息
mixer->ctl[n].info = ei;
mixer->ctl[n].mixer = mixer;
if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) //如果是枚舉類型,主要是這個SOC_GPIO_ENUM定義的control
char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
mixer->ctl[n].ename = enames;
for (m = 0; m < ei->value.enumerated.items; m++) //如果是枚舉型,就需要單獨獲取每一個枚舉
tmp.id.numid = ei->id.numid;
tmp.value.enumerated.item = m;
ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp)
二.驅動分析一
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
這個文件操作函數已經在開機的注冊,見rk音頻驅動分析之machine
1.打開播放dev,驅動分析1
//主要是找到對應的設備,創建snd_monitor_file,初始化snd_ctl_file
snd_ctl_open
//通過次設備號,找到聲卡
card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
//add the file to the file list of the card,這個file是用戶層傳下來的句柄
err = snd_card_file_add(card, file);
struct snd_monitor_file *mfile;
mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
INIT_LIST_HEAD(&mfile->shutdown_list);
list_add(&mfile->list, &card->files_list); //加入到card->files_list列表
try_module_get(card->module)
///* waiting events for read */
INIT_LIST_HEAD(&ctl->events);
//等待隊列頭
init_waitqueue_head(&ctl->change_sleep);
//賦值snd_ctl_file結構體
ctl->card = card;
ctl->prefer_pcm_subdevice = -1;
ctl->prefer_rawmidi_subdevice = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
list_add_tail(&ctl->list, &card->ctl_files); //放入card->ctl_files鏈表
三.驅動分析二
調用到snd_ctl_ioctl里面的
case SNDRV_CTL_IOCTL_ELEM_LIST然后,調用return snd_ctl_elem_list(card, argp);
static int snd_ctl_elem_list(struct snd_card *card,
struct snd_ctl_elem_list __user *_list)
snd_ctl_elem_list list;
copy_from_user(&list, _list, sizeof(list))
offset = list.offset; ///* W: first element ID to get */,第一個元素ID
space = list.space; /* W: count of element IDs to get */ 得到元素ID的數量
if (space > 0)
/* allocate temporary buffer for atomic operation */
dst = vmalloc(space * sizeof(struct snd_ctl_elem_id)); //snd_ctl_elem_id結構體,用於記錄element
list.count = card->controls_count; //contols的個數
plist = card->controls.next; //鏈表指針
while (plist != &card->controls) //snd_kcontrol不等於鏈表頭,主要是遍歷card->controls鏈表
kctl = snd_kcontrol(plist); //找到鏈表里面的snd_kcontrol
if (offset < kctl->count) // kctl->count是count of same elements ,offset 一般是小於1
break;
offset -= kctl->count; //offset = offset - kctl->count。
plist = plist->next; //下一個snd_kcontrol
while (space > 0 && plist != &card->controls) //// 遍歷card->controls鏈表上的所有登記數據,當然包括微件widget合成的數據
kctl = snd_kcontrol(plist);
for (jidx = offset; space > 0 && jidx < kctl->count; jidx++)
snd_ctl_build_ioff(id, kctl, jidx);
id++;
space--;
list.used++; //加一
plist = plist->next; //找到下一個snd_kcontrol
copy_to_user(list.pids, dst, list.used * sizeof(struct snd_ctl_elem_id) //拷貝給user
else
list.count = card->controls_count;
copy_to_user(_list, &list, sizeof(list))
四.驅動分析三
調用snd_ctl_ioctl里面的snd_ctl_card_info(card, ctl, cmd, argp);
//主要是獲取card的信息,主要是名字一類的
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
info->card = card->number;
strlcpy(info->id, card->id, sizeof(info->id));
strlcpy(info->driver, card->driver, sizeof(info->driver));
strlcpy(info->name, card->shortname, sizeof(info->name));
strlcpy(info->longname, card->longname, sizeof(info->longname));
strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
strlcpy(info->components, card->components, sizeof(info->components));
五.驅動分析四
調用snd_ctl_ioctl的caseSNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info_user(ctl, argp);
struct snd_ctl_elem_info info;
copy_from_user(&info, _info, sizeof(info) //從用戶空間拷貝
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0); //wait until the power-state is changed. /* full On */
if (snd_power_get_state(card) == power_state) //等待聲卡的電源狀態變為SNDRV_CTL_POWER_D0
return 0;
init_waitqueue_entry(&wait, current);
add_wait_queue(&card->power_sleep, &wait);
while (1)
if (snd_power_get_state(card) == power_state)
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(30 * HZ);
remove_wait_queue(&card->power_sleep, &wait);
result = snd_ctl_elem_info(ctl, &info);
//通過id找到control 實體
kctl = snd_ctl_find_id(card, &info->id); //find the control instance with the given id
////獲取snd_ctl_elem_info的信息,調用snd_soc_info_volsw這種,在定義snd_kcontrol_new的宏里面賦值SOC_SINGLE
result = kctl->info(kctl, info);
copy_to_user(_info, &info, sizeof(info)) //拷貝給應用
六.驅動分析五
調用snd_ctl_ioctl的case SNDRV_CTL_IOCTL_ELEM_READ:return snd_ctl_elem_read_user(card, argp);
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
control = memdup_user(_control, sizeof(*control)); //從應用拷貝
result = snd_power_wait(card, SNDRV_CTL_POWER_D0); //等待card上電
result = snd_ctl_elem_read(card, control);
kctl = snd_ctl_find_id(card, &control->id); //從card->controls找到control
list_for_each_entry(kctl, &card->controls, list)
//一個kcotrol占據多個numid,所以需要確認具體一個numid,在這個中間的偏移,根據這個偏移index值,就可以確定存儲 snd_ctl_elem_value.value.value[index]的下標
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL)
snd_ctl_build_ioff(&control->id, kctl, index_offset); //更新index和numid為下面的put操作准備
result = kctl->get(kctl, control); //如果是SOC_SINGLE宏定義的控件put函數指針則是: snd_soc_put_volsw
copy_to_user(_control, control, sizeof(*control))
六.驅動分析六
調用snd_ctl_ioctl的case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp);
result = snd_ctl_elem_write(card, file, control);
kctl = snd_ctl_find_id(card, &control->id);
index_offset = snd_ctl_get_ioff(kctl, &control->id);
vd = &kctl->vd[index_offset];
snd_ctl_build_ioff(&control->id, kctl, index_offset);
result = kctl->put(kctl, control); //這里是真正的寫
struct snd_ctl_elem_id id = control->id;
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &id); //與讀不同的就是這里會發一個消息出來
list_for_each_entry(ctl, &card->ctl_files, list) //獲取每一個snd_ctl_file
if (!ctl->subscribed) //如果不是active,就返回
continue;
list_for_each_entry(ev, &ctl->events, list) //遍歷snd_kctl_event
if (ev->id.numid == id->numid) //如果是現在這個,就跳出
ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
if (ev) {
ev->id = *id;
ev->mask = mask;
list_add_tail(&ev->list, &ctl->events);