linux/sound/usb/helper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 */
   4
   5#include <linux/init.h>
   6#include <linux/slab.h>
   7#include <linux/usb.h>
   8
   9#include "usbaudio.h"
  10#include "helper.h"
  11#include "quirks.h"
  12
  13/*
  14 * combine bytes and get an integer value
  15 */
  16unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
  17{
  18        switch (size) {
  19        case 1:  return *bytes;
  20        case 2:  return combine_word(bytes);
  21        case 3:  return combine_triple(bytes);
  22        case 4:  return combine_quad(bytes);
  23        default: return 0;
  24        }
  25}
  26
  27/*
  28 * parse descriptor buffer and return the pointer starting the given
  29 * descriptor type.
  30 */
  31void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
  32{
  33        u8 *p, *end, *next;
  34
  35        p = descstart;
  36        end = p + desclen;
  37        for (; p < end;) {
  38                if (p[0] < 2)
  39                        return NULL;
  40                next = p + p[0];
  41                if (next > end)
  42                        return NULL;
  43                if (p[1] == dtype && (!after || (void *)p > after)) {
  44                        return p;
  45                }
  46                p = next;
  47        }
  48        return NULL;
  49}
  50
  51/*
  52 * find a class-specified interface descriptor with the given subtype.
  53 */
  54void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
  55{
  56        unsigned char *p = after;
  57
  58        while ((p = snd_usb_find_desc(buffer, buflen, p,
  59                                      USB_DT_CS_INTERFACE)) != NULL) {
  60                if (p[0] >= 3 && p[2] == dsubtype)
  61                        return p;
  62        }
  63        return NULL;
  64}
  65
  66/* check the validity of pipe and EP types */
  67int snd_usb_pipe_sanity_check(struct usb_device *dev, unsigned int pipe)
  68{
  69        static const int pipetypes[4] = {
  70                PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
  71        };
  72        struct usb_host_endpoint *ep;
  73
  74        ep = usb_pipe_endpoint(dev, pipe);
  75        if (!ep || usb_pipetype(pipe) != pipetypes[usb_endpoint_type(&ep->desc)])
  76                return -EINVAL;
  77        return 0;
  78}
  79
  80/*
  81 * Wrapper for usb_control_msg().
  82 * Allocates a temp buffer to prevent dmaing from/to the stack.
  83 */
  84int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
  85                    __u8 requesttype, __u16 value, __u16 index, void *data,
  86                    __u16 size)
  87{
  88        int err;
  89        void *buf = NULL;
  90        int timeout;
  91
  92        if (snd_usb_pipe_sanity_check(dev, pipe))
  93                return -EINVAL;
  94
  95        if (size > 0) {
  96                buf = kmemdup(data, size, GFP_KERNEL);
  97                if (!buf)
  98                        return -ENOMEM;
  99        }
 100
 101        if (requesttype & USB_DIR_IN)
 102                timeout = USB_CTRL_GET_TIMEOUT;
 103        else
 104                timeout = USB_CTRL_SET_TIMEOUT;
 105
 106        err = usb_control_msg(dev, pipe, request, requesttype,
 107                              value, index, buf, size, timeout);
 108
 109        if (size > 0) {
 110                memcpy(data, buf, size);
 111                kfree(buf);
 112        }
 113
 114        snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
 115                              value, index, data, size);
 116
 117        return err;
 118}
 119
 120unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
 121                                         struct usb_host_interface *alts)
 122{
 123        switch (snd_usb_get_speed(chip->dev)) {
 124        case USB_SPEED_HIGH:
 125        case USB_SPEED_WIRELESS:
 126        case USB_SPEED_SUPER:
 127        case USB_SPEED_SUPER_PLUS:
 128                if (get_endpoint(alts, 0)->bInterval >= 1 &&
 129                    get_endpoint(alts, 0)->bInterval <= 4)
 130                        return get_endpoint(alts, 0)->bInterval - 1;
 131                break;
 132        default:
 133                break;
 134        }
 135        return 0;
 136}
 137
 138