OSDN Git Service

sound: usb: Add support for usb audio qmi service
authorHemant Kumar <hemantk@codeaurora.org>
Thu, 28 Jan 2016 19:41:42 +0000 (11:41 -0800)
committerKyle Yan <kyan@codeaurora.org>
Tue, 12 Jul 2016 01:56:26 +0000 (18:56 -0700)
Upon usb audio device enumeration remote entity
communicates with usb audio qmi server to enable
or disable particular audio stream on usb audio
device. This off loades ISOC data transfer
operations to remote entity and allows APQ to go
to power collapse more frequently for better
power saving.

Change-Id: I2c9b0ba0837f8fec5c320e3117aa3b2f553db6b2
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
sound/usb/Kconfig
sound/usb/Makefile
sound/usb/card.c
sound/usb/stream.c
sound/usb/usb_audio_qmi_svc.c [new file with mode: 0644]
sound/usb/usb_audio_qmi_v01.c [new file with mode: 0644]
sound/usb/usb_audio_qmi_v01.h [new file with mode: 0644]
sound/usb/usbaudio.h

index a452ad7..f32cfa4 100644 (file)
@@ -162,5 +162,13 @@ config SND_BCD2000
 
 source "sound/usb/line6/Kconfig"
 
+config SND_USB_AUDIO_QMI
+       tristate "USB Audio QMI Service driver"
+       depends on MSM_QMI_INTERFACE
+       help
+         Starts USB Audio QMI server to communicate with remote entity
+         to perform operations like enable or disable particular audio
+         stream on a connected USB device.
+
 endif  # SND_USB
 
index 2d2d122..d2ac038 100644 (file)
@@ -26,3 +26,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
 
 obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
 obj-$(CONFIG_SND_USB_LINE6)    += line6/
+obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o
index 0a189d3..e94f4d2 100644 (file)
@@ -387,6 +387,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
        list_for_each_entry_safe(ep, n, &chip->ep_list, list)
                snd_usb_endpoint_free(ep);
 
+       mutex_destroy(&chip->dev_lock);
        mutex_destroy(&chip->mutex);
        kfree(chip);
        return 0;
@@ -442,6 +443,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        }
 
        mutex_init(&chip->mutex);
+       mutex_init(&chip->dev_lock);
        init_waitqueue_head(&chip->shutdown_wait);
        chip->index = idx;
        chip->dev = dev;
index 8ee14f2..81172a3 100644 (file)
@@ -69,9 +69,14 @@ static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
 static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
 {
        struct snd_usb_stream *stream = pcm->private_data;
+       struct snd_usb_audio *chip;
+
        if (stream) {
+               mutex_lock(&stream->chip->dev_lock);
+               chip = stream->chip;
                stream->pcm = NULL;
                snd_usb_audio_stream_free(stream);
+               mutex_unlock(&chip->dev_lock);
        }
 }
 
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
new file mode 100644 (file)
index 0000000..145bc9e
--- /dev/null
@@ -0,0 +1,449 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/usb/audio.h>
+#include <linux/uaccess.h>
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <linux/usb.h>
+#include <linux/qmi_encdec.h>
+#include <soc/qcom/msm_qmi_interface.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "helper.h"
+#include "pcm.h"
+#include "usb_audio_qmi_v01.h"
+
+#define SND_PCM_CARD_NUM_MASK 0xffff0000
+#define SND_PCM_DEV_NUM_MASK 0xff00
+#define SND_PCM_STREAM_DIRECTION 0xff
+
+struct uaudio_dev {
+       atomic_t in_use;
+       struct kref kref;
+       wait_queue_head_t disconnect_wq;
+};
+
+static struct uaudio_dev uadev[SNDRV_CARDS];
+
+struct uaudio_qmi_svc {
+       struct qmi_handle *uaudio_svc_hdl;
+       void *curr_conn;
+       struct work_struct recv_msg_work;
+       struct workqueue_struct *uaudio_wq;
+       ktime_t t_request_recvd;
+       ktime_t t_resp_sent;
+};
+
+static struct uaudio_qmi_svc *uaudio_svc;
+
+static struct msg_desc uaudio_stream_req_desc = {
+       .max_msg_len = QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
+       .msg_id = QMI_UAUDIO_STREAM_REQ_V01,
+       .ei_array = qmi_uaudio_stream_req_msg_v01_ei,
+};
+
+static struct msg_desc uaudio_stream_resp_desc = {
+       .max_msg_len = QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
+       .msg_id = QMI_UAUDIO_STREAM_RESP_V01,
+       .ei_array = qmi_uaudio_stream_resp_msg_v01_ei,
+};
+
+static int prepare_qmi_response(struct snd_usb_substream *subs,
+               struct qmi_uaudio_stream_resp_msg_v01 *resp, int card_num)
+{
+       struct usb_interface *iface;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct usb_host_endpoint *ep;
+       struct uac_format_type_i_continuous_descriptor *fmt;
+       struct uac_format_type_i_discrete_descriptor *fmt_v1;
+       struct uac_format_type_i_ext_descriptor *fmt_v2;
+       struct uac1_as_header_descriptor *as;
+       struct uac1_ac_header_descriptor *ac;
+       int protocol;
+
+       iface = usb_ifnum_to_if(subs->dev, subs->interface);
+       if (!iface) {
+               pr_err("%s: interface # %d does not exist\n", __func__,
+                       subs->interface);
+               goto err;
+       }
+
+       alts = &iface->altsetting[subs->altset_idx];
+       altsd = get_iface_desc(alts);
+       protocol = altsd->bInterfaceProtocol;
+
+       /* get format type */
+       fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+                       UAC_FORMAT_TYPE);
+       if (!fmt) {
+               pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__,
+                       subs->interface, subs->altset_idx);
+               goto err;
+       }
+
+       if (protocol == UAC_VERSION_1) {
+               as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
+                       UAC_AS_GENERAL);
+               if (!as) {
+                       pr_err("%s: %u:%d : no UAC_AS_GENERAL desc\n", __func__,
+                               subs->interface, subs->altset_idx);
+                       goto err;
+               }
+               resp->bDelay = as->bDelay;
+               fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt;
+               resp->bSubslotSize = fmt_v1->bSubframeSize;
+       } else if (protocol == UAC_VERSION_2) {
+               fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt;
+               resp->bSubslotSize = fmt_v2->bSubslotSize;
+       } else {
+               pr_err("%s: unknown protocol version %x\n", __func__, protocol);
+               goto err;
+       }
+
+       ac = snd_usb_find_csint_desc(alts->extra,
+                                                alts->extralen,
+                                                NULL, UAC_HEADER);
+       if (!ac) {
+               pr_err("%s: %u:%d : no UAC_HEADER desc\n", __func__,
+                       subs->interface, subs->altset_idx);
+               goto err;
+       }
+       resp->bcdADC = ac->bcdADC;
+
+       resp->slot_id = subs->dev->slot_id;
+
+       memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
+
+       ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe);
+       if (!ep) {
+               pr_err("%s: data ep # %d context is null\n", __func__,
+                       subs->data_endpoint->ep_num);
+               goto err;
+       }
+       memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
+
+       if (subs->sync_endpoint) {
+               ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe);
+               if (!ep) {
+                       pr_err("%s: sync ep # %d context is null\n", __func__,
+                               subs->sync_endpoint->ep_num);
+                       goto err;
+               }
+               memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
+       }
+
+       if (!atomic_read(&uadev[card_num].in_use)) {
+               kref_init(&uadev[card_num].kref);
+               init_waitqueue_head(&uadev[card_num].disconnect_wq);
+               atomic_set(&uadev[card_num].in_use, 1);
+       } else {
+               kref_get(&uadev[card_num].kref);
+       }
+
+       return 0;
+err:
+       return -ENODEV;
+}
+
+void uaudio_disconnect_cb(struct snd_usb_audio *chip)
+{
+       int ret;
+       struct uaudio_dev *dev;
+       int card_num = chip->card_num;
+
+       mutex_lock(&chip->dev_lock);
+       if (card_num >=  SNDRV_CARDS) {
+               pr_err("%s: invalid card number\n", __func__);
+               goto done;
+       }
+
+       dev = &uadev[card_num];
+       if (atomic_read(&dev->in_use)) {
+               ret = wait_event_interruptible(dev->disconnect_wq,
+                               !atomic_read(&dev->in_use));
+               if (ret < 0) {
+                       pr_debug("%s: failed with ret %d\n", __func__, ret);
+                       goto done;
+               }
+       }
+done:
+       mutex_unlock(&chip->dev_lock);
+}
+
+static void uaudio_dev_release(struct kref *kref)
+{
+       struct uaudio_dev *dev = container_of(kref, struct uaudio_dev, kref);
+
+       atomic_set(&dev->in_use, 0);
+       wake_up(&dev->disconnect_wq);
+}
+
+static int handle_uaudio_stream_req(void *req_h, void *req)
+{
+       struct qmi_uaudio_stream_req_msg_v01 *req_msg;
+       struct qmi_uaudio_stream_resp_msg_v01 resp = {0};
+       struct snd_usb_substream *subs;
+       struct snd_usb_audio *chip = NULL;
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+       u8 pcm_card_num, pcm_dev_num, direction;
+       int ret = 0;
+
+       req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)req;
+
+       direction = req_msg->usb_token & SND_PCM_STREAM_DIRECTION;
+       pcm_dev_num = (req_msg->usb_token & SND_PCM_DEV_NUM_MASK) >> 8;
+       pcm_card_num = (req_msg->usb_token & SND_PCM_CARD_NUM_MASK) >> 16;
+
+       pr_debug("%s:card#:%d dev#:%d dir:%d en:%d fmt:%d rate:%d #ch:%d\n",
+               __func__, pcm_card_num, pcm_dev_num, direction, req_msg->enable,
+               req_msg->audio_format, req_msg->bit_rate,
+               req_msg->number_of_ch);
+
+       if (pcm_card_num >= SNDRV_CARDS) {
+               pr_err("%s: invalid card # %u", __func__, pcm_card_num);
+               ret = -EINVAL;
+               goto response;
+       }
+
+       subs = find_snd_usb_substream(pcm_card_num, pcm_dev_num, direction,
+                                       &chip, uaudio_disconnect_cb);
+       if (!subs || !chip || atomic_read(&chip->shutdown)) {
+               pr_err("%s: can't find substream for card# %u, dev# %u dir%u\n",
+                       __func__, pcm_card_num, pcm_dev_num, direction);
+               ret = -ENODEV;
+               goto response;
+       }
+
+       mutex_lock(&chip->dev_lock);
+       if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm
+                       || !subs->stream->chip) {
+               ret = -ENODEV;
+               mutex_unlock(&chip->dev_lock);
+               goto response;
+       }
+
+       subs->pcm_format = req_msg->audio_format;
+       subs->channels = req_msg->number_of_ch;
+       subs->cur_rate = req_msg->bit_rate;
+
+       ret = snd_usb_enable_audio_stream(subs, req_msg->enable);
+
+       if (!ret && req_msg->enable)
+               ret = prepare_qmi_response(subs, &resp, pcm_card_num);
+
+       mutex_unlock(&chip->dev_lock);
+
+response:
+       if (!req_msg->enable && ret != -EINVAL) {
+               if (atomic_read(&uadev[pcm_card_num].in_use))
+                       kref_put(&uadev[pcm_card_num].kref,
+                                       uaudio_dev_release);
+       }
+
+       resp.status = ret;
+       ret = qmi_send_resp_from_cb(svc->uaudio_svc_hdl, svc->curr_conn, req_h,
+                       &uaudio_stream_resp_desc, &resp, sizeof(resp));
+
+       svc->t_resp_sent = ktime_get();
+
+       pr_debug("%s: t_resp sent - t_req recvd (in ms) %lld\n", __func__,
+               ktime_to_ms(ktime_sub(svc->t_resp_sent, svc->t_request_recvd)));
+
+       return ret;
+}
+
+static int uaudio_qmi_svc_connect_cb(struct qmi_handle *handle,
+                              void *conn_h)
+{
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+
+       if (svc->uaudio_svc_hdl != handle || !conn_h) {
+               pr_err("%s: handle mismatch\n", __func__);
+               return -EINVAL;
+       }
+       if (svc->curr_conn) {
+               pr_err("%s: Service is busy\n", __func__);
+               return -ECONNREFUSED;
+       }
+       svc->curr_conn = conn_h;
+       return 0;
+}
+
+static int uaudio_qmi_svc_disconnect_cb(struct qmi_handle *handle,
+                                 void *conn_h)
+{
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+
+       if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) {
+               pr_err("%s: handle mismatch\n", __func__);
+               return -EINVAL;
+       }
+
+       svc->curr_conn = NULL;
+       return 0;
+}
+
+static int uaudio_qmi_svc_req_cb(struct qmi_handle *handle, void *conn_h,
+                       void *req_h, unsigned int msg_id, void *req)
+{
+       int ret;
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+
+       if (svc->uaudio_svc_hdl != handle || svc->curr_conn != conn_h) {
+               pr_err("%s: handle mismatch\n", __func__);
+               return -EINVAL;
+       }
+
+       switch (msg_id) {
+       case QMI_UAUDIO_STREAM_REQ_V01:
+               ret = handle_uaudio_stream_req(req_h, req);
+               break;
+
+       default:
+               ret = -ENOTSUPP;
+               break;
+       }
+       return ret;
+}
+
+static int uaudio_qmi_svc_req_desc_cb(unsigned int msg_id,
+       struct msg_desc **req_desc)
+{
+       int ret;
+
+       pr_debug("%s: msg_id %d\n", __func__, msg_id);
+
+       switch (msg_id) {
+       case QMI_UAUDIO_STREAM_REQ_V01:
+               *req_desc = &uaudio_stream_req_desc;
+               ret = sizeof(struct qmi_uaudio_stream_req_msg_v01);
+               break;
+
+       default:
+               ret = -ENOTSUPP;
+               break;
+       }
+       return ret;
+}
+
+static void uaudio_qmi_svc_recv_msg(struct work_struct *w)
+{
+       int ret;
+       struct uaudio_qmi_svc *svc = container_of(w, struct uaudio_qmi_svc,
+               recv_msg_work);
+
+       do {
+               pr_debug("%s: Notified about a Receive Event", __func__);
+       } while ((ret = qmi_recv_msg(svc->uaudio_svc_hdl)) == 0);
+
+       if (ret != -ENOMSG)
+               pr_err("%s: Error receiving message\n", __func__);
+}
+
+static void uaudio_qmi_svc_ntfy(struct qmi_handle *handle,
+               enum qmi_event_type event, void *priv)
+{
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+
+       pr_debug("%s: event %d", __func__, event);
+
+       svc->t_request_recvd = ktime_get();
+
+       switch (event) {
+       case QMI_RECV_MSG:
+               queue_work(svc->uaudio_wq, &svc->recv_msg_work);
+               break;
+       default:
+               break;
+       }
+}
+
+static struct qmi_svc_ops_options uaudio_svc_ops_options = {
+       .version = 1,
+       .service_id = UAUDIO_STREAM_SERVICE_ID_V01,
+       .service_vers = UAUDIO_STREAM_SERVICE_VERS_V01,
+       .connect_cb = uaudio_qmi_svc_connect_cb,
+       .disconnect_cb = uaudio_qmi_svc_disconnect_cb,
+       .req_desc_cb = uaudio_qmi_svc_req_desc_cb,
+       .req_cb = uaudio_qmi_svc_req_cb,
+};
+
+static int __init uaudio_qmi_svc_init(void)
+{
+       int ret;
+       struct uaudio_qmi_svc *svc;
+
+       svc = kzalloc(sizeof(struct uaudio_qmi_svc), GFP_KERNEL);
+       if (!svc)
+               return -ENOMEM;
+
+       svc->uaudio_wq = create_singlethread_workqueue("uaudio_svc");
+       if (!svc->uaudio_wq) {
+               ret = -ENOMEM;
+               goto free_svc;
+       }
+
+       svc->uaudio_svc_hdl = qmi_handle_create(uaudio_qmi_svc_ntfy, NULL);
+       if (!svc->uaudio_svc_hdl) {
+               pr_err("%s: Error creating svc_hdl\n", __func__);
+               ret = -EFAULT;
+               goto destroy_uaudio_wq;
+       }
+
+       ret = qmi_svc_register(svc->uaudio_svc_hdl, &uaudio_svc_ops_options);
+       if (ret < 0) {
+               pr_err("%s:Error registering uaudio svc %d\n", __func__, ret);
+               goto destroy_svc_handle;
+       }
+
+       INIT_WORK(&svc->recv_msg_work, uaudio_qmi_svc_recv_msg);
+
+       uaudio_svc = svc;
+
+       return 0;
+
+destroy_svc_handle:
+       qmi_handle_destroy(svc->uaudio_svc_hdl);
+destroy_uaudio_wq:
+       destroy_workqueue(svc->uaudio_wq);
+free_svc:
+       kfree(svc);
+       return ret;
+}
+
+static void __exit uaudio_qmi_svc_exit(void)
+{
+       struct uaudio_qmi_svc *svc = uaudio_svc;
+
+       qmi_svc_unregister(svc->uaudio_svc_hdl);
+       flush_workqueue(svc->uaudio_wq);
+       qmi_handle_destroy(svc->uaudio_svc_hdl);
+       destroy_workqueue(svc->uaudio_wq);
+       kfree(svc);
+       uaudio_svc = NULL;
+}
+
+module_init(uaudio_qmi_svc_init);
+module_exit(uaudio_qmi_svc_exit);
+
+MODULE_DESCRIPTION("USB AUDIO QMI Service Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
new file mode 100644 (file)
index 0000000..31b1ba7
--- /dev/null
@@ -0,0 +1,565 @@
+ /* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/qmi_encdec.h>
+
+#include <soc/qcom/msm_qmi_interface.h>
+
+#include "usb_audio_qmi_v01.h"
+
+static struct elem_info mem_info_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_8_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint64_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct mem_info_v01,
+                                          va),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_8_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint64_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct mem_info_v01,
+                                          pa),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct mem_info_v01,
+                                          size),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+static struct elem_info apps_mem_info_v01_ei[] = {
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct apps_mem_info_v01,
+                                          evt_ring),
+               .ei_array      = mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct apps_mem_info_v01,
+                                          tr_data),
+               .ei_array      = mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct apps_mem_info_v01,
+                                          tr_sync),
+               .ei_array      = mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct apps_mem_info_v01,
+                                          xfer_buff),
+               .ei_array      = mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct apps_mem_info_v01,
+                                          dcba),
+               .ei_array      = mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+static struct elem_info usb_endpoint_descriptor_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bLength),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bDescriptorType),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bEndpointAddress),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bmAttributes),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_2_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint16_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          wMaxPacketSize),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bInterval),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bRefresh),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_endpoint_descriptor_v01,
+                                          bSynchAddress),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+static struct elem_info usb_interface_descriptor_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bLength),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bDescriptorType),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bInterfaceNumber),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bAlternateSetting),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bNumEndpoints),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bInterfaceClass),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bInterfaceSubClass),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          bInterfaceProtocol),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0,
+               .offset         = offsetof(struct usb_interface_descriptor_v01,
+                                          iInterface),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+struct elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x01,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          priv_data),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x02,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          enable),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x03,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          usb_token),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x04,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          audio_format),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x05,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          number_of_ch),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x06,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          bit_rate),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x07,
+               .offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
+                                          xfer_buff_size),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x01,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       priv_data),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x02,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       status),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x03,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       slot_id),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x04,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       bSubslotSize),
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_interface_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x05,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       std_as_opr_intf_desc),
+               .ei_array      = usb_interface_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_endpoint_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x06,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       std_as_data_ep_desc),
+               .ei_array      = usb_endpoint_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_endpoint_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x07,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       std_as_sync_ep_desc),
+               .ei_array      = usb_endpoint_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x08,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       bDelay),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_2_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint16_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x09,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       bcdADC),
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct apps_mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x0A,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       xhci_mem_info),
+               .ei_array      = apps_mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x0B,
+               .offset         = offsetof(
+                                       struct qmi_uaudio_stream_resp_msg_v01,
+                                       interrupter_num),
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
+
+struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x01,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          usb_token),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x02,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          priv_data),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x03,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          status),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_4_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint32_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x04,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          slot_id),
+       },
+       {
+               .data_type      = QMI_UNSIGNED_1_BYTE,
+               .elem_len       = 1,
+               .elem_size      = sizeof(uint8_t),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x05,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          bSubslotSize),
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_interface_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x06,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          std_as_opr_intf_desc),
+               .ei_array      = usb_interface_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_endpoint_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x07,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          std_as_data_ep_desc),
+               .ei_array      = usb_endpoint_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct usb_endpoint_descriptor_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x08,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          std_as_sync_ep_desc),
+               .ei_array      = usb_endpoint_descriptor_v01_ei,
+       },
+       {
+               .data_type      = QMI_STRUCT,
+               .elem_len       = 1,
+               .elem_size      = sizeof(struct apps_mem_info_v01),
+               .is_array       = NO_ARRAY,
+               .tlv_type       = 0x09,
+               .offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+                                          xhci_mem_info),
+               .ei_array      = apps_mem_info_v01_ei,
+       },
+       {
+               .data_type      = QMI_EOTI,
+               .is_array       = NO_ARRAY,
+               .is_array       = QMI_COMMON_TLV_TYPE,
+       },
+};
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
new file mode 100644 (file)
index 0000000..7ad1ab8
--- /dev/null
@@ -0,0 +1,103 @@
+ /* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef USB_QMI_V01_H
+#define USB_QMI_V01_H
+
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41C
+#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
+
+#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
+#define QMI_UAUDIO_STREAM_REQ_V01 0x0001
+#define QMI_UADUIO_STREAM_IND_V01 0x0001
+
+
+struct mem_info_v01 {
+       uint64_t va;
+       uint64_t pa;
+       uint32_t size;
+};
+
+struct apps_mem_info_v01 {
+       struct mem_info_v01 evt_ring;
+       struct mem_info_v01 tr_data;
+       struct mem_info_v01 tr_sync;
+       struct mem_info_v01 xfer_buff;
+       struct mem_info_v01 dcba;
+};
+
+struct usb_endpoint_descriptor_v01 {
+       uint8_t bLength;
+       uint8_t bDescriptorType;
+       uint8_t bEndpointAddress;
+       uint8_t bmAttributes;
+       uint16_t wMaxPacketSize;
+       uint8_t bInterval;
+       uint8_t bRefresh;
+       uint8_t bSynchAddress;
+};
+
+struct usb_interface_descriptor_v01 {
+       uint8_t bLength;
+       uint8_t bDescriptorType;
+       uint8_t bInterfaceNumber;
+       uint8_t bAlternateSetting;
+       uint8_t bNumEndpoints;
+       uint8_t bInterfaceClass;
+       uint8_t bInterfaceSubClass;
+       uint8_t bInterfaceProtocol;
+       uint8_t iInterface;
+};
+
+struct qmi_uaudio_stream_req_msg_v01 {
+       uint32_t priv_data;
+       uint8_t enable;
+       uint32_t usb_token;
+       uint32_t audio_format;
+       uint32_t number_of_ch;
+       uint32_t bit_rate;
+       uint32_t xfer_buff_size;
+};
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
+extern struct elem_info qmi_uaudio_stream_req_msg_v01_ei[];
+
+struct qmi_uaudio_stream_resp_msg_v01 {
+       uint32_t priv_data;
+       uint32_t status;
+       uint32_t slot_id;
+       uint8_t bSubslotSize;
+       struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+       struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+       struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+       uint8_t bDelay;
+       uint16_t bcdADC;
+       struct apps_mem_info_v01 xhci_mem_info;
+       uint8_t interrupter_num;
+};
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 177
+extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
+
+struct qmi_uaudio_stream_ind_msg_v01 {
+       uint32_t usb_token;
+       uint32_t priv_data;
+       uint32_t status;
+       uint32_t slot_id;
+       uint8_t bSubslotSize;
+       struct usb_interface_descriptor_v01 std_as_opr_intf_desc;
+       struct usb_endpoint_descriptor_v01 std_as_data_ep_desc;
+       struct usb_endpoint_descriptor_v01 std_as_sync_ep_desc;
+       struct apps_mem_info_v01 xhci_mem_info;
+};
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 171
+extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
+
+#endif
index 207ddb2..b776122 100644 (file)
@@ -60,6 +60,8 @@ struct snd_usb_audio {
        bool autoclock;                 /* from the 'autoclock' module param */
 
        struct usb_host_interface *ctrl_intf;   /* the audio control interface */
+
+       struct mutex dev_lock;  /* to protect any race with disconnect */
        int card_num;   /* cache pcm card number to use upon disconnect */
        void (*disconnect_cb)(struct snd_usb_audio *chip);
 };