usb-ctrl: add a way to mount composite gdts

The usb-vhub has a limited number of usb-gadget ports. The BMC features
might want to attach usb-device count greater than the aspeed-vhub
contains ports. One of the ways to solve the issue could be by attaching
usb-gadget functions to a composite usb interface.

The change brings a way to mount mass_storage and cdc-based devices
under composite device configuration (VID/PID: 1D6B:0104)

This make possible to mount bouth device functions under single
vhub-port.

Tested:
* Insert usb-gadget:mass-storage device is success:
  ```bash
  #: usb-ctrl insert usb0 /tmp/image.iso cdrom
  ```
* Insert usb-gadget:usb-ecm device for same port is success:
  ```bash
  #: usb-ctrl ecm usb0 on A0:C5:F2:15:B5:89 A0:C5:F2:15:B5:88
  ```
* the sysfs displays function-descriptors for both interfaces:
  ```bash
  #: root@vegman-r120-020323000A:~# cd /sys/kernel/config/usb_gadget
  #: root@vegman-r120-020323000A:~# ls -la ./usb0/functions/
     drwxr-xr-x 4 root root 0 Dec 14 12:13 .
     drwxr-xr-x 6 root root 0 Dec 14 12:14 ..
     drwxr-xr-x 2 root root 0 Dec 14 12:13 ecm.usb0
     drwxr-xr-x 3 root root 0 Dec 14 12:11 mass_storage.usb0
  ```
* eject one of the mounted device works properly, another one stay
  online:
  ```bash
  #: usb-ctrl ecm usb0 off
  #: ls -la /sys/kernel/config/usb_gadget/usb0/functions/
     drwxr-xr-x 3 root root 0 Dec 14 12:19 .
     drwxr-xr-x 6 root root 0 Dec 14 12:19 ..
     drwxr-xr-x 3 root root 0 Dec 14 12:11 mass_storage.usb0
  ```

* output on the host side
  ```bash
  #: root@ubuntu:/home/ubuntu# lsusb -d 1d6b:0104 -v

     Bus 001 Device 003: ID 1d6b:0104 Linux Foundation Multifunction ...
     Device Descriptor:
     ...
       idVendor 0x1d6b Linux Foundation
       idProduct 0x0104 Multifunction Composite Gadget
     ...
       Interface Descriptor:
       ...
            bInterfaceClass 8 Mass Storage
            bInterfaceSubClass 6 SCSI
            bInterfaceProtocol 80 Bulk-Only
            iInterface 5 Mass Storage
       ...
       Interface Descriptor:
       ...
            bInterfaceClass 2 Communications
            bInterfaceSubClass 6 Ethernet Networking
            bInterfaceProtocol 0
            iInterface 7 CDC Ethernet Control Model (ECM)
       ...

  ```

Change-Id: I6f54ac5c116ee0bb08e9d18c33267f5b401c2c00
Signed-off-by: Igor Kononenko <i.kononenko.e@gmail.com>
diff --git a/usb-ctrl/usb-ctrl b/usb-ctrl/usb-ctrl
index 4d228fd..943e7d4 100644
--- a/usb-ctrl/usb-ctrl
+++ b/usb-ctrl/usb-ctrl
@@ -151,29 +151,49 @@
     local intf_type="$4"
     local bmc_mac="$3"
     local host_mac="$4"
-
-    if [ -d "$GADGET_BASE/${name}" ]; then
-        echo "Device ${name} already exists" >&2
-        return 1
-    fi
-    mkdir "$GADGET_BASE/${name}"
-    cd "$GADGET_BASE/${name}"
-
-    echo 0x1d6b > idVendor	# Linux Foundation
-    echo 0x0105 > idProduct # FunctionFS Gadget
-    mkdir strings/0x409
-    local machineid
-    machineid=$(cat /etc/machine-id)
-    local data="OpenBMC USB gadget device serial number"
-    local serial
-    serial=$( echo -n "${machineid}${data}${machineid}" | \
-        sha256sum | cut -b 0-12 )
-    echo "$serial" > strings/0x409/serialnumber
-    echo "OpenBMC" > strings/0x409/manufacturer
-    echo "OpenBMC USB Device" > strings/0x409/product
+    # By default, the gadget is not multifunctional
+    local is_multifunction=false
+    local gadget_function
+    local dev=
 
     gadget_function="functions/${dev_type}.${name}"
-    mkdir configs/c.1
+
+    # Checking whether the device is already attached
+    if [ -d "$GADGET_BASE/${name}" ]; then
+        # Checking whether requested function is attached
+        if [ -d "$GADGET_BASE/${name}/${gadget_function}" ]; then
+            echo "Function ${dev_type}.${name} already exists" >&2
+            return 1
+        fi
+
+        # if the requested function is not configured but the device
+        # is connected, it is assumed that the device is multifunctional
+        is_multifunction=true
+    fi
+
+    if [ "${is_multifunction}" = false ]; then
+        mkdir "$GADGET_BASE/${name}"
+        cd "$GADGET_BASE/${name}"
+        echo 0x1d6b > idVendor	# Linux Foundation
+        echo 0x0104 > idProduct # Multifunction Composite Gadget
+
+        mkdir strings/0x409
+        local machineid
+        machineid=$(cat /etc/machine-id)
+        local data="OpenBMC USB gadget device serial number"
+        local serial
+        serial=$( echo -n "${machineid}${data}${machineid}" | \
+            sha256sum | cut -b 0-12 )
+        echo "$serial" > strings/0x409/serialnumber
+        echo "OpenBMC" > strings/0x409/manufacturer
+        echo "OpenBMC USB Device" > strings/0x409/product
+
+        mkdir configs/c.1
+    else
+        cd "$GADGET_BASE/${name}"
+        udc=$(cat "$GADGET_BASE/${name}/UDC")
+    fi
+
     mkdir "${gadget_function}"
     case "${dev_type}" in
         mass_storage)
@@ -186,13 +206,18 @@
             echo "${host_mac}" > "${gadget_function}/host_addr"
             ;;
     esac
-    mkdir configs/c.1/strings/0x409
+    if [ "${is_multifunction}" = false ]; then
+        mkdir configs/c.1/strings/0x409
 
-    echo "Conf 1" > configs/c.1/strings/0x409/configuration
-    echo 120 > configs/c.1/MaxPower
+        echo "Conf 1" > configs/c.1/strings/0x409/configuration
+        echo 120 > configs/c.1/MaxPower
+        dev=$(which_dev)
+    else
+        dev=$(cat "$GADGET_BASE/${name}/UDC")
+        # reattach UDC to reconfigure interface descriptors.
+        echo '' > UDC
+    fi
     ln -s "${gadget_function}" configs/c.1
-    local dev
-    dev=$(which_dev)
     echo "$dev" > UDC
 }
 
@@ -202,13 +227,27 @@
 {
     local name="$1"
     local dev_type="$2"
+    local func_cnt
+    local udc
+
+    func_cnt=$(find "$GADGET_BASE/${name}/functions/" -maxdepth 1 -mindepth 1 | wc -l)
+    udc=$(cat "$GADGET_BASE/${name}/UDC")
 
     echo '' > "$GADGET_BASE/${name}/UDC"
 
+    # remove function
     rm -f "$GADGET_BASE/${name}/configs/c.1/${dev_type}.${name}"
+    rmdir "$GADGET_BASE/${name}/functions/${dev_type}.${name}"
+
+    # check wether composite device is used
+    if [ "${func_cnt}" -gt 1 ]; then
+        # bring the device back online
+        echo "${udc}" > "$GADGET_BASE/${name}/UDC"
+        return 0;
+    fi
+
     rmdir "$GADGET_BASE/${name}/configs/c.1/strings/0x409"
     rmdir "$GADGET_BASE/${name}/configs/c.1"
-    rmdir "$GADGET_BASE/${name}/functions/${dev_type}.${name}"
     rmdir "$GADGET_BASE/${name}/strings/0x409"
     rmdir "$GADGET_BASE/${name}"
 }