content: /*
   * Copyright (C) 2017 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.android.server.usb.descriptors;
  
  import android.util.Log;
  
  import com.android.server.usb.descriptors.report.ReportCanvas;
  import com.android.server.usb.descriptors.report.UsbStrings;
  
  /**
   * @hide
   * An audio class-specific Interface.
   * see audio10.pdf section 4.3.2
   */
  public abstract class UsbACInterface extends UsbDescriptor {
      private static final String TAG = "UsbACInterface";
  
      // Audio Control Subtypes
      public static final byte ACI_UNDEFINED = 0;
      public static final byte ACI_HEADER = 1;
      public static final byte ACI_INPUT_TERMINAL = 2;
      public static final byte ACI_OUTPUT_TERMINAL = 3;
      public static final byte ACI_MIXER_UNIT = 4;
      public static final byte ACI_SELECTOR_UNIT = 5;
      public static final byte ACI_FEATURE_UNIT = 6;
      public static final byte ACI_PROCESSING_UNIT = 7;
      public static final byte ACI_EXTENSION_UNIT = 8;
      // Not handled yet
      public static final byte ACI_CLOCK_SOURCE = 0x0A;
      public static final byte ACI_CLOCK_SELECTOR = 0x0B;
      public static final byte ACI_CLOCK_MULTIPLIER = 0x0C;
      public static final byte ACI_SAMPLE_RATE_CONVERTER =  0x0D;
  
      // Audio Streaming Subtypes
      public static final byte ASI_UNDEFINED = 0;
      public static final byte ASI_GENERAL = 1;
      public static final byte ASI_FORMAT_TYPE = 2;
      public static final byte ASI_FORMAT_SPECIFIC = 3;
  
      // MIDI Streaming Subtypes
      public static final byte MSI_UNDEFINED = 0;
      public static final byte MSI_HEADER = 1;
      public static final byte MSI_IN_JACK = 2;
      public static final byte MSI_OUT_JACK = 3;
      public static final byte MSI_ELEMENT = 4;
  
      // Sample format IDs (encodings)
      // FORMAT_I
      public static final int FORMAT_I_UNDEFINED     = 0x0000;
      public static final int FORMAT_I_PCM           = 0x0001;
      public static final int FORMAT_I_PCM8          = 0x0002;
      public static final int FORMAT_I_IEEE_FLOAT    = 0x0003;
      public static final int FORMAT_I_ALAW          = 0x0004;
      public static final int FORMAT_I_MULAW         = 0x0005;
      // FORMAT_II
      public static final int FORMAT_II_UNDEFINED    = 0x1000;
      public static final int FORMAT_II_MPEG         = 0x1001;
      public static final int FORMAT_II_AC3          = 0x1002;
      // FORMAT_III
      public static final int FORMAT_III_UNDEFINED              = 0x2000;
      public static final int FORMAT_III_IEC1937AC3             = 0x2001;
      public static final int FORMAT_III_IEC1937_MPEG1_Layer1   = 0x2002;
      public static final int FORMAT_III_IEC1937_MPEG1_Layer2   = 0x2003;
      public static final int FORMAT_III_IEC1937_MPEG2_EXT      = 0x2004;
      public static final int FORMAT_III_IEC1937_MPEG2_Layer1LS = 0x2005;
  
      protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
      protected final int mSubclass;  // from the mSubclass member of the
                                      // "enclosing" Interface Descriptor
  
      public UsbACInterface(int length, byte type, byte subtype, int subclass) {
          super(length, type);
          mSubtype = subtype;
          mSubclass = subclass;
      }
  
      public byte getSubtype() {
          return mSubtype;
      }
  
      public int getSubclass() {
          return mSubclass;
      }
  
      private static UsbDescriptor allocAudioControlDescriptor(UsbDescriptorParser parser,
              ByteStream stream, int length, byte type, byte subtype, int subClass) {
          switch (subtype) {
              case ACI_HEADER:
              {
                  int acInterfaceSpec = stream.unpackUsbShort();
                  parser.setACInterfaceSpec(acInterfaceSpec);
                  if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                      return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                  } else {
                      return new Usb10ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                  }
              }
  
              case ACI_INPUT_TERMINAL:
              {
                  int acInterfaceSpec = parser.getACInterfaceSpec();
                  if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                      return new Usb20ACInputTerminal(length, type, subtype, subClass);
                  } else {
                      return new Usb10ACInputTerminal(length, type, subtype, subClass);
                  }
              }
  
              case ACI_OUTPUT_TERMINAL:
              {
                  int acInterfaceSpec = parser.getACInterfaceSpec();
                  if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                      return new Usb20ACOutputTerminal(length, type, subtype, subClass);
                  } else {
                      return new Usb10ACOutputTerminal(length, type, subtype, subClass);
                  }
              }
  
              case ACI_SELECTOR_UNIT:
                  return new UsbACSelectorUnit(length, type, subtype, subClass);
  
              case ACI_FEATURE_UNIT:
                  return new UsbACFeatureUnit(length, type, subtype, subClass);
  
              case ACI_MIXER_UNIT:
              {
                  int acInterfaceSpec = parser.getACInterfaceSpec();
                  if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                      return new Usb20ACMixerUnit(length, type, subtype, subClass);
                  } else {
                      return new Usb10ACMixerUnit(length, type, subtype, subClass);
                  }
              }
  
              case ACI_PROCESSING_UNIT:
              case ACI_EXTENSION_UNIT:
              case ACI_UNDEFINED:
                  // break; Fall through until we implement this descriptor
              default:
                  Log.w(TAG, "Unknown Audio Class Interface subtype:0x"
                          + Integer.toHexString(subtype));
                  return new UsbACInterfaceUnparsed(length, type, subtype, subClass);
          }
      }
  
      private static UsbDescriptor allocAudioStreamingDescriptor(UsbDescriptorParser parser,
              ByteStream stream, int length, byte type, byte subtype, int subClass) {
          //int spec = parser.getUsbSpec();
          int acInterfaceSpec = parser.getACInterfaceSpec();
          switch (subtype) {
              case ASI_GENERAL:
                  if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                      return new Usb20ASGeneral(length, type, subtype, subClass);
                  } else {
                      return new Usb10ASGeneral(length, type, subtype, subClass);
                  }
  
              case ASI_FORMAT_TYPE:
                  return UsbASFormat.allocDescriptor(parser, stream, length, type, subtype, subClass);
  
              case ASI_FORMAT_SPECIFIC:
              case ASI_UNDEFINED:
                  // break; Fall through until we implement this descriptor
              default:
                  Log.w(TAG, "Unknown Audio Streaming Interface subtype:0x"
                          + Integer.toHexString(subtype));
                  return null;
          }
      }
  
      private static UsbDescriptor allocMidiStreamingDescriptor(int length, byte type,
              byte subtype, int subClass) {
          switch (subtype) {
              case MSI_HEADER:
                  return new UsbMSMidiHeader(length, type, subtype, subClass);
  
              case MSI_IN_JACK:
                  return new UsbMSMidiInputJack(length, type, subtype, subClass);
  
              case MSI_OUT_JACK:
                  return new UsbMSMidiOutputJack(length, type, subtype, subClass);
  
              case MSI_ELEMENT:
                  // break;
                  // Fall through until we implement that descriptor
  
              case MSI_UNDEFINED:
              default:
                  Log.w(TAG, "Unknown MIDI Streaming Interface subtype:0x"
                          + Integer.toHexString(subtype));
                  return null;
          }
      }
  
      /**
       * Allocates an audio class interface subtype based on subtype and subclass.
       */
      public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
              int length, byte type) {
          byte subtype = stream.getByte();
          UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
          int subClass = interfaceDesc.getUsbSubclass();
          switch (subClass) {
              case AUDIO_AUDIOCONTROL:
                  return allocAudioControlDescriptor(
                          parser, stream, length, type, subtype, subClass);
  
              case AUDIO_AUDIOSTREAMING:
                  return allocAudioStreamingDescriptor(
                          parser, stream, length, type, subtype, subClass);
  
              case AUDIO_MIDISTREAMING:
                  return allocMidiStreamingDescriptor(length, type, subtype, subClass);
  
              default:
                  Log.w(TAG, "Unknown Audio Class Interface Subclass: 0x"
                          + Integer.toHexString(subClass));
                  return null;
          }
      }
  
      @Override
      public void report(ReportCanvas canvas) {
          super.report(canvas);
  
          int subClass = getSubclass();
          String subClassName = UsbStrings.getACInterfaceSubclassName(subClass);
  
          byte subtype = getSubtype();
          String subTypeName = UsbStrings.getACControlInterfaceName(subtype);
  
          canvas.openList();
          canvas.writeListItem("Subclass: " + ReportCanvas.getHexString(subClass)
                  + " " + subClassName);
          canvas.writeListItem("Subtype: " + ReportCanvas.getHexString(subtype) + " " + subTypeName);
          canvas.closeList();
      }
  }
  