162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/usb.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "usbaudio.h" 1062306a36Sopenharmony_ci#include "helper.h" 1162306a36Sopenharmony_ci#include "quirks.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * combine bytes and get an integer value 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ciunsigned int snd_usb_combine_bytes(unsigned char *bytes, int size) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci switch (size) { 1962306a36Sopenharmony_ci case 1: return *bytes; 2062306a36Sopenharmony_ci case 2: return combine_word(bytes); 2162306a36Sopenharmony_ci case 3: return combine_triple(bytes); 2262306a36Sopenharmony_ci case 4: return combine_quad(bytes); 2362306a36Sopenharmony_ci default: return 0; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * parse descriptor buffer and return the pointer starting the given 2962306a36Sopenharmony_ci * descriptor type. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_civoid *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci u8 *p, *end, *next; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci p = descstart; 3662306a36Sopenharmony_ci end = p + desclen; 3762306a36Sopenharmony_ci for (; p < end;) { 3862306a36Sopenharmony_ci if (p[0] < 2) 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci next = p + p[0]; 4162306a36Sopenharmony_ci if (next > end) 4262306a36Sopenharmony_ci return NULL; 4362306a36Sopenharmony_ci if (p[1] == dtype && (!after || (void *)p > after)) { 4462306a36Sopenharmony_ci return p; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci p = next; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci return NULL; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * find a class-specified interface descriptor with the given subtype. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_civoid *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci unsigned char *p = after; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci while ((p = snd_usb_find_desc(buffer, buflen, p, 5962306a36Sopenharmony_ci USB_DT_CS_INTERFACE)) != NULL) { 6062306a36Sopenharmony_ci if (p[0] >= 3 && p[2] == dsubtype) 6162306a36Sopenharmony_ci return p; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Wrapper for usb_control_msg(). 6862306a36Sopenharmony_ci * Allocates a temp buffer to prevent dmaing from/to the stack. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ciint snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, 7162306a36Sopenharmony_ci __u8 requesttype, __u16 value, __u16 index, void *data, 7262306a36Sopenharmony_ci __u16 size) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int err; 7562306a36Sopenharmony_ci void *buf = NULL; 7662306a36Sopenharmony_ci int timeout; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (usb_pipe_type_check(dev, pipe)) 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (size > 0) { 8262306a36Sopenharmony_ci buf = kmemdup(data, size, GFP_KERNEL); 8362306a36Sopenharmony_ci if (!buf) 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (requesttype & USB_DIR_IN) 8862306a36Sopenharmony_ci timeout = USB_CTRL_GET_TIMEOUT; 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci timeout = USB_CTRL_SET_TIMEOUT; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci err = usb_control_msg(dev, pipe, request, requesttype, 9362306a36Sopenharmony_ci value, index, buf, size, timeout); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (size > 0) { 9662306a36Sopenharmony_ci memcpy(data, buf, size); 9762306a36Sopenharmony_ci kfree(buf); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype, 10162306a36Sopenharmony_ci value, index, data, size); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return err; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciunsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, 10762306a36Sopenharmony_ci struct usb_host_interface *alts) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci switch (snd_usb_get_speed(chip->dev)) { 11062306a36Sopenharmony_ci case USB_SPEED_HIGH: 11162306a36Sopenharmony_ci case USB_SPEED_SUPER: 11262306a36Sopenharmony_ci case USB_SPEED_SUPER_PLUS: 11362306a36Sopenharmony_ci if (get_endpoint(alts, 0)->bInterval >= 1 && 11462306a36Sopenharmony_ci get_endpoint(alts, 0)->bInterval <= 4) 11562306a36Sopenharmony_ci return get_endpoint(alts, 0)->bInterval - 1; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct usb_host_interface * 12462306a36Sopenharmony_cisnd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct usb_interface *iface; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci iface = usb_ifnum_to_if(chip->dev, ifnum); 12962306a36Sopenharmony_ci if (!iface) 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci return usb_altnum_to_altsetting(iface, altsetting); 13262306a36Sopenharmony_ci} 133