1fb726d48Sopenharmony_ci/*
2fb726d48Sopenharmony_ci * Copyright (C) 2022 Huawei Device Co., Ltd.
3fb726d48Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4fb726d48Sopenharmony_ci * you may not use this file except in compliance with the License.
5fb726d48Sopenharmony_ci * You may obtain a copy of the License at
6fb726d48Sopenharmony_ci *
7fb726d48Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8fb726d48Sopenharmony_ci *
9fb726d48Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10fb726d48Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11fb726d48Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fb726d48Sopenharmony_ci * See the License for the specific language governing permissions and
13fb726d48Sopenharmony_ci * limitations under the License.
14fb726d48Sopenharmony_ci */
15fb726d48Sopenharmony_ci
16fb726d48Sopenharmony_ciimport { TransmissionInterface } from './TransmissionInterface';
17fb726d48Sopenharmony_ciimport { info } from '../../log/Log';
18fb726d48Sopenharmony_ciimport { HDC_DEVICE_FILTER } from '../common/ConstantType';
19fb726d48Sopenharmony_ci
20fb726d48Sopenharmony_ciexport interface matchingUsbDevice {
21fb726d48Sopenharmony_ci  configurationValue: number;
22fb726d48Sopenharmony_ci  interfaceNumber: number;
23fb726d48Sopenharmony_ci  // @ts-ignore
24fb726d48Sopenharmony_ci  endPoints: USBEndpoint[];
25fb726d48Sopenharmony_ci}
26fb726d48Sopenharmony_ci
27fb726d48Sopenharmony_ciexport class UsbTransmissionChannel implements TransmissionInterface {
28fb726d48Sopenharmony_ci  // @ts-ignore
29fb726d48Sopenharmony_ci  private _device: USBDevice | null;
30fb726d48Sopenharmony_ci  private readonly endpointIn: number;
31fb726d48Sopenharmony_ci  private readonly endpointOut: number;
32fb726d48Sopenharmony_ci  private readonly interfaceNumber: number;
33fb726d48Sopenharmony_ci
34fb726d48Sopenharmony_ci  private constructor(
35fb726d48Sopenharmony_ci    // @ts-ignore
36fb726d48Sopenharmony_ci    device: USBDevice,
37fb726d48Sopenharmony_ci    endpointIn: number,
38fb726d48Sopenharmony_ci    endpointOut: number,
39fb726d48Sopenharmony_ci    interfaceNumber: number
40fb726d48Sopenharmony_ci  ) {
41fb726d48Sopenharmony_ci    this._device = device;
42fb726d48Sopenharmony_ci    this.endpointIn = endpointIn;
43fb726d48Sopenharmony_ci    this.endpointOut = endpointOut;
44fb726d48Sopenharmony_ci    this.interfaceNumber = interfaceNumber;
45fb726d48Sopenharmony_ci  }
46fb726d48Sopenharmony_ci
47fb726d48Sopenharmony_ci  /**
48fb726d48Sopenharmony_ci   * Send data to the device side
49fb726d48Sopenharmony_ci   *
50fb726d48Sopenharmony_ci   * @param writeData writeData
51fb726d48Sopenharmony_ci   */
52fb726d48Sopenharmony_ci  async writeData(writeData: ArrayBuffer): Promise<void> {
53fb726d48Sopenharmony_ci    await this._device?.transferOut(this.endpointOut, writeData);
54fb726d48Sopenharmony_ci  }
55fb726d48Sopenharmony_ci
56fb726d48Sopenharmony_ci  /**
57fb726d48Sopenharmony_ci   * read data from device via usb
58fb726d48Sopenharmony_ci   *
59fb726d48Sopenharmony_ci   * @param length
60fb726d48Sopenharmony_ci   */
61fb726d48Sopenharmony_ci  async readData(length: number): Promise<DataView> {
62fb726d48Sopenharmony_ci    const result = await this._device?.transferIn(this.endpointOut, length);
63fb726d48Sopenharmony_ci    if (result?.data) {
64fb726d48Sopenharmony_ci      return result.data;
65fb726d48Sopenharmony_ci    } else {
66fb726d48Sopenharmony_ci      return Promise.resolve(new DataView(new ArrayBuffer(0)));
67fb726d48Sopenharmony_ci    }
68fb726d48Sopenharmony_ci  }
69fb726d48Sopenharmony_ci
70fb726d48Sopenharmony_ci  /**
71fb726d48Sopenharmony_ci   * Close the device connection
72fb726d48Sopenharmony_ci   */
73fb726d48Sopenharmony_ci  async close(): Promise<void> {
74fb726d48Sopenharmony_ci    try {
75fb726d48Sopenharmony_ci      await this._device?.releaseInterface(this.interfaceNumber);
76fb726d48Sopenharmony_ci      await this._device?.close();
77fb726d48Sopenharmony_ci    } catch (e) {}
78fb726d48Sopenharmony_ci    this._device = null;
79fb726d48Sopenharmony_ci  }
80fb726d48Sopenharmony_ci
81fb726d48Sopenharmony_ci  /**
82fb726d48Sopenharmony_ci   * 打开设备
83fb726d48Sopenharmony_ci   *
84fb726d48Sopenharmony_ci   * @param usbDevice
85fb726d48Sopenharmony_ci   */
86fb726d48Sopenharmony_ci  static async openHdcDevice(
87fb726d48Sopenharmony_ci    // @ts-ignore
88fb726d48Sopenharmony_ci    usbDevice: USBDevice
89fb726d48Sopenharmony_ci  ): Promise<UsbTransmissionChannel | null> {
90fb726d48Sopenharmony_ci    try {
91fb726d48Sopenharmony_ci      await usbDevice.open();
92fb726d48Sopenharmony_ci      const matchDevice = this.filterAndFindDevice(usbDevice, HDC_DEVICE_FILTER);
93fb726d48Sopenharmony_ci      info('matchDevice is', matchDevice);
94fb726d48Sopenharmony_ci      if (!matchDevice) {
95fb726d48Sopenharmony_ci        throw new Error('Could not find hdc device');
96fb726d48Sopenharmony_ci      }
97fb726d48Sopenharmony_ci      await usbDevice.selectConfiguration(matchDevice.configurationValue);
98fb726d48Sopenharmony_ci      await usbDevice.claimInterface(matchDevice.interfaceNumber);
99fb726d48Sopenharmony_ci      const endpointIn = UsbTransmissionChannel.filterEndpointNumber(matchDevice.endPoints, 'in');
100fb726d48Sopenharmony_ci      const endpointOut = UsbTransmissionChannel.filterEndpointNumber(matchDevice.endPoints, 'out');
101fb726d48Sopenharmony_ci      return new UsbTransmissionChannel(usbDevice, endpointIn, endpointOut, matchDevice.interfaceNumber);
102fb726d48Sopenharmony_ci    } catch (e) {
103fb726d48Sopenharmony_ci      return Promise.resolve(null);
104fb726d48Sopenharmony_ci    }
105fb726d48Sopenharmony_ci  }
106fb726d48Sopenharmony_ci
107fb726d48Sopenharmony_ci  /**
108fb726d48Sopenharmony_ci   * Filter out matching devices
109fb726d48Sopenharmony_ci   *
110fb726d48Sopenharmony_ci   * @param device device
111fb726d48Sopenharmony_ci   * @param filter filter
112fb726d48Sopenharmony_ci   */
113fb726d48Sopenharmony_ci  private static filterAndFindDevice(
114fb726d48Sopenharmony_ci    // @ts-ignore
115fb726d48Sopenharmony_ci    device: USBDevice,
116fb726d48Sopenharmony_ci    // @ts-ignore
117fb726d48Sopenharmony_ci    filter: USBDeviceFilter
118fb726d48Sopenharmony_ci  ): matchingUsbDevice | null {
119fb726d48Sopenharmony_ci    for (const config of device.configurations) {
120fb726d48Sopenharmony_ci      for (const intf of config.interfaces) {
121fb726d48Sopenharmony_ci        for (const al of intf.alternates) {
122fb726d48Sopenharmony_ci          if (
123fb726d48Sopenharmony_ci            filter.classCode === al.interfaceClass &&
124fb726d48Sopenharmony_ci            filter.subclassCode === al.interfaceSubclass &&
125fb726d48Sopenharmony_ci            filter.protocolCode === al.interfaceProtocol
126fb726d48Sopenharmony_ci          ) {
127fb726d48Sopenharmony_ci            return {
128fb726d48Sopenharmony_ci              configurationValue: config.configurationValue,
129fb726d48Sopenharmony_ci              interfaceNumber: intf.interfaceNumber,
130fb726d48Sopenharmony_ci              endPoints: al.endpoints,
131fb726d48Sopenharmony_ci            };
132fb726d48Sopenharmony_ci          }
133fb726d48Sopenharmony_ci        }
134fb726d48Sopenharmony_ci      }
135fb726d48Sopenharmony_ci    }
136fb726d48Sopenharmony_ci    return null;
137fb726d48Sopenharmony_ci  }
138fb726d48Sopenharmony_ci
139fb726d48Sopenharmony_ci  private static filterEndpointNumber(
140fb726d48Sopenharmony_ci    // @ts-ignore
141fb726d48Sopenharmony_ci    usbEndpoints: USBEndpoint[],
142fb726d48Sopenharmony_ci    dir: 'in' | 'out',
143fb726d48Sopenharmony_ci    type = 'bulk'
144fb726d48Sopenharmony_ci  ): number {
145fb726d48Sopenharmony_ci    let endpoint = usbEndpoints.filter((element) => {
146fb726d48Sopenharmony_ci      return element.direction === dir && element.type === type;
147fb726d48Sopenharmony_ci    });
148fb726d48Sopenharmony_ci    return endpoint[0].endpointNumber;
149fb726d48Sopenharmony_ci  }
150fb726d48Sopenharmony_ci}
151