diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2005-04-29 10:26:14 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-05-29 04:05:40 -0400 |
commit | 6639b6c2367f884ca172b78d69f7da17bfab2e5e (patch) | |
tree | 5e2a119766b6199f7ce873e31c44e1462b206138 | |
parent | 84957a8ab086377a025e0448fa716ed5983f3c3a (diff) |
[ALSA] usb-audio - add mixer control notifications
USB generic driver
Add support for the optional status interrupt endpoint in audio control
interfaces, and translate USB status notifications into ALSA mixer
control notifications.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
-rw-r--r-- | sound/usb/usbmixer.c | 101 |
1 files changed, 98 insertions, 3 deletions
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 7bbccebd76c6..dd045ea6fb01 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c | |||
@@ -55,6 +55,8 @@ struct usb_mixer_interface { | |||
55 | unsigned int ctrlif; | 55 | unsigned int ctrlif; |
56 | struct list_head list; | 56 | struct list_head list; |
57 | unsigned int ignore_ctl_error; | 57 | unsigned int ignore_ctl_error; |
58 | struct urb *urb; | ||
59 | usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */ | ||
58 | }; | 60 | }; |
59 | 61 | ||
60 | 62 | ||
@@ -83,13 +85,15 @@ struct usb_mixer_build { | |||
83 | 85 | ||
84 | struct usb_mixer_elem_info { | 86 | struct usb_mixer_elem_info { |
85 | struct usb_mixer_interface *mixer; | 87 | struct usb_mixer_interface *mixer; |
88 | usb_mixer_elem_info_t *next_id_elem; /* list of controls with same id */ | ||
89 | snd_ctl_elem_id_t *elem_id; | ||
86 | unsigned int id; | 90 | unsigned int id; |
87 | unsigned int control; /* CS or ICN (high byte) */ | 91 | unsigned int control; /* CS or ICN (high byte) */ |
88 | unsigned int cmask; /* channel mask bitmap: 0 = master */ | 92 | unsigned int cmask; /* channel mask bitmap: 0 = master */ |
89 | int channels; | 93 | int channels; |
90 | int val_type; | 94 | int val_type; |
91 | int min, max, res; | 95 | int min, max, res; |
92 | unsigned int initialized: 1; | 96 | u8 initialized; |
93 | }; | 97 | }; |
94 | 98 | ||
95 | 99 | ||
@@ -414,6 +418,7 @@ static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_ou | |||
414 | 418 | ||
415 | static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl) | 419 | static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl) |
416 | { | 420 | { |
421 | usb_mixer_elem_info_t *cval = kctl->private_data; | ||
417 | int err; | 422 | int err; |
418 | 423 | ||
419 | while (snd_ctl_find_id(state->chip->card, &kctl->id)) | 424 | while (snd_ctl_find_id(state->chip->card, &kctl->id)) |
@@ -421,8 +426,12 @@ static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl) | |||
421 | if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { | 426 | if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) { |
422 | snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); | 427 | snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); |
423 | snd_ctl_free_one(kctl); | 428 | snd_ctl_free_one(kctl); |
429 | return err; | ||
424 | } | 430 | } |
425 | return err; | 431 | cval->elem_id = &kctl->id; |
432 | cval->next_id_elem = state->mixer->id_elems[cval->id]; | ||
433 | state->mixer->id_elems[cval->id] = cval; | ||
434 | return 0; | ||
426 | } | 435 | } |
427 | 436 | ||
428 | 437 | ||
@@ -1522,6 +1531,11 @@ static int parse_audio_unit(mixer_build_t *state, int unitid) | |||
1522 | 1531 | ||
1523 | static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) | 1532 | static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) |
1524 | { | 1533 | { |
1534 | kfree(mixer->id_elems); | ||
1535 | if (mixer->urb) { | ||
1536 | kfree(mixer->urb->transfer_buffer); | ||
1537 | usb_free_urb(mixer->urb); | ||
1538 | } | ||
1525 | kfree(mixer); | 1539 | kfree(mixer); |
1526 | } | 1540 | } |
1527 | 1541 | ||
@@ -1580,6 +1594,76 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) | |||
1580 | return 0; | 1594 | return 0; |
1581 | } | 1595 | } |
1582 | 1596 | ||
1597 | static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, | ||
1598 | int unitid) | ||
1599 | { | ||
1600 | usb_mixer_elem_info_t *info; | ||
1601 | |||
1602 | for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) | ||
1603 | snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
1604 | info->elem_id); | ||
1605 | } | ||
1606 | |||
1607 | static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs) | ||
1608 | { | ||
1609 | struct usb_mixer_interface *mixer = urb->context; | ||
1610 | |||
1611 | if (urb->status == 0) { | ||
1612 | u8 *buf = urb->transfer_buffer; | ||
1613 | int i; | ||
1614 | |||
1615 | for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) { | ||
1616 | snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n", | ||
1617 | buf[0], buf[1]); | ||
1618 | /* ignore any notifications not from the control interface */ | ||
1619 | if ((buf[0] & 0x0f) != 0) | ||
1620 | continue; | ||
1621 | if (!(buf[0] & 0x40)) | ||
1622 | snd_usb_mixer_notify_id(mixer, buf[1]); | ||
1623 | } | ||
1624 | } | ||
1625 | if (urb->status != -ENOENT && urb->status != -ECONNRESET) { | ||
1626 | urb->dev = mixer->chip->dev; | ||
1627 | usb_submit_urb(urb, GFP_ATOMIC); | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | /* create the handler for the optional status interrupt endpoint */ | ||
1632 | static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) | ||
1633 | { | ||
1634 | struct usb_host_interface *hostif; | ||
1635 | struct usb_endpoint_descriptor *ep; | ||
1636 | void *transfer_buffer; | ||
1637 | int buffer_length; | ||
1638 | unsigned int epnum; | ||
1639 | |||
1640 | hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; | ||
1641 | /* we need one interrupt input endpoint */ | ||
1642 | if (get_iface_desc(hostif)->bNumEndpoints < 1) | ||
1643 | return 0; | ||
1644 | ep = get_endpoint(hostif, 0); | ||
1645 | if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN || | ||
1646 | (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) | ||
1647 | return 0; | ||
1648 | |||
1649 | epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; | ||
1650 | buffer_length = le16_to_cpu(ep->wMaxPacketSize); | ||
1651 | transfer_buffer = kmalloc(buffer_length, GFP_KERNEL); | ||
1652 | if (!transfer_buffer) | ||
1653 | return -ENOMEM; | ||
1654 | mixer->urb = usb_alloc_urb(0, GFP_KERNEL); | ||
1655 | if (!mixer->urb) { | ||
1656 | kfree(transfer_buffer); | ||
1657 | return -ENOMEM; | ||
1658 | } | ||
1659 | usb_fill_int_urb(mixer->urb, mixer->chip->dev, | ||
1660 | usb_rcvintpipe(mixer->chip->dev, epnum), | ||
1661 | transfer_buffer, buffer_length, | ||
1662 | snd_usb_mixer_status_complete, mixer, ep->bInterval); | ||
1663 | usb_submit_urb(mixer->urb, GFP_KERNEL); | ||
1664 | return 0; | ||
1665 | } | ||
1666 | |||
1583 | int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) | 1667 | int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) |
1584 | { | 1668 | { |
1585 | static snd_device_ops_t dev_ops = { | 1669 | static snd_device_ops_t dev_ops = { |
@@ -1598,8 +1682,14 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) | |||
1598 | #ifdef IGNORE_CTL_ERROR | 1682 | #ifdef IGNORE_CTL_ERROR |
1599 | mixer->ignore_ctl_error = 1; | 1683 | mixer->ignore_ctl_error = 1; |
1600 | #endif | 1684 | #endif |
1685 | mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL); | ||
1686 | if (!mixer->id_elems) { | ||
1687 | kfree(mixer); | ||
1688 | return -ENOMEM; | ||
1689 | } | ||
1601 | 1690 | ||
1602 | if ((err = snd_usb_mixer_controls(mixer)) < 0) { | 1691 | if ((err = snd_usb_mixer_controls(mixer)) < 0 || |
1692 | (err = snd_usb_mixer_status_create(mixer)) < 0) { | ||
1603 | snd_usb_mixer_free(mixer); | 1693 | snd_usb_mixer_free(mixer); |
1604 | return err; | 1694 | return err; |
1605 | } | 1695 | } |
@@ -1615,4 +1705,9 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) | |||
1615 | 1705 | ||
1616 | void snd_usb_mixer_disconnect(struct list_head *p) | 1706 | void snd_usb_mixer_disconnect(struct list_head *p) |
1617 | { | 1707 | { |
1708 | struct usb_mixer_interface *mixer; | ||
1709 | |||
1710 | mixer = list_entry(p, struct usb_mixer_interface, list); | ||
1711 | if (mixer->urb) | ||
1712 | usb_kill_urb(mixer->urb); | ||
1618 | } | 1713 | } |