1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.usb;
18
19import com.android.internal.util.Preconditions;
20
21import android.os.Parcel;
22import android.os.Parcelable;
23
24/**
25 * Represents a physical USB port and describes its characteristics.
26 * <p>
27 * This object is immutable.
28 * </p>
29 *
30 * @hide
31 */
32public final class UsbPort implements Parcelable {
33    private final String mId;
34    private final int mSupportedModes;
35
36    /**
37     * Mode bit: This USB port can act as a downstream facing port (host).
38     * <p>
39     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
40     * combination of roles (and possibly others as well).
41     * </p>
42     */
43    public static final int MODE_DFP = 1 << 0;
44
45    /**
46     * Mode bit: This USB port can act as an upstream facing port (device).
47     * <p>
48     * Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
49     * combination of roles (and possibly others as well).
50     * </p>
51     */
52    public static final int MODE_UFP = 1 << 1;
53
54    /**
55     * Mode bit: This USB port can act either as an downstream facing port (host) or as
56     * an upstream facing port (device).
57     * <p>
58     * Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
59     * combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
60     * combination of roles (and possibly others as well).
61     * </p>
62     */
63    public static final int MODE_DUAL = MODE_DFP | MODE_UFP;
64
65    /**
66     * Power role: This USB port can act as a source (provide power).
67     */
68    public static final int POWER_ROLE_SOURCE = 1;
69
70    /**
71     * Power role: This USB port can act as a sink (receive power).
72     */
73    public static final int POWER_ROLE_SINK = 2;
74
75    /**
76     * Data role: This USB port can act as a host (access data services).
77     */
78    public static final int DATA_ROLE_HOST = 1;
79
80    /**
81     * Data role: This USB port can act as a device (offer data services).
82     */
83    public static final int DATA_ROLE_DEVICE = 2;
84
85    private static final int NUM_DATA_ROLES = 3;
86
87    /** @hide */
88    public UsbPort(String id, int supportedModes) {
89        mId = id;
90        mSupportedModes = supportedModes;
91    }
92
93    /**
94     * Gets the unique id of the port.
95     *
96     * @return The unique id of the port; not intended for display.
97     */
98    public String getId() {
99        return mId;
100    }
101
102    /**
103     * Gets the supported modes of the port.
104     * <p>
105     * The actual mode of the port may vary depending on what is plugged into it.
106     * </p>
107     *
108     * @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
109     * {@link #MODE_DUAL}.
110     */
111    public int getSupportedModes() {
112        return mSupportedModes;
113    }
114
115    /**
116     * Combines one power and one data role together into a unique value with
117     * exactly one bit set.  This can be used to efficiently determine whether
118     * a combination of roles is supported by testing whether that bit is present
119     * in a bit-field.
120     *
121     * @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
122     * or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
123     * @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
124     * or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
125     * @hide
126     */
127    public static int combineRolesAsBit(int powerRole, int dataRole) {
128        checkRoles(powerRole, dataRole);
129        final int index = powerRole * NUM_DATA_ROLES + dataRole;
130        return 1 << index;
131    }
132
133    /** @hide */
134    public static String modeToString(int mode) {
135        switch (mode) {
136            case 0:
137                return "none";
138            case MODE_DFP:
139                return "dfp";
140            case MODE_UFP:
141                return "ufp";
142            case MODE_DUAL:
143                return "dual";
144            default:
145                return Integer.toString(mode);
146        }
147    }
148
149    /** @hide */
150    public static String powerRoleToString(int role) {
151        switch (role) {
152            case 0:
153                return "no-power";
154            case POWER_ROLE_SOURCE:
155                return "source";
156            case POWER_ROLE_SINK:
157                return "sink";
158            default:
159                return Integer.toString(role);
160        }
161    }
162
163    /** @hide */
164    public static String dataRoleToString(int role) {
165        switch (role) {
166            case 0:
167                return "no-data";
168            case DATA_ROLE_HOST:
169                return "host";
170            case DATA_ROLE_DEVICE:
171                return "device";
172            default:
173                return Integer.toString(role);
174        }
175    }
176
177    /** @hide */
178    public static String roleCombinationsToString(int combo) {
179        StringBuilder result = new StringBuilder();
180        result.append("[");
181
182        boolean first = true;
183        while (combo != 0) {
184            final int index = Integer.numberOfTrailingZeros(combo);
185            combo &= ~(1 << index);
186            final int powerRole = index / NUM_DATA_ROLES;
187            final int dataRole = index % NUM_DATA_ROLES;
188            if (first) {
189                first = false;
190            } else {
191                result.append(", ");
192            }
193            result.append(powerRoleToString(powerRole));
194            result.append(':');
195            result.append(dataRoleToString(dataRole));
196        }
197
198        result.append("]");
199        return result.toString();
200    }
201
202    /** @hide */
203    public static void checkRoles(int powerRole, int dataRole) {
204        Preconditions.checkArgumentInRange(powerRole, 0, POWER_ROLE_SINK, "powerRole");
205        Preconditions.checkArgumentInRange(dataRole, 0, DATA_ROLE_DEVICE, "dataRole");
206    }
207
208    @Override
209    public String toString() {
210        return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
211    }
212
213    @Override
214    public int describeContents() {
215        return 0;
216    }
217
218    @Override
219    public void writeToParcel(Parcel dest, int flags) {
220        dest.writeString(mId);
221        dest.writeInt(mSupportedModes);
222    }
223
224    public static final Parcelable.Creator<UsbPort> CREATOR =
225            new Parcelable.Creator<UsbPort>() {
226        @Override
227        public UsbPort createFromParcel(Parcel in) {
228            String id = in.readString();
229            int supportedModes = in.readInt();
230            return new UsbPort(id, supportedModes);
231        }
232
233        @Override
234        public UsbPort[] newArray(int size) {
235            return new UsbPort[size];
236        }
237    };
238}
239