Loading sound/usb/pcm.c +68 −2 Original line number Original line Diff line number Diff line Loading @@ -150,6 +150,69 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) return found; return found; } } /* * find a matching audio format as well as non-zero service interval */ static struct audioformat *find_format_and_si(struct snd_usb_substream *subs, unsigned int datainterval) { unsigned int i; struct audioformat *fp; struct audioformat *found = NULL; int cur_attr = 0, attr; list_for_each_entry(fp, &subs->fmt_list, list) { if (datainterval != fp->datainterval) continue; if (!(fp->formats & pcm_format_to_bits(subs->pcm_format))) continue; if (fp->channels != subs->channels) continue; if (subs->cur_rate < fp->rate_min || subs->cur_rate > fp->rate_max) continue; if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { for (i = 0; i < fp->nr_rates; i++) if (fp->rate_table[i] == subs->cur_rate) break; if (i >= fp->nr_rates) continue; } attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; if (!found) { found = fp; cur_attr = attr; continue; } /* avoid async out and adaptive in if the other method * supports the same format. * this is a workaround for the case like * M-audio audiophile USB. */ if (attr != cur_attr) { if ((attr == USB_ENDPOINT_SYNC_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (attr == USB_ENDPOINT_SYNC_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) continue; if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) { found = fp; cur_attr = attr; continue; } } /* find the format with the largest max. packet size */ if (fp->maxpacksize > found->maxpacksize) { found = fp; cur_attr = attr; } } return found; } static int init_pitch_v1(struct snd_usb_audio *chip, int iface, static int init_pitch_v1(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct usb_host_interface *alts, struct audioformat *fmt) struct audioformat *fmt) Loading Loading @@ -574,7 +637,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } } int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, bool enable) int datainterval, bool enable) { { struct audioformat *fmt; struct audioformat *fmt; struct usb_host_interface *alts; struct usb_host_interface *alts; Loading @@ -594,6 +657,9 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, } } snd_usb_autoresume(subs->stream->chip); snd_usb_autoresume(subs->stream->chip); if (datainterval != -EINVAL) fmt = find_format_and_si(subs, datainterval); else fmt = find_format(subs); fmt = find_format(subs); if (!fmt) { if (!fmt) { dev_err(&subs->dev->dev, dev_err(&subs->dev->dev, Loading sound/usb/pcm.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -11,6 +11,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct usb_host_interface *alts, struct audioformat *fmt); struct audioformat *fmt); int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, bool enable); int datainterval, bool enable); #endif /* __USBAUDIO_PCM_H */ #endif /* __USBAUDIO_PCM_H */ sound/usb/usb_audio_qmi_svc.c +47 −3 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,10 @@ #include "pcm.h" #include "pcm.h" #include "usb_audio_qmi_v01.h" #include "usb_audio_qmi_v01.h" #define BUS_INTERVAL_FULL_SPEED 1000 /* in us */ #define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */ #define MAX_BINTERVAL_ISOC_EP 16 #define SND_PCM_CARD_NUM_MASK 0xffff0000 #define SND_PCM_CARD_NUM_MASK 0xffff0000 #define SND_PCM_DEV_NUM_MASK 0xff00 #define SND_PCM_DEV_NUM_MASK 0xff00 #define SND_PCM_STREAM_DIRECTION 0xff #define SND_PCM_STREAM_DIRECTION 0xff Loading Loading @@ -948,6 +952,32 @@ static int info_idx_from_ifnum(int card_num, int intf_num, bool enable) return -EINVAL; return -EINVAL; } } static int get_data_interval_from_si(struct snd_usb_substream *subs, u32 service_interval) { unsigned int bus_intval, bus_intval_mult, binterval; if (subs->dev->speed >= USB_SPEED_HIGH) bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE; else bus_intval = BUS_INTERVAL_FULL_SPEED; if (service_interval % bus_intval) return -EINVAL; bus_intval_mult = service_interval / bus_intval; binterval = ffs(bus_intval_mult); if (!binterval || binterval > MAX_BINTERVAL_ISOC_EP) return -EINVAL; /* check if another bit is set then bail out */ bus_intval_mult = bus_intval_mult >> binterval; if (bus_intval_mult) return -EINVAL; return (binterval - 1); } static void handle_uaudio_stream_req(struct qmi_handle *handle, static void handle_uaudio_stream_req(struct qmi_handle *handle, struct sockaddr_qrtr *sq, struct sockaddr_qrtr *sq, struct qmi_txn *txn, struct qmi_txn *txn, Loading @@ -961,7 +991,7 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, struct intf_info *info; struct intf_info *info; int pcm_format; int pcm_format; u8 pcm_card_num, pcm_dev_num, direction; u8 pcm_card_num, pcm_dev_num, direction; int info_idx = -EINVAL, ret = 0; int info_idx = -EINVAL, datainterval = -EINVAL, ret = 0; req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg; req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg; Loading Loading @@ -1028,9 +1058,23 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->pcm_format = pcm_format; subs->pcm_format = pcm_format; subs->channels = req_msg->number_of_ch; subs->channels = req_msg->number_of_ch; subs->cur_rate = req_msg->bit_rate; subs->cur_rate = req_msg->bit_rate; if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); if (ret == -EINVAL) { pr_err("%s: invalid service interval %u\n", __func__, req_msg->service_interval); mutex_unlock(&chip->dev_lock); goto response; } datainterval = ret; pr_debug("%s: data interval %u\n", __func__, ret); } uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf; uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf; ret = snd_usb_enable_audio_stream(subs, req_msg->enable); ret = snd_usb_enable_audio_stream(subs, datainterval, req_msg->enable); if (!ret && req_msg->enable) if (!ret && req_msg->enable) ret = prepare_qmi_response(subs, req_msg, &resp, info_idx); ret = prepare_qmi_response(subs, req_msg, &resp, info_idx); Loading Loading @@ -1097,7 +1141,7 @@ static void uaudio_qmi_disconnect_work(struct work_struct *w) info->direction); info->direction); continue; continue; } } snd_usb_enable_audio_stream(subs, 0); snd_usb_enable_audio_stream(subs, -EINVAL, 0); } } atomic_set(&uadev[idx].in_use, 0); atomic_set(&uadev[idx].in_use, 0); mutex_lock(&chip->dev_lock); mutex_lock(&chip->dev_lock); Loading sound/usb/usb_audio_qmi_v01.c +18 −0 Original line number Original line Diff line number Diff line Loading @@ -367,6 +367,24 @@ struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = { .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, xfer_buff_size), xfer_buff_size), }, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, .tlv_type = 0x14, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, service_interval_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, .tlv_type = 0x14, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, service_interval), }, { { .data_type = QMI_EOTI, .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = NO_ARRAY, Loading sound/usb/usb_audio_qmi_v01.h +3 −1 Original line number Original line Diff line number Diff line Loading @@ -99,8 +99,10 @@ struct qmi_uaudio_stream_req_msg_v01 { uint32_t bit_rate; uint32_t bit_rate; uint8_t xfer_buff_size_valid; uint8_t xfer_buff_size_valid; uint32_t xfer_buff_size; uint32_t xfer_buff_size; uint8_t service_interval_valid; uint32_t service_interval; }; }; #define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39 #define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46 extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[]; extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[]; struct qmi_uaudio_stream_resp_msg_v01 { struct qmi_uaudio_stream_resp_msg_v01 { Loading Loading
sound/usb/pcm.c +68 −2 Original line number Original line Diff line number Diff line Loading @@ -150,6 +150,69 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) return found; return found; } } /* * find a matching audio format as well as non-zero service interval */ static struct audioformat *find_format_and_si(struct snd_usb_substream *subs, unsigned int datainterval) { unsigned int i; struct audioformat *fp; struct audioformat *found = NULL; int cur_attr = 0, attr; list_for_each_entry(fp, &subs->fmt_list, list) { if (datainterval != fp->datainterval) continue; if (!(fp->formats & pcm_format_to_bits(subs->pcm_format))) continue; if (fp->channels != subs->channels) continue; if (subs->cur_rate < fp->rate_min || subs->cur_rate > fp->rate_max) continue; if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { for (i = 0; i < fp->nr_rates; i++) if (fp->rate_table[i] == subs->cur_rate) break; if (i >= fp->nr_rates) continue; } attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; if (!found) { found = fp; cur_attr = attr; continue; } /* avoid async out and adaptive in if the other method * supports the same format. * this is a workaround for the case like * M-audio audiophile USB. */ if (attr != cur_attr) { if ((attr == USB_ENDPOINT_SYNC_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (attr == USB_ENDPOINT_SYNC_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) continue; if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE && subs->direction == SNDRV_PCM_STREAM_CAPTURE)) { found = fp; cur_attr = attr; continue; } } /* find the format with the largest max. packet size */ if (fp->maxpacksize > found->maxpacksize) { found = fp; cur_attr = attr; } } return found; } static int init_pitch_v1(struct snd_usb_audio *chip, int iface, static int init_pitch_v1(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct usb_host_interface *alts, struct audioformat *fmt) struct audioformat *fmt) Loading Loading @@ -574,7 +637,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } } int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, bool enable) int datainterval, bool enable) { { struct audioformat *fmt; struct audioformat *fmt; struct usb_host_interface *alts; struct usb_host_interface *alts; Loading @@ -594,6 +657,9 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, } } snd_usb_autoresume(subs->stream->chip); snd_usb_autoresume(subs->stream->chip); if (datainterval != -EINVAL) fmt = find_format_and_si(subs, datainterval); else fmt = find_format(subs); fmt = find_format(subs); if (!fmt) { if (!fmt) { dev_err(&subs->dev->dev, dev_err(&subs->dev->dev, Loading
sound/usb/pcm.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -11,6 +11,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct usb_host_interface *alts, struct audioformat *fmt); struct audioformat *fmt); int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, bool enable); int datainterval, bool enable); #endif /* __USBAUDIO_PCM_H */ #endif /* __USBAUDIO_PCM_H */
sound/usb/usb_audio_qmi_svc.c +47 −3 Original line number Original line Diff line number Diff line Loading @@ -35,6 +35,10 @@ #include "pcm.h" #include "pcm.h" #include "usb_audio_qmi_v01.h" #include "usb_audio_qmi_v01.h" #define BUS_INTERVAL_FULL_SPEED 1000 /* in us */ #define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */ #define MAX_BINTERVAL_ISOC_EP 16 #define SND_PCM_CARD_NUM_MASK 0xffff0000 #define SND_PCM_CARD_NUM_MASK 0xffff0000 #define SND_PCM_DEV_NUM_MASK 0xff00 #define SND_PCM_DEV_NUM_MASK 0xff00 #define SND_PCM_STREAM_DIRECTION 0xff #define SND_PCM_STREAM_DIRECTION 0xff Loading Loading @@ -948,6 +952,32 @@ static int info_idx_from_ifnum(int card_num, int intf_num, bool enable) return -EINVAL; return -EINVAL; } } static int get_data_interval_from_si(struct snd_usb_substream *subs, u32 service_interval) { unsigned int bus_intval, bus_intval_mult, binterval; if (subs->dev->speed >= USB_SPEED_HIGH) bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE; else bus_intval = BUS_INTERVAL_FULL_SPEED; if (service_interval % bus_intval) return -EINVAL; bus_intval_mult = service_interval / bus_intval; binterval = ffs(bus_intval_mult); if (!binterval || binterval > MAX_BINTERVAL_ISOC_EP) return -EINVAL; /* check if another bit is set then bail out */ bus_intval_mult = bus_intval_mult >> binterval; if (bus_intval_mult) return -EINVAL; return (binterval - 1); } static void handle_uaudio_stream_req(struct qmi_handle *handle, static void handle_uaudio_stream_req(struct qmi_handle *handle, struct sockaddr_qrtr *sq, struct sockaddr_qrtr *sq, struct qmi_txn *txn, struct qmi_txn *txn, Loading @@ -961,7 +991,7 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, struct intf_info *info; struct intf_info *info; int pcm_format; int pcm_format; u8 pcm_card_num, pcm_dev_num, direction; u8 pcm_card_num, pcm_dev_num, direction; int info_idx = -EINVAL, ret = 0; int info_idx = -EINVAL, datainterval = -EINVAL, ret = 0; req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg; req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg; Loading Loading @@ -1028,9 +1058,23 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->pcm_format = pcm_format; subs->pcm_format = pcm_format; subs->channels = req_msg->number_of_ch; subs->channels = req_msg->number_of_ch; subs->cur_rate = req_msg->bit_rate; subs->cur_rate = req_msg->bit_rate; if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); if (ret == -EINVAL) { pr_err("%s: invalid service interval %u\n", __func__, req_msg->service_interval); mutex_unlock(&chip->dev_lock); goto response; } datainterval = ret; pr_debug("%s: data interval %u\n", __func__, ret); } uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf; uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf; ret = snd_usb_enable_audio_stream(subs, req_msg->enable); ret = snd_usb_enable_audio_stream(subs, datainterval, req_msg->enable); if (!ret && req_msg->enable) if (!ret && req_msg->enable) ret = prepare_qmi_response(subs, req_msg, &resp, info_idx); ret = prepare_qmi_response(subs, req_msg, &resp, info_idx); Loading Loading @@ -1097,7 +1141,7 @@ static void uaudio_qmi_disconnect_work(struct work_struct *w) info->direction); info->direction); continue; continue; } } snd_usb_enable_audio_stream(subs, 0); snd_usb_enable_audio_stream(subs, -EINVAL, 0); } } atomic_set(&uadev[idx].in_use, 0); atomic_set(&uadev[idx].in_use, 0); mutex_lock(&chip->dev_lock); mutex_lock(&chip->dev_lock); Loading
sound/usb/usb_audio_qmi_v01.c +18 −0 Original line number Original line Diff line number Diff line Loading @@ -367,6 +367,24 @@ struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = { .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, xfer_buff_size), xfer_buff_size), }, }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .is_array = NO_ARRAY, .tlv_type = 0x14, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, service_interval_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, .elem_size = sizeof(uint32_t), .is_array = NO_ARRAY, .tlv_type = 0x14, .offset = offsetof(struct qmi_uaudio_stream_req_msg_v01, service_interval), }, { { .data_type = QMI_EOTI, .data_type = QMI_EOTI, .is_array = NO_ARRAY, .is_array = NO_ARRAY, Loading
sound/usb/usb_audio_qmi_v01.h +3 −1 Original line number Original line Diff line number Diff line Loading @@ -99,8 +99,10 @@ struct qmi_uaudio_stream_req_msg_v01 { uint32_t bit_rate; uint32_t bit_rate; uint8_t xfer_buff_size_valid; uint8_t xfer_buff_size_valid; uint32_t xfer_buff_size; uint32_t xfer_buff_size; uint8_t service_interval_valid; uint32_t service_interval; }; }; #define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39 #define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46 extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[]; extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[]; struct qmi_uaudio_stream_resp_msg_v01 { struct qmi_uaudio_stream_resp_msg_v01 { Loading