📄 usbmixer.c
字号:
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); kfree(namelist); kfree(cval); return -ENOMEM; } kctl->private_value = (unsigned long)namelist; kctl->private_free = usb_mixer_selector_elem_free; nameid = desc[desc[0] - 1]; len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); if (len) ; else if (nameid) snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); else { len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 0); if (! len) strlcpy(kctl->id.name, "USB", sizeof(kctl->id.name)); if ((state->oterm.type & 0xff00) == 0x0100) strlcat(kctl->id.name, " Capture Source", sizeof(kctl->id.name)); else strlcat(kctl->id.name, " Playback Source", sizeof(kctl->id.name)); } snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", cval->id, kctl->id.name, num_ins); if ((err = add_control_to_empty(state, kctl)) < 0) return err; return 0;}/* * parse an audio unit recursively */static int parse_audio_unit(struct mixer_build *state, int unitid){ unsigned char *p1; if (test_and_set_bit(unitid, state->unitbitmap)) return 0; /* the unit already visited */ p1 = find_audio_control_unit(state, unitid); if (!p1) { snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); return -EINVAL; } switch (p1[2]) { case INPUT_TERMINAL: return 0; /* NOP */ case MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case SELECTOR_UNIT: return parse_audio_selector_unit(state, unitid, p1); case FEATURE_UNIT: return parse_audio_feature_unit(state, unitid, p1); case PROCESSING_UNIT: return parse_audio_processing_unit(state, unitid, p1); case EXTENSION_UNIT: return parse_audio_extension_unit(state, unitid, p1); default: snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); return -EINVAL; }}static void snd_usb_mixer_free(struct usb_mixer_interface *mixer){ kfree(mixer->id_elems); if (mixer->urb) { kfree(mixer->urb->transfer_buffer); usb_free_urb(mixer->urb); } usb_free_urb(mixer->rc_urb); kfree(mixer->rc_setup_packet); kfree(mixer);}static int snd_usb_mixer_dev_free(struct snd_device *device){ struct usb_mixer_interface *mixer = device->device_data; snd_usb_mixer_free(mixer); return 0;}/* * create mixer controls * * walk through all OUTPUT_TERMINAL descriptors to search for mixers */static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer){ unsigned char *desc; struct mixer_build state; int err; const struct usbmix_ctl_map *map; struct usb_host_interface *hostif; hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; memset(&state, 0, sizeof(state)); state.chip = mixer->chip; state.mixer = mixer; state.buffer = hostif->extra; state.buflen = hostif->extralen; /* check the mapping table */ for (map = usbmix_ctl_maps; map->id; map++) { if (map->id == state.chip->usb_id) { state.map = map->map; state.selector_map = map->selector_map; mixer->ignore_ctl_error = map->ignore_ctl_error; break; } } desc = NULL; while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) { if (desc[0] < 9) continue; /* invalid descriptor? */ set_bit(desc[3], state.unitbitmap); /* mark terminal ID as visited */ state.oterm.id = desc[3]; state.oterm.type = combine_word(&desc[4]); state.oterm.name = desc[8]; err = parse_audio_unit(&state, desc[7]); if (err < 0) return err; } return 0;}static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid){ struct usb_mixer_elem_info *info; for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, info->elem_id);}static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer, int unitid){ if (!mixer->rc_cfg) return; /* unit ids specific to Extigy/Audigy 2 NX: */ switch (unitid) { case 0: /* remote control */ mixer->rc_urb->dev = mixer->chip->dev; usb_submit_urb(mixer->rc_urb, GFP_ATOMIC); break; case 4: /* digital in jack */ case 7: /* line in jacks */ case 19: /* speaker out jacks */ case 20: /* headphones out jack */ break; default: snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid); break; }}static void snd_usb_mixer_status_complete(struct urb *urb){ struct usb_mixer_interface *mixer = urb->context; if (urb->status == 0) { u8 *buf = urb->transfer_buffer; int i; for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) { snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n", buf[0], buf[1]); /* ignore any notifications not from the control interface */ if ((buf[0] & 0x0f) != 0) continue; if (!(buf[0] & 0x40)) snd_usb_mixer_notify_id(mixer, buf[1]); else snd_usb_mixer_memory_change(mixer, buf[1]); } } if (urb->status != -ENOENT && urb->status != -ECONNRESET) { urb->dev = mixer->chip->dev; usb_submit_urb(urb, GFP_ATOMIC); }}/* create the handler for the optional status interrupt endpoint */static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer){ struct usb_host_interface *hostif; struct usb_endpoint_descriptor *ep; void *transfer_buffer; int buffer_length; unsigned int epnum; hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; /* we need one interrupt input endpoint */ if (get_iface_desc(hostif)->bNumEndpoints < 1) return 0; ep = get_endpoint(hostif, 0); if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return 0; epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; buffer_length = le16_to_cpu(ep->wMaxPacketSize); transfer_buffer = kmalloc(buffer_length, GFP_KERNEL); if (!transfer_buffer) return -ENOMEM; mixer->urb = usb_alloc_urb(0, GFP_KERNEL); if (!mixer->urb) { kfree(transfer_buffer); return -ENOMEM; } usb_fill_int_urb(mixer->urb, mixer->chip->dev, usb_rcvintpipe(mixer->chip->dev, epnum), transfer_buffer, buffer_length, snd_usb_mixer_status_complete, mixer, ep->bInterval); usb_submit_urb(mixer->urb, GFP_KERNEL); return 0;}static void snd_usb_soundblaster_remote_complete(struct urb *urb){ struct usb_mixer_interface *mixer = urb->context; const struct rc_config *rc = mixer->rc_cfg; u32 code; if (urb->status < 0 || urb->actual_length < rc->packet_length) return; code = mixer->rc_buffer[rc->offset]; if (rc->length == 2) code |= mixer->rc_buffer[rc->offset + 1] << 8; /* the Mute button actually changes the mixer control */ if (code == rc->mute_code) snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); mixer->rc_code = code; wmb(); wake_up(&mixer->rc_waitq);}static int snd_usb_sbrc_hwdep_open(struct snd_hwdep *hw, struct file *file){ struct usb_mixer_interface *mixer = hw->private_data; if (test_and_set_bit(0, &mixer->rc_hwdep_open)) return -EBUSY; return 0;}static int snd_usb_sbrc_hwdep_release(struct snd_hwdep *hw, struct file *file){ struct usb_mixer_interface *mixer = hw->private_data; clear_bit(0, &mixer->rc_hwdep_open); smp_mb__after_clear_bit(); return 0;}static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf, long count, loff_t *offset){ struct usb_mixer_interface *mixer = hw->private_data; int err; u32 rc_code; if (count != 1 && count != 4) return -EINVAL; err = wait_event_interruptible(mixer->rc_waitq, (rc_code = xchg(&mixer->rc_code, 0)) != 0); if (err == 0) { if (count == 1) err = put_user(rc_code, buf); else err = put_user(rc_code, (u32 __user *)buf); } return err < 0 ? err : count;}static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file, poll_table *wait){ struct usb_mixer_interface *mixer = hw->private_data; poll_wait(file, &mixer->rc_waitq, wait); return mixer->rc_code ? POLLIN | POLLRDNORM : 0;}static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer){ struct snd_hwdep *hwdep; int err, len, i; for (i = 0; i < ARRAY_SIZE(rc_configs); ++i) if (rc_configs[i].usb_id == mixer->chip->usb_id) break; if (i >= ARRAY_SIZE(rc_configs)) return 0; mixer->rc_cfg = &rc_configs[i]; len = mixer->rc_cfg->packet_length; init_waitqueue_head(&mixer->rc_waitq); err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); if (err < 0) return err; snprintf(hwdep->name, sizeof(hwdep->name), "%s remote control", mixer->chip->card->shortname); hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC; hwdep->private_data = mixer; hwdep->ops.read = snd_usb_sbrc_hwdep_read; hwdep->ops.open = snd_usb_sbrc_hwdep_open; hwdep->ops.release = snd_usb_sbrc_hwdep_release; hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL); if (!mixer->rc_urb) return -ENOMEM; mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL); if (!mixer->rc_setup_packet) { usb_free_urb(mixer->rc_urb); mixer->rc_urb = NULL; return -ENOMEM; } mixer->rc_setup_packet->bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; mixer->rc_setup_packet->bRequest = GET_MEM; mixer->rc_setup_packet->wValue = cpu_to_le16(0); mixer->rc_setup_packet->wIndex = cpu_to_le16(0); mixer->rc_setup_packet->wLength = cpu_to_le16(len); usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, snd_usb_soundblaster_remote_complete, mixer); return 0;}#define snd_audigy2nx_led_info snd_ctl_boolean_mono_infostatic int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; return 0;}static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; int value = ucontrol->value.integer.value[0]; int err, changed; if (value > 1) return -EINVAL; changed = value != mixer->audigy2nx_leds[index]; err = snd_usb_ctl_msg(mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, value, index + 2, NULL, 0, 100); if (err < 0) return err; mixer->audigy2nx_leds[index] = value; return changed;}static struct snd_kcontrol_new snd_audigy2nx_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "CMSS LED Switch", .info = snd_audigy2nx_led_info, .get = snd_audigy2nx_led_get, .put = snd_audigy2nx_led_put, .private_value = 0, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Power LED Switch", .info = snd_audigy2nx_led_info, .get = snd_audigy2nx_led_get, .put = snd_audigy2nx_led_put, .private_value = 1, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Dolby Digital LED Switch", .info = snd_audigy2nx_led_info, .get = snd_audigy2nx_led_get, .put = snd_audigy2nx_led_put, .private_value = 2, },};static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer){ int i, err; for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { err = snd_ctl_add(mixer->chip->card, snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); if (err < 0) return err; } mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ return 0;}static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ static const struct { int unitid; const char *name; } jacks[] = { {4, "dig in "}, {7, "line in"}, {19, "spk out"}, {20, "hph out"}, }; struct usb_mixer_interface *mixer = entry->private_data; int i, err; u8 buf[3]; snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname); for (i = 0; i < ARRAY_SIZE(jacks); ++i) { snd_iprintf(buffer, "%s: ", jacks[i].name); err = snd_usb_ctl_msg(mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, jacks[i].unitid << 8, buf, 3, 100); if (err == 3 && buf[0] == 3) snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); else snd_iprintf(buffer, "?\n"); }}int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif){ static struct snd_device_ops dev_ops = { .dev_free = snd_usb_mixer_dev_free }; struct usb_mixer_interface *mixer; int err; strcpy(chip->card->mixername, "USB Mixer"); mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); if (!mixer) return -ENOMEM; mixer->chip = chip; mixer->ctrlif = ctrlif;#ifdef IGNORE_CTL_ERROR mixer->ignore_ctl_error = 1;#endif mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL); if (!mixer->id_elems) { kfree(mixer); return -ENOMEM; } if ((err = snd_usb_mixer_controls(mixer)) < 0 || (err = snd_usb_mixer_status_create(mixer)) < 0) goto _error; if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) goto _error; if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) { struct snd_info_entry *entry; if ((err = snd_audigy2nx_controls_create(mixer)) < 0) goto _error; if (!snd_card_proc_new(chip->card, "audigy2nx", &entry)) snd_info_set_text_ops(entry, mixer, snd_audigy2nx_proc_read); } err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); if (err < 0) goto _error; list_add(&mixer->list, &chip->mixer_list); return 0;_error: snd_usb_mixer_free(mixer); return err;}void snd_usb_mixer_disconnect(struct list_head *p){ struct usb_mixer_interface *mixer; mixer = list_entry(p, struct usb_mixer_interface, list); usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -