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.hardware.usb.UsbEndpoint;
  import android.util.Log;
  
  import com.android.server.usb.descriptors.report.ReportCanvas;
  
  /**
   * @hide
   * A Usb Endpoint Descriptor.
   * see usb11.pdf section 9.6.4
   */
  public class UsbEndpointDescriptor extends UsbDescriptor {
      private static final String TAG = "UsbEndpointDescriptor";
      private static final boolean DEBUG = false;
  
      public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
      public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
      public static final int DIRECTION_OUTPUT = 0x0000;
      public static final int DIRECTION_INPUT = (byte) 0x0080;
  
      public static final int MASK_ATTRIBS_TRANSTYPE = 0b00000011;
      public static final int TRANSTYPE_CONTROL = 0x00;
      public static final int TRANSTYPE_ISO = 0x01;
      public static final int TRANSTYPE_BULK = 0x02;
      public static final int TRANSTYPE_INTERRUPT = 0x03;
  
      public static final byte MASK_ATTRIBS_SYNCTYPE = 0b00001100;
      public static final byte SYNCTYPE_NONE = 0b00000000;
      public static final byte SYNCTYPE_ASYNC = 0b00000100;
      public static final byte SYNCTYPE_ADAPTSYNC = 0b00001000;
      public static final byte SYNCTYPE_RESERVED = 0b00001100;
  
      public static final int MASK_ATTRIBS_USEAGE = 0b00110000;
      public static final int USEAGE_DATA = 0b00000000;
      public static final int USEAGE_FEEDBACK = 0b00010000;
      public static final int USEAGE_EXPLICIT = 0b00100000;
      public static final int USEAGE_RESERVED = 0b00110000;
  
      private int mEndpointAddress;   // 2:1 Endpoint Address
                                      // Bits 0..3b Endpoint Number.
                                      // Bits 4..6b Reserved. Set to Zero
                                      // Bits 7 Direction 0 = Out, 1 = In
                                      // (Ignored for Control Endpoints)
      private int mAttributes;    // 3:1 Various flags
                                  // Bits 0..1 Transfer Type:
                                  //     00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt
                                  // Bits 2..7 are reserved. If Isochronous endpoint,
                                  // Bits 3..2 = Synchronisation Type (Iso Mode)
                                  //  00 = No Synchonisation
                                  //  01 = Asynchronous
                                  //  10 = Adaptive
                                  //  11 = Synchronous
                                  // Bits 5..4 = Usage Type (Iso Mode)
                                  //  00: Data Endpoint
                                  //  01:Feedback Endpoint 10
                                  //  Explicit Feedback Data Endpoint
                                  //  11: Reserved
      private int mPacketSize;    // 4:2 Maximum Packet Size this endpoint is capable of
                                  // sending or receiving
      private int mInterval;      // 6:1 Interval for polling endpoint data transfers. Value in
                                  // frame counts.
                                  // Ignored for Bulk & Control Endpoints. Isochronous must equal
                                  // 1 and field may range from 1 to 255 for interrupt endpoints.
      private byte mRefresh;
      private byte mSyncAddress;
  
      public UsbEndpointDescriptor(int length, byte type) {
          super(length, type);
          mHierarchyLevel = 4;
      }
  
      public int getEndpointAddress() {
          return mEndpointAddress;
      }
  
      public int getAttributes() {
          return mAttributes;
      }
  
      public int getPacketSize() {
          return mPacketSize;
      }
  
      public int getInterval() {
          return mInterval;
      }
  
      public byte getRefresh() {
          return mRefresh;
      }
  
      public byte getSyncAddress() {
          return mSyncAddress;
      }
  
      /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
          if (DEBUG) {
              Log.d(TAG, "toAndroid() type:"
                      + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
                      + " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
                      + " usage:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_USEAGE));
          }
          return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
      }
  
      @Override
      public int parseRawDescriptors(ByteStream stream) {
          mEndpointAddress = stream.getUnsignedByte();
          mAttributes = stream.getUnsignedByte();
          mPacketSize = stream.unpackUsbShort();
          mInterval = stream.getUnsignedByte();
          if (mLength == 9) {
              mRefresh = stream.getByte();
              mSyncAddress = stream.getByte();
          }
          return mLength;
      }
  
      @Override
      public void report(ReportCanvas canvas) {
          super.report(canvas);
  
          canvas.openList();
  
          int address = getEndpointAddress();
          canvas.writeListItem("Address: "
                  + ReportCanvas.getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
                  + ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
                  == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
  
          int attributes = getAttributes();
          canvas.openListItem();
          canvas.write("Attributes: " + ReportCanvas.getHexString(attributes) + " ");
          switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
              case UsbEndpointDescriptor.TRANSTYPE_CONTROL:
                  canvas.write("Control");
                  break;
              case UsbEndpointDescriptor.TRANSTYPE_ISO:
                  canvas.write("Iso");
                  break;
              case UsbEndpointDescriptor.TRANSTYPE_BULK:
                  canvas.write("Bulk");
                  break;
              case UsbEndpointDescriptor.TRANSTYPE_INTERRUPT:
                  canvas.write("Interrupt");
                  break;
          }
          canvas.closeListItem();
  
          // These flags are only relevant for ISO transfer type
          if ((attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE)
                  == UsbEndpointDescriptor.TRANSTYPE_ISO) {
              canvas.openListItem();
              canvas.write("Aync: ");
              switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_SYNCTYPE) {
                  case UsbEndpointDescriptor.SYNCTYPE_NONE:
                      canvas.write("NONE");
                      break;
                  case UsbEndpointDescriptor.SYNCTYPE_ASYNC:
                      canvas.write("ASYNC");
                      break;
                  case UsbEndpointDescriptor.SYNCTYPE_ADAPTSYNC:
                      canvas.write("ADAPTIVE ASYNC");
                      break;
              }
              canvas.closeListItem();
  
              canvas.openListItem();
              canvas.write("Useage: ");
              switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_USEAGE) {
                  case UsbEndpointDescriptor.USEAGE_DATA:
                      canvas.write("DATA");
                      break;
                  case UsbEndpointDescriptor.USEAGE_FEEDBACK:
                      canvas.write("FEEDBACK");
                      break;
                  case UsbEndpointDescriptor.USEAGE_EXPLICIT:
                      canvas.write("EXPLICIT FEEDBACK");
                      break;
                  case UsbEndpointDescriptor.USEAGE_RESERVED:
                      canvas.write("RESERVED");
                      break;
              }
              canvas.closeListItem();
          }
          canvas.writeListItem("Package Size: " + getPacketSize());
          canvas.writeListItem("Interval: " + getInterval());
          canvas.closeList();
      }
  }
  