程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Rockchip RK3399 - USB调试

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板
eMMC16GB
LPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot2023.04
linux6.3
----------------------------------------------------------------------------------------------------------------------------

一、设备树

设备树配置参考文档:

  • Documentation/devicetree/bindings/usb/generic-ehci.yaml; (USB控制器的通用属性配置说明)
  • Documentation/devicetree/bindings/usb/generic-ohci.yaml; (USB控制器的通用属性配置说明)
  • Documentation/devicetree/bindings/usb/generic-xhci.yaml; (USB控制器的通用属性配置说明)
  • Documentation/devicetree/bindings/usb/fcs,fusb302.yaml
  • Documentation/devicetree/bindings/usb/rockchip,rk3399-dwc3.yaml;(适用于RK3399
  • Documentation/devicetree/bindings/usb/usb-uhci.txt
  • Documentation/devicetree/bindings/usb/usb-xhci.yaml
  • Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
  • Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml;
  • Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml;

1.1 USB2.0 Host Type-A

NanoPC-T4开发板支持两个USB2.0 Host Type-A接口,对应的USB控制器为USB2.0 HOST(EHCI&OHCI)、对应的USB PHYUSB2.0 HOST PHY

因此对应的设备树配置,包括USB2.0 HOST(EHCI&OHCI)控制器设备树配置和USB2.0 HOST PHY设备树配置。

1.1.1 控制器配置

(1) USB2.0 HOST0控制器设备节点usb_host0_ehciusb_host0_ohci定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

usb_host0_ehci: usb@fe380000 {
		compatible = "generic-ehci";
		reg = <0x0 0xfe380000 0x0 0x20000>;
		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
				 <&u2phy0>;
		phys = <&u2phy0_host>;
		phy-names = "usb";
		status = "disabled";
};

usb_host0_ohci: usb@fe3a0000 {
		compatible = "generic-ohci";
		reg = <0x0 0xfe3a0000 0x0 0x20000>;
		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>,
				 <&u2phy0>;
		phys = <&u2phy0_host>;
		phy-names = "usb";
		status = "disabled";
};

USB控制器设备节点中,通过phys属性关联对应的USB PHY

phys = <&u2phy0_host>;

这表明这两个USB控制器都用了u2phy0_host这个USB PHY.

这两个USB控制器就是我们之前介绍的是EHCIOHCIOHCI支持USB1.0USB1.1EHCI支持USB2.0

(2) USB2.0 HOST1控制器设备节点usb_host1_ehciusb_host1_ohci定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

usb_host1_ehci: usb@fe3c0000 {
		compatible = "generic-ehci";
		reg = <0x0 0xfe3c0000 0x0 0x20000>;
		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
				 <&u2phy1>;
		phys = <&u2phy1_host>;
		phy-names = "usb";
		status = "disabled";
};

usb_host1_ohci: usb@fe3e0000 {
		compatible = "generic-ohci";
		reg = <0x0 0xfe3e0000 0x0 0x20000>;
		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>,
				 <&u2phy1>;
		phys = <&u2phy1_host>;
		phy-names = "usb";
		status = "disabled";
};
1.1.2 PHY配置

USB2.0 PHY包含两个端口,以USB2.0 PHY0为例,包含的两个端口设备节点为u2phy0_hostu2phy0_otg

(1) USB2.0 HOST PHY0设备节点u2phy0_host定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

u2phy0: usb2phy@e450 {
		compatible = "rockchip,rk3399-usb2phy";
		reg = <0xe450 0x10>;
		clocks = <&cru SCLK_USB2PHY0_REF>;
		clock-names = "phyclk";
		#clock-cells = <0>;
		clock-output-names = "clk_usbphy0_480m";
		status = "disabled";

		u2phy0_host: host-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "linestate";
				status = "disabled";
		};

		u2phy0_otg: otg-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "otg-bvalid", "otg-id",
								  "linestate";
				status = "disabled";
		};
};

(2) USB2.0 HOST PHY1设备节点u2phy1_host定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

u2phy1: usb2phy@e460 {
		compatible = "rockchip,rk3399-usb2phy";
		reg = <0xe460 0x10>;
		clocks = <&cru SCLK_USB2PHY1_REF>;
		clock-names = "phyclk";
		#clock-cells = <0>;
		clock-output-names = "clk_usbphy1_480m";
		status = "disabled";

		u2phy1_host: host-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "linestate";
				status = "disabled";
		};

		u2phy1_otg: otg-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "otg-bvalid", "otg-id",
								  "linestate";
				status = "disabled";
		};
};
1.1.3 电源配置

(1) USB2.0 PHY芯片三路电源依次为VCCA0V9_S3VCCA1V8_S3VCC3V3_S3

VCCA0V9_S3VCCA1V8_S3这两路由PMIC_SLEEP_H(连接RK3399GPIO1_A5/AP_PWROFF,这个应该是处理器睡眠引脚,处理器工作时电源有效)引脚控制的;

VCC3V3_S3RK808电源管理芯片第7号引脚VSWOUT提供,因此需要配置其电源输出为3.3V

arch/arm64/boot/dts/rockchip/rk3399-evb.dts配置vcc3v3_s3设备节点;

&i2c0 {
    ...... 

    rk808: pmic@1b {
		......

        regulators {
           
		    ......
			
            vcc3v3_s3: SWITCH_REG1 {
                regulator-always-on;
                regulator-boot-on;
                regulator-name = "vcc3v3_s3";

                regulator-state-mem {
                    regulator-off-in-suspend;
                };
            };
			
			......

        };
    };
};

完整的rk808设备节点配置参考:rk808驱动配置

(2) USB2.0 Host Type-A接口电源VCC5V0_HOST0RT9724GQW提供的,其输入端为VCC5V0_SYS

arch/arm64/boot/dts/rockchip/rk3399-evb.dts配置vcc5v0_host

vcc5v0_sys: vcc5v0-sys {
		compatible = "regulator-fixed";
		regulator-name = "vcc5v0_sys";
		regulator-always-on;
		regulator-boot-on;
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
};

vcc5v0_host: vcc5v0-host-regulator {
		compatible = "regulator-fixed";
		regulator-name = "vcc5v0_host";
		regulator-always-on;
		regulator-boot-on;	
		vin-supply = <&vcc5v0_sys>;
};
1.1.4 使能

下面介绍的都是配置在arch/arm64/boot/dts/rockchip/rk3399-evb.dts文件。

(1) 为了同时支持USB1.0USB1.1USB2.0,就需要同时用到EHCIOHCI,因此需要把这两个USB控制器都打开:

&usb_host0_ehci {
        status = "okay";
};

&usb_host0_ohci {
        status = "okay";
};

&usb_host1_ehci {
        status = "okay";
};

&usb_host1_ohci {
        status = "okay";
};

(2) 使能USB2.0 HOST PHY0,同时配置USB2.0 HOST PHY0 phy-supply属性,用于控制USB电源VBUS

&u2phy0 {
        status = "okay";
};

&u2phy0_host {
        phy-supply = <&vcc5v0_host>;
        status = "okay";
};

(3) 使能USB2.0 HOST PHY1,同时配置USB2.0 HOST PHY1 phy-supply属性,用于控制USB电源VBUS

&u2phy1 {
        status = "okay";
};

&u2phy1_host {
        phy-supply = <&vcc5v0_host>;
        status = "okay";
};

1.2 USB3.0 Host Type-A

NanoPC-T4开发板支持1个USB3.0 Host Type-A接口,对应的USB控制器为USB3.0 OTG1(DWC3/xHCI)、对应的USB PHYUSB3.0 Type-C PHY1USB2.0 OTG PHY1

因此对应的设备树配置,包括USB3.0 OTG1(DWC3/xHCI)控制器设备树配置和USB3.0 Type-C PHY1USB2.0 OTG PHY1设备树配置。

1.2.1 控制器配置

(1) USB3.0 OTG1(DWC3/xHCI)控制器设备节点usbdrd3_1,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

usbdrd3_1: usb@fe900000 {
		compatible = "rockchip,rk3399-dwc3";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
		clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
				 <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
				 <&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
		clock-names = "ref_clk", "suspend_clk",
					  "bus_clk", "aclk_usb3_rksoc_axi_perf",
					  "aclk_usb3", "grf_clk";
		resets = <&cru SRST_A_USB3_OTG1>;
		reset-names = "usb3-otg";
		status = "disabled";
		usbdrd_dwc3_1: usb@fe900000 {
				compatible = "snps,dwc3";
				reg = <0x0 0xfe900000 0x0 0x100000>;
				interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH 0>;
				clocks = <&cru SCLK_USB3OTG1_REF>, <&cru ACLK_USB3OTG1>,
						 <&cru SCLK_USB3OTG1_SUSPEND>;
				clock-names = "ref", "bus_early", "suspend";
				dr_mode = "otg";
				phys = <&u2phy1_otg>, <&tcphy1_usb3>;
				phy-names = "usb2-phy", "usb3-phy";
				phy_type = "utmi_wide";
				snps,dis_enblslpm_quirk;
				snps,dis-u2-freeclk-exists-quirk;
				snps,dis_u2_susphy_quirk;
				snps,dis-del-phy-power-chg-quirk;
				snps,dis-tx-ipgap-linecheck-quirk;
				power-domains = <&power RK3399_PD_USB3>;
				status = "disabled";
		};
};
1.2.2 PHY配置

(1) USB3.0 Type-C PHY1设备节点tcphy1_usb3,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

tcphy1: phy@ff800000 {
		compatible = "rockchip,rk3399-typec-phy";
		reg = <0x0 0xff800000 0x0 0x40000>;
		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
				 <&cru SCLK_UPHY1_TCPDPHY_REF>;
		clock-names = "tcpdcore", "tcpdphy-ref";
		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
		assigned-clock-rates = <50000000>;
		power-domains = <&power RK3399_PD_TCPD1>;
		resets = <&cru SRST_UPHY1>,
				 <&cru SRST_UPHY1_PIPE_L00>,
				 <&cru SRST_P_UPHY1_TCPHY>;
		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
		rockchip,grf = <&grf>;
		status = "disabled";

		tcphy1_dp: dp-port {
				#phy-cells = <0>;
		};

		tcphy1_usb3: usb3-port {
				#phy-cells = <0>;
		};
};

(2) USB2.0 OTG PHY1设备节点u2phy1_otg,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

u2phy1: usb2phy@e460 {
		compatible = "rockchip,rk3399-usb2phy";
		reg = <0xe460 0x10>;
		clocks = <&cru SCLK_USB2PHY1_REF>;
		clock-names = "phyclk";
		#clock-cells = <0>;
		clock-output-names = "clk_usbphy1_480m";
		status = "disabled";

		u2phy1_host: host-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "linestate";
				status = "disabled";
		};

		u2phy1_otg: otg-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "otg-bvalid", "otg-id",
								  "linestate";
				status = "disabled";
		};
};
1.2.3 电源配置

(1) USB2.0 PHY1芯片三路电源依次为VCCA0V9_S3VCCA1V8_S3VCC3V3_S3, 这个配置上面已经介绍了,不再重复介绍。

(2) USB3.0 Type-C PHY1芯片三路电源和USB2.0 PHY1芯片三路电源一样。

(3) USB3.0 Host Type-A接口电源VCC5V0_HOST2RT9724GQW提供的,其输入端为VCC5V0_SYS

arch/arm64/boot/dts/rockchip/rk3399-evb.dts配置vcc5v2_host

vcc5v0_sys: vcc5v0-sys {
		compatible = "regulator-fixed";
		regulator-name = "vcc5v0_sys";
		regulator-always-on;
		regulator-boot-on;
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
};

vcc5v2_host: vcc5v2-host-regulator {
		compatible = "regulator-fixed";
		regulator-name = "vcc5v2_host";
		regulator-always-on;
		regulator-boot-on;	
		vin-supply = <&vcc5v0_sys>;
};
1.2.4 使能

下面介绍的都是配置在arch/arm64/boot/dts/rockchip/rk3399-evb.dts文件。

(1) 使能usbdrd3_1

/* Configurate and Enable USB3.0 OTG Controller */
&usbdrd3_1 {
	status = "okay";
};

&usbdrd_dwc3_1 {
	/* 配置dr_mode为host,表示只支持Host only mode */
	dr_mode = "host";
	status = "okay";
};

(2) 使能tcphy1_usb3

/* Enable USB3.0 PHY */
&tcphy1 {
	status = "okay";
};

&tcphy1_usb3{
	status = "okay";
};

(3) 使能u2phy1_otg,同时配置USB2.0 OTG PHY1 phy-supply属性,用于控制USB电源VBUS

/* Enable USB2.0 PHY */
&u2phy1 {
   status = "okay";
};

&u2phy1_otg {
	phy-supply = <&vcc5v2_host>;
	status = "okay";
};
1.2.5 总结

USB3.0 Host Type-A接口设备树配置的注意点如下:

  • 对应的fusb节点不要配置,因为USB3.0 Host Type-A接口不需要fusb302芯片;
  • 对应的USB控制器子节点(usbdrd_dwc3)和PHY的节点(tcphyu2phy)都要删除extcon属性;
  • 对应的USB控制器子节点(usbdrd_dwc3)的dr_mode属性要配置为host

1.3 USB3.0 Type-C

NanoPC-T4开发板支持1个USB3.0 Type-C接口,对应的USB控制器为USB3.0 OTG0(DWC3/xHCI)、对应的USB PHYUSB3.0 Type-C PHY0USB2.0 OTG PHY0

因此对应的设备树配置,包括USB3.0 OTG0(DWC3/xHCI)控制器设备树配置和USB3.0 Type-C PHY0USB2.0 OTG PHY0设备树配置。

1.3.1 控制器配置

(1) USB3.0 OTG0(DWC3/xHCI)控制器设备节点usbdrd3_0,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

usbdrd3_0: usb@fe800000 {
		compatible = "rockchip,rk3399-dwc3";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
		clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
				 <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
				 <&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
		clock-names = "ref_clk", "suspend_clk",
					  "bus_clk", "aclk_usb3_rksoc_axi_perf",
					  "aclk_usb3", "grf_clk";
		resets = <&cru SRST_A_USB3_OTG0>;
		reset-names = "usb3-otg";
		status = "disabled";
		usbdrd_dwc3_0: usb@fe800000 {
				compatible = "snps,dwc3";
				reg = <0x0 0xfe800000 0x0 0x100000>;
				interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
				clocks = <&cru SCLK_USB3OTG0_REF>, <&cru ACLK_USB3OTG0>,
						 <&cru SCLK_USB3OTG0_SUSPEND>;
				clock-names = "ref", "bus_early", "suspend";
				dr_mode = "otg";
				phys = <&u2phy0_otg>, <&tcphy0_usb3>;
				phy-names = "usb2-phy", "usb3-phy";
				phy_type = "utmi_wide";
				snps,dis_enblslpm_quirk;
				snps,dis-u2-freeclk-exists-quirk;
				snps,dis_u2_susphy_quirk;
				snps,dis-del-phy-power-chg-quirk;
				snps,dis-tx-ipgap-linecheck-quirk;
				power-domains = <&power RK3399_PD_USB3>;
				status = "disabled";
		};
};
1.3.2 PHY配置

(1) USB3.0 Type-C PHY0设备节点tcphy0_usb3,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

tcphy0: phy@ff7c0000 {
		compatible = "rockchip,rk3399-typec-phy";
		reg = <0x0 0xff7c0000 0x0 0x40000>;
		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
				 <&cru SCLK_UPHY0_TCPDPHY_REF>;
		clock-names = "tcpdcore", "tcpdphy-ref";
		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
		assigned-clock-rates = <50000000>;
		power-domains = <&power RK3399_PD_TCPD0>;
		resets = <&cru SRST_UPHY0>,
				 <&cru SRST_UPHY0_PIPE_L00>,
				 <&cru SRST_P_UPHY0_TCPHY>;
		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
		rockchip,grf = <&grf>;
		status = "disabled";

		tcphy0_dp: dp-port {
				#phy-cells = <0>;
		};

		tcphy0_usb3: usb3-port {
				#phy-cells = <0>;
		};
};

(2) USB2.0 OTG PHY0设备节点u2phy0_otg,定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi

u2phy0: usb2phy@e450 {
		compatible = "rockchip,rk3399-usb2phy";
		reg = <0xe450 0x10>;
		clocks = <&cru SCLK_USB2PHY0_REF>;
		clock-names = "phyclk";
		#clock-cells = <0>;
		clock-output-names = "clk_usbphy0_480m";
		status = "disabled";

		u2phy0_host: host-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "linestate";
				status = "disabled";
		};

		u2phy0_otg: otg-port {
				#phy-cells = <0>;
				interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH 0>,
							 <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH 0>;
				interrupt-names = "otg-bvalid", "otg-id",
								  "linestate";
				status = "disabled";
		};
};
1.3.3 电源配置

(1) USB2.0 PHY0芯片三路电源依次为VCCA0V9_S3VCCA1V8_S3VCC3V3_S3, 这个配置上面已经介绍了,不再重复介绍。

(2) USB3.0 Type-C PHY0芯片三路电源和USB2.0 PHY0芯片三路电源一样。

(3) USB3.0 Type-C接口电源VBUS_TYPECRT9724GQW提供的,其输入端为VCC5V0_SYS。由GPIO4_D2引脚使能,高电平有效;

arch/arm64/boot/dts/rockchip/rk3399-evb.dts配置vbus_typec

vcc5v0_sys: vcc5v0-sys {
		compatible = "regulator-fixed";
		regulator-name = "vcc5v0_sys";
		regulator-always-on;
		regulator-boot-on;
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
};

vbus_typec: vbus-typec {
		compatible = "regulator-fixed";
		enable-active-high;
		gpio = <&gpio4 RK_PD2 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&vbus_typec_en>;
		regulator-name = "vbus_typec";
		vin-supply = <&vcc5v0_sys>;
};

由于vbus_typec节点配置了default状态对应的引脚配置节点为vbus_typec_en,因此需要在pinctrl节点下增加引脚配置节点vbus_typec_en,这个节点在fusb配置中介绍。

1.3.4 fusb302配置

arch/arm64/boot/dts/rockchip/rk3399-evb.dts配置:

&i2c4 {
        clock-frequency = <400000>;
        i2c-scl-rising-time-ns = <160>;
        i2c-scl-falling-time-ns = <30>;
        status = "okay";

        fusb0: typec-portc@22 {
                compatible = "fcs,fusb302";
                reg = <0x22>;
                interrupt-parent = <&gpio1>;
                interrupts = <RK_PA2 IRQ_TYPE_LEVEL_LOW>;
                pinctrl-names = "default";
                pinctrl-0 = <&fusb0_int>;
                vbus-supply = <&vbus_typec>;
	            status = "okay";
        };
};

&pinctrl {
        fusb30x {
                fusb0_int: fusb0-int {
                       rockchip,pins = <1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>;
                };
				
				vbus_typec_en: vbus-typec-en {
					   rockchip,pins = <4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_up>;
				};
        };
		......
}

其中GPIO1_A2连接了fusb302的中断引脚,低电平触发。

fusb0设备节点匹配的驱动程序是drivers/usb/typec/tcpm/fusb302.c,这里fusb0设备节点配置的可能有问题,因此后面测试的时候无法自动识别是接入了USB主机或USB设备。这里我就不去追究如何配置了,具体可以参考:

如果你使用的是linux 4.19,更改设备节点fusb0如下:

&i2c4 {
		.....
        fusb0: fusb30x@22 {
                compatible = "fairchild,fusb302";
                reg = <0x22>;
                pinctrl-names = "default";
                pinctrl-0 = <&fusb0_int>;
                int-n-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;    /*  中断引脚GPIO1_A2配置 */
                vbus-5v-gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>;   /* VBUS使能引脚GPIO4_D2配置 */
                status = "okay";
        };
};

fusb0设备节点匹配的驱动程序是drivers/mfd/fusb302.c

1.3.5 使能

下面介绍的都是配置在arch/arm64/boot/dts/rockchip/rk3399-evb.dts文件。

(1) 使能usbdrd3_0

通过dr_mode属性设置USB控制器的模式,一共有三种模式:otg(同时支持主机/设备)、host(主机)、peripheral(设备);这里配置为otg

/* Configurate and Enable USB3.0 OTG Controller notifier */
&usbdrd3_0 {
	status = "okay";
};

&usbdrd_dwc3_0 {
	/* 配置dr_mode为otg */
	dr_mode = "otg";
	status = "okay";
};

需要注意的是由于我们使用的是linux 6.3内核,内核没有支持驱动drivers/mfd/fusb302.c,导致不会为fusb302驱动注册extcon设备,因此不要在usbdrd3_0设备节点配置extcon属性,不然将会由于找不到extcon,导致usbdrd3_0无法正常工作;并且出现类似如下错误:

[   24.692156] platform ff7c0000.phy: deferred probe pending
[   24.692188] platform fe800000.usb: deferred probe pending

如果你使用的是linux 4.19的内核,由于内核支持驱动drivers/mfd/fusb302.c,因此在usbdrd3_0节点配置extcon属性;

&usbdrd3_0 {
	status = "okay";
    /* 配置extcon属性,用于接收fusb302驱动的UFP/DFP */
  	extcon = <&fusb0>;
};

(2) 使能tcphy0_usb3

/* Enable USB3.0 PHY */
&tcphy0 {
	status = "okay";
};

&tcphy0_usb3{
	status = "okay";
};

同上,如果是linux 4.19的内核,需要在tcphy0节点配置extcon属性。

(3) 使能u2phy0_otg,同时配置USB2.0 OTG PHY0 phy-supply属性,用于控制USB电源VBUS

/* Enable USB2.0 PHY */
&u2phy0 {
     status = "okay";
};

&u2phy0_otg {
	phy-supply = <&vbus_typec>;
	status = "okay";
};

同上,如果是linux 4.19的内核,需要在u2phy0节点配置extcon属性。

1.3.6 总结

USB3.0 Type-C设备树配置的注意点如下:

  • 对应的fusb节点需要配置,因为USB3.0 Type-C需要fusb302芯片;
  • 对应的USB控制器子节点(usbdrd_dwc3)和PHY的节点(tcphyu2phy)都要配置extcon属性;
  • 对应的USB控制器子节点(usbdrd_dwc3)的dr_mode属性要配置为otg

二、 配置内核

2.1 配置USB PHY

linux内核根目录下执行make menuconfig配置以下选项:

Device Drivers  --->
    PHY Subsystem  ---> 
       <*> Rockchip INNO USB2PHY Driver
	   <*> Rockchip TYPEC PHY Driver

USB2.0 PHY使用的是Innosilicon IP,应选择Rockchip INNO USB2PHY Driver,驱动位于drivers/phy/rockchip/phy-rockchip-inno-usb2.c
USB3.0 Type-C PHY使用的是Type-C[Vendor: Cadence],应选择Rockchip TYPEC PHY Driver,驱动位于drivers/phy/rockchip/phy-rockchip-typec.c

2.2 配置USB控制器

linux内核根目录下执行make menuconfig配置以下选项:

Device Drivers  --->
    [*] USB support --->
	   <*> xHCI HCD (USB 3.0) support
       -*- Generic xHCI driver for a platform device
	   <*> EHCI HCD (USB 2.0) support
       -*- Root Hub Transaction Translators
       [*] Improved Transaction Translator scheduling
       <*> Generic EHCI driver for a platform device
       <*> OHCI HCD (USB 1.1) support
       <*> Generic OHCI driver for a platform device
	<*> USB support  -->
	   [*] USB announce new devices(输出识别的每个usb设备的基本信息,比如idVendor、idProduct、制造商、产品、和序列号等)

OHCI HCD (USB 1.1)选择OHCI Driver配置;

EHCI HCD (USB 2.0) 选择EHCI Driver配置;

xHCI HCD (USB 3.0)选择xHCI Driver配置。

2.3 配置USB OTG

默认情况下dw3是双角色(即同时支持HostDevice模式,可以动态切换):

Device Drivers --->
	[*] USB support --->
		<*> DesignWare USB2 DRD Core Support
			DWC2 Mode Selection (Dual Role mode)
		<*> DesignWare USB3 DRD Core Support
			DWC3 Mode Selection (Dual Role mode)

RK3399 USB3.0 OTG使用Synopsys方案,即xHCI扩展的DWC3控制器,因此需要配置DesignWare USB3 DRD Core Support

2.4 配置USB Gadget

由于我们将USB3.0 Type-C接口配置为了USB OTG,作为USB device(丛机)使用,因此我们可以将开发板模拟成一个存储设备、或者声卡、鼠标等设备,然后可以通过开发板上的USB3.0 Type-C接口将PC和开发板连接在一起。

linux内核根目录下执行make menuconfig配置以下选项:

File systems  --->
	Pseudo filesystems  --->
		/* 配置CONFIG_CONFIGFS_FS,为用户空间提供访问配置内核驱动的configfs文件系统 */
		-*- Userspace-driven configuration filesystem  
Device Drivers  --->
    [*] USB support  ---> 
       <*> USB Gadget Support ---> 
		  /* 配置了CONFIG_USB_CONFIGFS、同时会自动配置CONFIG_USB_LIBCOMPOSITE(生成libcomposite.ko,提供USB Gadget Composite框架) */
		  /* 需要注意的是CONFIG_USB_CONFIGFS配置成了模块,下面的usb_f_xxx.ko也会编译成模块,因为他们依赖于libcomposite.ko */
		  /* 这里之所以都选择为模块的形式,是为了调试方便,有些模块,比如U盘加载时还需要提供介质,就是说加载模块时还需要参数,否则加载不上 */
	      <M>  USB Gadget functions configurable through configfs  
          [*]  Generic serial bulk in/out            /* usb_f_serial.ko */                               
		  [*]  Abstract Control Model (CDC ACM)  /* usb_f_acm.ko */  		  
          [*]  Object Exchange Model (CDC OBEX)  /* usb_f_obex.ko */ 
		  [*]  Network Control Model (CDC NCM)   /* usb_f_ncm.ko */  
	      [*]  Ethernet Control Model (CDC ECM)  /* usb_f_ecm.ko */  
		  [*]  Ethernet Control Model (CDC ECM) subset  /* usb_f_ecm_subset.ko */
		  [*]  RNDIS           /* usb_f_rndis.ko */ 
		  [*]  Ethernet Emulation Model (EEM)  /* usb_f_eem.ko */
		  [*]  Mass storage     (大容量存储功能)      /* usb_f_mass_storage.ko */             
		  [ ]  Loopback and sourcesink function (for testing)                                   
	      /* 文件系统功能(FunctionFS)可以让用户在用户空间中创建USB复合功能,就像GadgetFS可以让用户在用户空间中创建USB设备一样 */      
		  [*]  Function filesystem (FunctionFS) /* usb_f_fs.ko */                          
		  [*]  Audio Class 1.0 (音频功能 1.0) /* usb_f_uac1.ko */
		  [*]  Audio Class 1.0 (legacy implementation)  /* usb_f_uac1_legacy.ko */   
		  [*]  Audio Class 2.0 (音频功能 2.0) /* usb_f_uac2.ko */
		  [ ]  MIDI function                                                                   
		  [*]  HID function    (HID功能)    /* usb_f_hid.ko */
		  [*]  USB Webcam functio (USB摄像头功能)    /* usb_f_uvc.ko */
		  [ ]  Printer function  
		  USB Gadget precomposed configurations  --->  /* USB Gadget预配置(位于drivers/usb/gadget/legacy) */
			  <M> Audio Gadget           (音频设备)   /* g_audio.ko */
			  < > Ethernet Gadget (with CDC Ethernet support)     (网络设备)
			  < > Network Control Model (NCM) support     
			  <M> Gadget Filesystem       /* 配置CONFIG_USB_GADGETFS */              
			  <M> Function Filesystem     /* 配置CONFIG_USB_FUNCTIONFS */                                
			  <M> Mass Storage Gadget  (大容量存储设备) /* g_mass_storage.ko */
			  < > Serial Gadget (with CDC ACM and CDC OBEX support)   (串口设备)
			  < > MIDI Gadget                    
			  < > Printer Gadget    (打印机)
			  < > CDC Composite Device (Ethernet and ACM)  
			  < > CDC Composite Device (ACM and mass storage)
			  < > Multifunction Composite Gadget 
			  <M> HID Gadget           (hid设备) /* g_hid.ko */
			  < > EHCI Debug Device Gadget
			  < > USB Webcam Gadget    (USB摄像头)  
			  <*> USB Raw Gadget  (生成设备节点/dev/raw-gadget)

USB Gadget驱动主要包括两大块:

  • USB Gadget functions各种USB子类设备功能接口驱动,位于drivers/usb/gadget/function目录下,里面给出了对应的sample。其作用是配置USB子类协议的接口描述以及其它子类协议,比如uvc协议,hid等;比如我们配置了大容量存储设备驱动,编译完之后,就会在function目录下生成usb_f_mass_storage.ko

  • USB Gadget Legacy:整个gadget设备驱动的入口,位于drivers/usb/gadget/legacy目录下,这个目录下的驱动目前已经过时了,不再建议使用了,以后的linux版本或许会消失:里面给出了常用的USB类设备的驱动sample,其作用就是配置USB设备描述符信息,提供一个usb_composite_driver, 然后注册到composite层;比如我们配置了大容量存储设备驱动,编译完之后,就会在legacy目录下生成g_mass_storage.ko

有关USB Gadget驱动更加具体内容参考《一文搞懂 USB 设备端驱动框架》

下面我们介绍几个Gadget设备驱动:

  • Mass Storage Gadget用于模拟大容量存储设备;需要注意:linux下的SD卡、eMMC文件系统一般是ext4,在windows下不识别;所以如果做实验,可以使用FAT格式的存储器,比如U盘;或者是NFTS格式的移动硬盘;因此我们还需要配置内核支持exFAT/NFTS文件系统;
File systems 
    <*> FUSE (Filesystem in Userspace) support
	DOS/FAT/EXFAT/NT Filesystems  ---> 
	 	 < > MSDOS fs support   
	 	 <*> VFAT (Windows-95) fs support   
	 	 (437) Default codepage for FAT 
	 	 (iso8859-1) Default iocharset for FAT 
	 	 [*] Enable FAT UTF-8 option by default
	 	 <*> exFAT filesystem support 
	 	 (utf8) Default iocharset for exFAT (NEW)
	 	 <*> NTFS file system support
		     [ ] NTFS debugging support
		     [*] NTFS write support
	 	 <*> NTFS Read-Write file system support 
		     [*] 64 bits per NTFS clusters 
  	 	     [*] activate support of external compressions lzx/xpress
             [*] NTFS POSIX Access Control Lists
  • Audio Gadget:用于模拟声卡;USB声卡就是USB接口的外置声卡,一般电脑都自带了声卡,但是内部自带的声卡效果相对来说比较差,不能满足很多HIFI玩家的需求,USB声卡通过USB接口来传递音频数据。具体的ADCDAC过程由声卡完成,摆脱了电脑主板体积的限制,外置USB声卡就可以做的很好。NanoPC-T4开发板搭载了音频解码芯片,因此可以将NanoPC-T4开发板作为一个外置USB声卡。该驱动程序它会自动使用默认音频解码器来播放来自主机机器的音频。

  • HID Gadget:用于模拟人机接口设备,包括键盘、鼠标、操纵杆、扫描仪、数字化板、触摸屏等。

  • Gadget FilesystemUSB GadgetFS提供了一种基于文件系统的API,允许用户程序实现单个配置的USB设备,包括端点I/O和不涉及枚举的控制请求。所有硬件支持的端点、传输速度和传输类型都可以通过readwrite调用进行访问;

  • Function FilesystemUSB FunctionFS允许用户在用户空间中创建USB组合功能,就像GadgetFS允许用户在用户空间中创建USB设备一样。这允许创建组合设备,其中一些功能在内核空间中实现(例如Ethernet、串口或大容量存储),而其他功能在用户空间中实现;

2.5 配置USB外设

根据开发板硬件情况,配置支持各种USB设备。

2.5.1 配置大容量存储设备

移动硬盘属于SCSI设备,所以在配置USB模块之前需要配置SCSI选项;

Device Drivers --->
    SCSI device support --->
        <*> SCSI device support
        [*] legacy /proc/scsi/ support
        *** SCSI support type (disk, tape, CD-ROM) ***
        <*> SCSI disk support
        < > SCSI tape support
        < > SCSI CDROM support
        <*> SCSI generic support
        [*] /dev/bsg support (SG v4)
        <*> SCSI media changer support
        [*] Verbose SCSI error reporting (kernel size +=75K)
        [*] SCSI logging facility
        [*] Asynchronous SCSI scanning
        SCSI Transports --->
        [*] SCSI low-level drivers --->
        [ ] SCSI Device Handlers ----

配置完SCSI Device Support后,可以在USB Support中找到如下选项,选上即可

Device Driver --->
	[*] USB support --->
		<*> USB Mass Storage support
2.5.2 配置USB转串口

(1) 支持USB 3G Modem

USB 3G Modem使用的是USB转串口,使用时需要选上如下选项:

Device Driver --->
	[*] USB support --->
		<*> USB Serial Converter support --->
			<*> USB driver for GSM and CDMA modems

此外,USB 3G Modem还需要使能PPP拨号的相关配置项:

Device Driver --->
	[*] Network device support --->
		<*> PPP (point-to-point protocol) support
        <*> PPP BSD-Compress compression
        <*> PPP Deflate compression
        [*] PPP filtering
        <*> PPP MPPE compression (encryption)
        [*] PPP multilink support
        <*> PPP over Ethernet
        <*> PPP over L2TP
        <*> PPP on L2TP Access Concentrator
        <*> PPP on PPTP Network Server
        <*> PPP support for async serial ports
        <*> PPP support for sync tty ports

(2) 支持PL2303

如果要使用PL2303输出数据到串口,需要选择如下选项,同时需要禁用 USB driver for GSM and CDMA modems,否则,PL2303可能会被误识别为USB 3G modem

Device Driver --->
	[*] USB support --->
		<*> USB Serial Converter support --->
			<*> USB Prolific 2303 Single Port Serial Driver

(3) 支持USB GPS
如果要支持USB GPS,如u-blox 6-GPS Receiver设备,需要选择如下选项:

Device Drivers --->
	[*] USB support --->
		[*] USB Modem (CDC ACM) support
2.5.3 配置HID

在内核中,配置USB键盘、鼠标、触摸屏驱动有多种方式,我们采用其中一种即可。

(1) 通用HID驱动程序

如果要使用USB接口的keyboards(键盘)、mice(鼠标)、joysticks(摇杆)、graphic tablets(绘图板)等其他的HID设备,那么就需要配置USB HID Transport layer(USB人机接口设备传输层)。

USB HID transport layer此选项对应配置项就是CONFIG_USB_HID,也就是通用HID驱动程序。

Device Drivers --->
	[*] HID bus support
		-*-   HID bus core support
			 <*>  Generic HID driver
	    USB HID support  --->
		     <*> USB HID transport layer
			 [ ] PID device support
	 		 [*] /dev/hiddev raw HID device support

USB HID Transport LayerUSB协议栈中的一个组件,用于支持 HID类型的设备与计算机之间的通信。USB HID传输层负责解析来自HID设备的数据,并将其转换为适当的格式,以便应用程序可以理解和使用。

通用HID驱动程序在内核源码的drivers/hid/usbhid/目录下,如下所示:

[root@rk3399 linux-6.3]# ls  drivers/hid/usbhid/
built-in.a  hid-core.o  hiddev.o     hid-pidff.o  Makefile       usbhid.h  usbmouse.c
hid-core.c  hiddev.c    hid-pidff.c  Kconfig      modules.order  usbkbd.c

drivers/hid/usbhid/Makefile

# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the USB input drivers
#

usbhid-y        := hid-core.o
usbhid-$(CONFIG_USB_HIDDEV)     += hiddev.o
usbhid-$(CONFIG_HID_PID)        += hid-pidff.o

obj-$(CONFIG_USB_HID)           += usbhid.o
obj-$(CONFIG_USB_KBD)           += usbkbd.o
obj-$(CONFIG_USB_MOUSE)         += usbmouse.o

如上面Makefile所示,根据Kconfig文件里CONFIG_USB_HID=y或者m,通用HID驱动程序可以编译进内核,也可以编译成usbhid.ko模块,usbhid.ko可以由hid-core.c+hiddev.c+hid-pidff.c组成。

当我们把USB接口的鼠标/键盘接入到设备上时,在/dev/input目录下会生成event*设备,用evtest工具打开/dev/input/event*设备,移动鼠标和点击左/右键或者敲击键盘时会显示出相应的鼠标/键盘事件。

在某些嵌入式设备上,如果不想为USB鼠标、键盘使用通用的HID驱动程序,而更喜欢在其有限的Boot Protocol模式下使用鼠标/键盘时,可以使用usbmouse.c/usbkbd.c作为鼠标和键盘驱动程序。

关于USB鼠标我们之前有单独的文章介绍:linux驱动移植-usb鼠标接口驱动

关于USB键盘我们之前有单独的文章介绍:linux驱动移植-usb键盘接口驱动

(2) Generic input layer

实际上在linux中还有一个通用输入层子系统,用于处理和管理各种输入设备,例如键盘、鼠标、触摸屏和游戏手柄等。通用输入层提供了一个统一的接口,使应用程序可以以一致的方式与不同类型的输入设备进行交互,配置如下:

Device Drivers  --->
   Input device support  
      -*- Generic input layer (needed for keyboard, mouse, ...) 

那他们之间有什么区别呢?

区别在于:

  • Generic Input LayerLinux内核的一个通用输入设备管理层,它与各种输入设备进行交互,并将输入数据传递给应用程序;

  • USB HID Transport Layer则是USB协议栈中的一个特定组件,专门用于处理HID设备的通信和数据传输;

通用输入层可以与各种输入设备一起使用(不仅限于USB HID设备),而USB HID传输层则专注于USB HID设备的通信协议。针对于USB输入设备,我们配置USB HID transport layer即可。

2.5.4 配置USB摄像头
Device Drivers -->
	--- Multimedia support 
	    [ ]  Filter media drivers  (取消选择)
        Media core support  --->
		    <*> Video4Linux core (NEW) 
		    [*] Media Controller API (NEW) 
	    Media drivers  --->
			[*] Media USB Adapters(启用usb总线的媒体驱动程序,drivers/media/usb)
				<*> USB Video Class(UVC)(我们的摄像头支持UVC,选择这个驱动即可)
				[*] UVC input events device  supports(NEW)
				<> GSPCA based webcams --> (GSPCA 是一个法国程序员在业余时间制作的一个万能USB 摄像头驱动程序,在此可以选择对应类型USB摄像头的支持)
				<> SONIX Bayer USB Camer Driver (NEW)
				<> OV772x/OV965x/... 系列摄像头支持
				<> ....  
        	[*] Media platform devices (NEW)  --->
				[*]V4L platform devices

关于USB摄像头我们之前有单独的文章介绍:linux驱动移植-usb摄像头uvc驱动

2.5.5 配置USB音频
Device Driver --->
	<*> Sound card support --->
		<*> Advanced Linux Sound Architecture --->
			[*] USB sound devices --->
				[*] USB Audio/MIDI driver
2.5.6 配置USB HUB

如果要支持USB HUB,请将Disable external hubs配置选项去掉;

Device Drivers --->
	[*] USB support --->
		[ ] Disable external hubs

其它有可能用到的USB设备还有很多,如GPSPrinter等,有可能需要Vendor定制的驱动,也有可能
是标准的 设备类驱动,如需支持这类设备,可直接在网络上搜索linux对该设备支持要做的工作。

2.6 配置extcon

2.6.1 linux 6.3配置

配置Fairchild FUSB302 Type-C chip driver驱动,将drivers/usb/typec/tcpm/fusb302.c编译到内核;

Device Driver --->
    -*- External Connector Class (extcon) support  --->
	<*> USB support --->
		<*> USB Type-C Support --->
			[*] USB Type-C Port Controller Manager --->
			[*] Fairchild FUSB302 Type-C chip driver
2.6.2 linux 4.19配置

如果你使用的是linux 4.19内核,则配置Fairchild fusb302 Support驱动,将drivers/mfd/fusb302.c编译到内核;

Device Driver --->
	<*> Fairchild fusb302 Support

drivers/mfd/fusb302.c驱动通过注册extcon设备来识别是接入了USB主机或USB设备。

drivers/mfd/fusb302.cprobe函数:

点击查看代码
static int fusb30x_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
{
        struct fusb30x_chip *chip;
        struct PD_CAP_INFO *pd_cap_info;
        int ret;
        char *string[2];

        chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
        if (!chip)
                return -ENOMEM;

        if (fusb30x_port_used == 0xff)
                return -1;

        chip->port_num = fusb30x_port_used++;
        fusb30x_port_info[chip->port_num] = chip;

        chip->dev = &client->dev;
        chip->regmap = devm_regmap_init_i2c(client, &fusb302_regmap_config);
        if (IS_ERR(chip->regmap)) {
                dev_err(&client->dev, "Failed to allocate regmap!\n");
                return PTR_ERR(chip->regmap);
        }

        ret = fusb_initialize_gpio(chip);
        if (ret)
                return ret;

        fusb_initialize_timer(chip);

        chip->fusb30x_wq = create_workqueue("fusb302_wq");
        INIT_WORK(&chip->work, fusb302_work_func);

        chip->role = ROLE_MODE_NONE;
        chip->try_role = ROLE_MODE_NONE;
        if (!of_property_read_string(chip->dev->of_node, "fusb302,role",
                                     (const char **)&string[0])) {
                if (!strcmp(string[0], "ROLE_MODE_DRP"))
                        chip->role = ROLE_MODE_DRP;
                else if (!strcmp(string[0], "ROLE_MODE_DFP"))
                        chip->role = ROLE_MODE_DFP;
                else if (!strcmp(string[0], "ROLE_MODE_UFP"))
                        chip->role = ROLE_MODE_UFP;
        }
        if (chip->role == ROLE_MODE_NONE) {
                dev_warn(chip->dev,
                         "Can't get property of role, set role to default DRP\n");
                chip->role = ROLE_MODE_DRP;
                string[0] = "ROLE_MODE_DRP";
        }

        if (!of_property_read_string(chip->dev->of_node, "fusb302,try_role",
                                     (const char **)&string[1])) {
                if (!strcmp(string[1], "ROLE_MODE_DFP"))
                        chip->try_role = ROLE_MODE_DFP;
                else if (!strcmp(string[1], "ROLE_MODE_UFP"))
                        chip->try_role = ROLE_MODE_UFP;
        }

        if (chip->try_role == ROLE_MODE_NONE)
                string[1] = "ROLE_MODE_NONE";

        chip->vconn_supported = true;
        tcpm_init(chip);
        tcpm_set_rx_enable(chip, 0);
        chip->conn_state = unattached;
        tcpm_set_cc(chip, chip->role);

        chip->n_caps_used = 1;
        chip->source_power_supply[0] = 0x64;
        chip->source_max_current[0] = 0x96;

        pd_cap_info = &chip->pd_cap_info;
        pd_cap_info->dual_role_power = 1;
        pd_cap_info->data_role_swap = 1;

        pd_cap_info->externally_powered = 1;
        pd_cap_info->usb_suspend_support = 0;
        pd_cap_info->usb_communications_cap = 0;
        pd_cap_info->supply_type = 0;
        pd_cap_info->peak_current = 0;

        chip->extcon = devm_extcon_dev_allocate(&client->dev, fusb302_cable);
        if (IS_ERR(chip->extcon)) {
                dev_err(&client->dev, "allocat extcon failed\n");
                return PTR_ERR(chip->extcon);
        }

        ret = devm_extcon_dev_register(&client->dev, chip->extcon);
        if (ret) {
                dev_err(&client->dev, "failed to register extcon: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
                                             EXTCON_PROP_USB_TYPEC_POLARITY);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set USB property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
                                             EXTCON_PROP_USB_TYPEC_POLARITY);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set USB_HOST property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
                                             EXTCON_PROP_USB_TYPEC_POLARITY);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set DISP_DP property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_USB,
                                             EXTCON_PROP_USB_SS);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set USB USB_SS property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_USB_HOST,
                                             EXTCON_PROP_USB_SS);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set USB_HOST USB_SS property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_DISP_DP,
                                             EXTCON_PROP_USB_SS);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set DISP_DP USB_SS property capability: %d\n",
                        ret);
                return ret;
        }

        ret = extcon_set_property_capability(chip->extcon, EXTCON_CHG_USB_FAST,
                                             EXTCON_PROP_USB_TYPEC_POLARITY);
        if (ret) {
                dev_err(&client->dev,
                        "failed to set USB_PD property capability: %d\n", ret);
                return ret;
        }

        i2c_set_clientdata(client, chip);

        spin_lock_init(&chip->irq_lock);
        chip->enable_irq = 1;

        chip->gpio_int_irq = gpiod_to_irq(chip->gpio_int);
        if (chip->gpio_int_irq < 0) {
                dev_err(&client->dev,
                        "Unable to request IRQ for INT_N GPIO! %d\n",
                        ret);
                ret = chip->gpio_int_irq;
                goto IRQ_ERR;
        }

        ret = devm_request_threaded_irq(&client->dev,
                                        chip->gpio_int_irq,
                                        NULL,
                                        cc_interrupt_handler,
                                        IRQF_ONESHOT | IRQF_TRIGGER_LOW,
                                        client->name,
                                        chip);
        if (ret) {
                dev_err(&client->dev, "irq request failed\n");
                goto IRQ_ERR;
        }

        dev_info(chip->dev,
                 "port %d probe success with role %s, try_role %s\n",
                 chip->port_num, string[0], string[1]);

        chip->input = devm_input_allocate_device(&client->dev);
        if (!chip->input) {
                dev_err(chip->dev, "Can't allocate input dev\n");
                ret = -ENOMEM;
                goto IRQ_ERR;
        }

        chip->input->name = "Typec_Headphone";
        chip->input->phys = "fusb302/typec";

        input_set_capability(chip->input, EV_SW, SW_HEADPHONE_INSERT);

        ret = input_register_device(chip->input);
        if (ret) {
                dev_err(chip->dev, "Can't register input device: %d\n", ret);
                goto IRQ_ERR;
        }
        return 0;
IRQ_ERR:
        destroy_workqueue(chip->fusb30x_wq);
        return ret;
}

2.7 保存配置

配置完内核之后记得保存配置:

img

存档:

root@zhengyang:/work/sambashare/rk3399/linux-6.3# mv rk3399_defconfig ./arch/arm64/configs/

重新配置内核(如果不想重新编译内核,可以存档一份到.config):

root@zhengyang:/work/sambashare/rk3399/linux-6.3# make rk3399_defconfig

2.8 烧录内核

2.8.1 编译内核

linux内核根目录下执行如下命令进行编译内核:

root@zhengyang:/work/sambashare/rk3399/linux-6.3# make -j8

u-boot-2023.04路径下的mkimage工具拷贝过来,然后在命令行使用mkimage工具编译即可:

root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../u-boot-2023.04/tools/mkimage ./
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ./mkimage -f kernel.its kernel.itb
2.8.2 通过tftp烧录内核

给开发板上电,同时连接上网线,进入uboot命令行。我们将内核拷贝到tftp文件目录:

root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp kernel.itb /work/tftpboot/

接着给开发板上电。通过uboot命令行将kernel.itb下到内存地址0x10000000处:

=> tftp 0x10000000 kernel.itb

通过mmc write命令将内核镜像烧录到eMMC0x8000个扇区处:

=> mmc erase 0x8000 0xA000
=> mmc write 0x10000000 0x8000 0xA000
2.8.3 启动内核

我们重新启动开发板,我们会看到与USB相关的日志:

[    1.829924] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
[    1.830411] ehci-platform fe380000.usb: EHCI Host Controller
[    1.830442] ohci-platform fe3a0000.usb: Generic Platform OHCI controller
[    1.830485] ohci-platform fe3a0000.usb: new USB bus registered, assigned bus number 1
[    1.830590] ehci-platform fe3c0000.usb: EHCI Host Controller
[    1.830609] ohci-platform fe3e0000.usb: Generic Platform OHCI controller
[    1.830621] ehci-platform fe3c0000.usb: new USB bus registered, assigned bus number 2
[    1.830638] ohci-platform fe3e0000.usb: new USB bus registered, assigned bus number 3
[    1.830658] ohci-platform fe3a0000.usb: irq 53, io mem 0xfe3a0000
[    1.830761] ehci-platform fe3c0000.usb: irq 52, io mem 0xfe3c0000
[    1.830789] ohci-platform fe3e0000.usb: irq 54, io mem 0xfe3e0000
[    1.836166] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 4
[    1.842483] ehci-platform fe380000.usb: new USB bus registered, assigned bus number 5
[    1.850092] xhci-hcd xhci-hcd.0.auto: hcc params 0x0220fe64 hci version 0x110 quirks 0x0000000002010010
[    1.853065] ehci-platform fe3c0000.usb: USB 2.0 started, EHCI 1.00
[    1.853516] usb usb2: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.03
[    1.853532] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.853544] usb usb2: Product: EHCI Host Controller
[    1.853553] usb usb2: Manufacturer: Linux 6.3.0 ehci_hcd
[    1.853562] usb usb2: SerialNumber: fe3c0000.usb
[    1.854382] hub 2-0:1.0: USB hub found
[    1.854450] hub 2-0:1.0: 1 port detected
[    1.858836] ehci-platform fe380000.usb: irq 51, io mem 0xfe380000
[    1.865071] xhci-hcd xhci-hcd.0.auto: irq 49, io mem 0xfe800000
[    1.885063] ehci-platform fe380000.usb: USB 2.0 started, EHCI 1.00
[    1.890257] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
[    1.897120] usb usb5: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.03
[    2.023756] usb usb5: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.031870] usb usb5: Product: EHCI Host Controller
[    2.037347] usb usb5: Manufacturer: Linux 6.3.0 ehci_hcd
[    2.043306] usb usb5: SerialNumber: fe380000.usb
[    2.049117] hub 5-0:1.0: USB hub found
[    2.053375] hub 5-0:1.0: 1 port detected
[    2.058339] usb usb1: New USB device found, idVendor=1d6b, idProduct=0001, bcdDevice= 6.03
[    2.067630] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.075719] usb usb1: Product: Generic Platform OHCI controller
[    2.082361] usb usb1: Manufacturer: Linux 6.3.0 ohci_hcd
[    2.088316] usb usb1: SerialNumber: fe3a0000.usb
[    2.094011] hub 1-0:1.0: USB hub found
[    2.098273] hub 1-0:1.0: 1 port detected
[    2.103093] xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 6
[    2.103266] usb usb3: New USB device found, idVendor=1d6b, idProduct=0001, bcdDevice= 6.03
[    2.111691] xhci-hcd xhci-hcd.0.auto: Host supports USB 3.0 SuperSpeed
[    2.120951] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.136415] usb usb3: Product: Generic Platform OHCI controller
[    2.143068] usb usb3: Manufacturer: Linux 6.3.0 ohci_hcd
[    2.149052] usb usb3: SerialNumber: fe3e0000.usb
[    2.154791] hub 3-0:1.0: USB hub found
[    2.159046] hub 3-0:1.0: 1 port detected
[    2.164005] usb usb4: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.03
[    2.173296] usb usb4: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.181386] usb usb4: Product: xHCI Host Controller
[    2.186857] usb usb4: Manufacturer: Linux 6.3.0 xhci-hcd
[    2.192813] usb usb4: SerialNumber: xhci-hcd.0.auto
[    2.198818] hub 4-0:1.0: USB hub found
[    2.203079] hub 4-0:1.0: 1 port detected
[    2.207872] usb usb6: We don't know the algorithms for LPM for this host, disabling LPM.
[    2.217093] usb usb6: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 6.03
[    2.226375] usb usb6: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.234485] usb usb6: Product: xHCI Host Controller
[    2.239956] usb usb6: Manufacturer: Linux 6.3.0 xhci-hcd
[    2.245911] usb usb6: SerialNumber: xhci-hcd.0.auto
[    2.251953] hub 6-0:1.0: USB hub found
[    2.256206] hub 6-0:1.0: 1 port detected
[    2.261191] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[    2.267479] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 7
[    2.276205] xhci-hcd xhci-hcd.1.auto: hcc params 0x0220fe64 hci version 0x110 quirks 0x0000000002010010
[    2.286876] xhci-hcd xhci-hcd.1.auto: irq 50, io mem 0xfe900000
[    2.293711] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
[    2.299883] xhci-hcd xhci-hcd.1.auto: new USB bus registered, assigned bus number 8
[    2.308563] xhci-hcd xhci-hcd.1.auto: Host supports USB 3.0 SuperSpeed
[    2.316043] usb usb7: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.03
[    2.325407] usb usb7: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.333513] usb usb7: Product: xHCI Host Controller
[    2.338977] usb usb7: Manufacturer: Linux 6.3.0 xhci-hcd
[    2.345017] usb usb7: SerialNumber: xhci-hcd.1.auto
[    2.351075] hub 7-0:1.0: USB hub found
[    2.355332] hub 7-0:1.0: 1 port detected
[    2.360123] usb usb8: We don't know the algorithms for LPM for this host, disabling LPM.
[    2.369320] usb usb8: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 6.03
[    2.378593] usb usb8: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    2.386698] usb usb8: Product: xHCI Host Controller
[    2.392169] usb usb8: Manufacturer: Linux 6.3.0 xhci-hcd
[    2.398123] usb usb8: SerialNumber: xhci-hcd.1.auto
[    2.404138] hub 8-0:1.0: USB hub found
[    2.408398] hub 8-0:1.0: 1 port detected
[    2.414435] usbcore: registered new interface driver usb-storage
[    2.421325] usbcore: registered new interface driver pl2303
[    2.427613] usbserial: USB Serial support registered for pl2303
[    3.598344] usbcore: registered new interface driver uvcvideo
[    3.718570] usbcore: registered new interface driver usbhid
[    3.730651] usbhid: USB HID core driver
[    3.744550] usbcore: registered new interface driver snd-usb-audio

从上面日志上可以看到总共注册了8个USB总线,编号依次从1~8。每个USB总线上对应一个USB控制器,一共有8个USB控制器;

  • ohci-platform fe3a0000.usbUSB总线编号为1,对应设备节点usb_host0_ohci,对应开发板上接口为USBH2

  • ehci-platform fe3c0000.usbUSB总线编号为2,对应设备节点usb_host1_ehci,对应开发板上接口为USBH3

  • ohci-platform fe3e0000.usbUSB总线编号为3,对应设备节点usb_host1_ohci,对应开发板上接口为USBH3

  • xhci-hcd xhci-hcd.0.auto(fe800000.usb)USB总线编号为4,对应设备节点usbdrd3_0,对应开发板上接口为Type-C/DP

  • ehci-platform fe380000.usbUSB总线编号为5,对应设备节点usb_host0_ehci,对应开发板上接口为USBH2

  • xhci-hcd xhci-hcd.0.auto(fe800000.usb)USB总线编号为6,对应设备节点usbdrd3_0,对应开发板上接口为Type-C/DP

  • xhci-hcd xhci-hcd.1.auto(fe900000.usb)USB总线编号为7,对应设备节点usbdrd3_1,对应开发板上接口为CON5

  • xhci-hcd xhci-hcd.1.auto(fe900000.usb)USB总线编号为8,对应设备节点usbdrd3_1,对应开发板上接口为CON5

三、测试USB设备

我们接下来在开发板USB2.0 Host Type-AUSB3.0 Host Type-A接口插入不同的USB设备进行测试。

3.1 测试USB鼠标

我们将USB鼠标接到开发板其中一个USB2.0 Host Type-A接口(对应开发板上的USBH3), 我们可以在内核日志中,找到USB鼠标信息的日志:

[ 1457.916575] usb 3-1: new full-speed USB device number 2 using ohci-platform
[ 1458.149684] usb 3-1: New USB device found, idVendor=046d, idProduct=c52b, bcdDevice=24.11
[ 1458.149723] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1458.149740] usb 3-1: Product: USB Receiver
[ 1458.149754] usb 3-1: Manufacturer: Logitech
[ 1458.161331] input: Logitech USB Receiver as /devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.0/0003:046D:C52B.0001/input/input1
[ 1458.221766] hid-generic 0003:046D:C52B.0001: input: USB HID v1.11 Keyboard [Logitech USB Receiver] on usb-fe3e0000.usb-1/input0
[ 1458.230238] input: Logitech USB Receiver Mouse as /devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.1/0003:046D:C52B.0002/input/input2
[ 1458.230914] input: Logitech USB Receiver Consumer Control as /devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.1/0003:046D:C52B.0002/input/input3
[ 1458.289373] input: Logitech USB Receiver System Control as /devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.1/0003:046D:C52B.0002/input/input4
[ 1458.290293] hid-generic 0003:046D:C52B.0002: input,hiddev96: USB HID v1.11 Mouse [Logitech USB Receiver] on usb-fe3e0000.usb-1/input1
[ 1458.297793] hid-generic 0003:046D:C52B.0003: hiddev97: USB HID v1.11 Device [Logitech USB Receiver] on usb-fe3e0000.usb-1/input2

此时可以在ubuntu桌面系统测试鼠标,可以发现是可以正常使用的。

USB鼠标工作在全速模式下,OHCI控制器,USB1.1通信协议,使用的USB总线编号为3,设备编号为2。

日志开头表示的是:"驱动模块的名字: + 具体的信息",其中:

  • input: 描述的是将鼠标作为输入设备注册到内核,对应事件类输入设备节点/dev/input/event2/dev/input/event3/dev/input/event4
  • hid-generic 0003:046D:C52B.xxxx:鼠标匹配的驱动是hid-generic驱动,这里可以看到注册了两个hid设备节点:hiddev96hiddev97

3.2 测试USB键盘

(1) 我们将USB键盘接到开发板另一个USB2.0 Host Type-A接口(对应开发板上的USBH2),在内核日志中,找到USB键盘信息的日志:

[ 1619.954338] usb 1-1: new low-speed USB device number 2 using ohci-platform
[ 1620.183564] usb 1-1: New USB device found, idVendor=1a2c, idProduct=4d7e, bcdDevice= 1.10
[ 1620.183603] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1620.183620] usb 1-1: Product: USB Keyboard
[ 1620.183634] usb 1-1: Manufacturer: SEMICO
[ 1620.193197] input: SEMICO USB Keyboard as /devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.0/0003:1A2C:4D7E.0004/input/input6
[ 1620.251611] hid-generic 0003:1A2C:4D7E.0004: input: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-fe3a0000.usb-1/input0
[ 1620.263525] input: SEMICO USB Keyboard Consumer Control as /devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.1/0003:1A2C:4D7E.0005/input/input7
[ 1620.323415] input: SEMICO USB Keyboard System Control as /devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.1/0003:1A2C:4D7E.0005/input/input8
[ 1620.324115] input: SEMICO USB Keyboard as /devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.1/0003:1A2C:4D7E.0005/input/input10
[ 1620.324871] hid-generic 0003:1A2C:4D7E.0005: input,hiddev98: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-fe3a0000.usb-1/input1

此时可以在ubuntu桌面系统测试键盘,可以发现是可以正常使用的。

USB键盘工作在低速模式下,OHCI控制器,USB1.0通信协议,使用的USB总线编号为1,设备编号为2。

这里可以看到注册了1个hid设备节点:hiddev98

(2) 拔掉USB键盘,尝试将USB键盘接到USB3.0 Host Type-A接口(对应开发板上的CON5),在内核日志中,找到USB键盘信息的日志:

[ 1665.652164] usb 1-1: USB disconnect, device number 2
[ 1678.668389] usb 7-1: new low-speed USB device number 2 using xhci-hcd
[ 1678.822673] usb 7-1: New USB device found, idVendor=1a2c, idProduct=4d7e, bcdDevice= 1.10
[ 1678.822713] usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1678.822729] usb 7-1: Product: USB Keyboard
[ 1678.822743] usb 7-1: Manufacturer: SEMICO
[ 1678.850429] input: SEMICO USB Keyboard as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1:1.0/0003:1A2C:4D7E.0006/input/input11
[ 1678.909715] hid-generic 0003:1A2C:4D7E.0006: input: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-xhci-hcd.1.auto-1/input0
[ 1678.921243] input: SEMICO USB Keyboard Consumer Control as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1:1.1/0003:1A2C:4D7E.0007/input/input12
[ 1678.981216] input: SEMICO USB Keyboard System Control as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1:1.1/0003:1A2C:4D7E.0007/input/input13
[ 1678.981907] input: SEMICO USB Keyboard as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1:1.1/0003:1A2C:4D7E.0007/input/input15
[ 1678.982716] hid-generic 0003:1A2C:4D7E.0007: input,hiddev98: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-xhci-hcd.1.auto-1/input1

此时可以在ubuntu桌面系统测试键盘,可以发现是可以正常使用的。

USB键盘工作在低速模式下,xHCI控制器,USB1.0通信协议,使用的USB总线编号为7,设备编号为2。

3.3 测试USB摄像头

我们将USB摄像头接到开发板的USB3.0 Host Type-A接口(对应开发板上的CON5),在内核日志中,找到USB摄像头信息的日志:

[ 2305.074204] usb 7-1: USB disconnect, device number 2
[ 2323.296616] usb 7-1: new high-speed USB device number 3 using xhci-hcd
[ 2323.483982] usb 7-1: New USB device found, idVendor=0c45, idProduct=6340, bcdDevice= 0.00
[ 2323.484022] usb 7-1: New USB device strings: Mfr=2, Product=1, SerialNumber=0
[ 2323.484038] usb 7-1: Product: USB 2.0 Camera
[ 2323.484052] usb 7-1: Manufacturer: Sonix Technology Co., Ltd.
[ 2323.495745] usb 7-1: Found UVC 1.00 device USB 2.0 Camera (0c45:6340)
[ 2323.519918] input: USB 2.0 Camera: USB Camera as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1:1.0/input/input16

USB键盘工作在高速模式下,xHCI控制器,USB2.0通信协议,使用的USB总线编号为7,设备编号为2。

从上面的信息可以看到这里,已经识别到了我们的USB摄像头,VID=0c45PID=6340

安装应用程序茄子:

root@rk3399:/# sudo apt-get install cheese

ubuntu输入cheese命令捕捉视频:

root@rk3399:/# cheese

如图所示:

3.4 测试移动硬盘

准备好一个移动硬盘,注意移动硬盘要为NFTS格式的!NTFSexFAT由于版权问题所以在Linux下支持的不完善,操作的话可能会有问题,比如只能读,不能写或者无法识别等。

准备好以后将移动硬盘插入到开发板USB3.0 Host Type-A接口(对应开发板上的CON5),在内核日志中,找到移动硬盘信息的日志:

[ 3341.772611] usb 7-1: USB disconnect, device number 3
[ 3393.909693] usb 8-1: new SuperSpeed USB device number 2 using xhci-hcd
[ 3393.932045] usb 8-1: New USB device found, idVendor=0480, idProduct=b200, bcdDevice= 1.00
[ 3393.932085] usb 8-1: New USB device strings: Mfr=2, Product=3, SerialNumber=1
[ 3393.932102] usb 8-1: Product: External USB 3.0
[ 3393.932115] usb 8-1: Manufacturer: TOSHIBA
[ 3393.932129] usb 8-1: SerialNumber: 2018010302552D
[ 3393.936501] usb-storage 8-1:1.0: USB Mass Storage device detected
[ 3393.937468] scsi host0: usb-storage 8-1:1.0
[ 3394.970703] scsi 0:0:0:0: Direct-Access     TOSHIBA  External USB 3.0 0    PQ: 0 ANSI: 6
[ 3394.972008] sd 0:0:0:0: Attached scsi generic sg0 type 0
[ 3394.973469] sd 0:0:0:0: [sda] Spinning up disk...
[ 3395.993613] ...ready
[ 3398.068783] sd 0:0:0:0: [sda] 1953525168 512-byte logical blocks: (1.00 TB/932 GiB)
[ 3398.069873] sd 0:0:0:0: [sda] Write Protect is off
[ 3398.069891] sd 0:0:0:0: [sda] Mode Sense: 43 00 00 00
[ 3398.071119] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[ 3398.114863]  sda: sda1 sda2 sda3 sda4 < sda5 >
[ 3398.116779] sd 0:0:0:0: [sda] Attached SCSI disk

移动硬盘工作在超高速模式下,xHCI控制器,USB3.0通信协议,使用的USB总线编号为8,设备编号为2。

可以看出,系统检测到移动硬盘插入,大小为1TB,对应的设备文件为/dev/sda/dev/sda1~5,大家可以查看一下/dev目录下有没有sdasda1~5这6个文件;

root@rk3399:/# ll /dev/sda*
brw-rw---- 1 root disk 8, 0 Sep 16 15:28 /dev/sda
brw-rw---- 1 root disk 8, 1 Sep 16 15:28 /dev/sda1
brw-rw---- 1 root disk 8, 2 Sep 16 15:28 /dev/sda2
brw-rw---- 1 root disk 8, 3 Sep 16 15:28 /dev/sda3
brw-rw---- 1 root disk 8, 4 Sep 16 15:28 /dev/sda4
brw-rw---- 1 root disk 8, 5 Sep 16 15:28 /dev/sda5

其中:

  • /dev/sda:是整个移动硬盘;
  • /dev/sda1~5: 是移动硬盘的第1个到第5个分区,这是是由于我在PC上已经将移动硬盘分了5个分区;

(1) 要想访问移动硬盘我们需要先对移动硬盘进行挂载,理论上挂载到任意一个目录下都可以.

测试发现ubuntu系统会自动将移动硬盘下的各个分区挂载到/media/root下;

root@rk3399:/media/root# df -T
Filesystem     Type     1K-blocks     Used Available Use% Mounted on
/dev/root      ext4      14801956  4998424   9143588  36% /
devtmpfs       devtmpfs   1950140        0   1950140   0% /dev
tmpfs          tmpfs      1972572        0   1972572   0% /dev/shm
tmpfs          tmpfs       394516     1760    392756   1% /run
tmpfs          tmpfs         5120        4      5116   1% /run/lock
tmpfs          tmpfs      1972572        0   1972572   0% /sys/fs/cgroup
tmpfs          tmpfs       394512       16    394496   1% /run/user/0
/dev/sda3      fuseblk  209715196 14878708 194836488   8% /media/root/小白PE工具盘1
/dev/sda5      fuseblk  347613180 14758876 332854304   5% /media/root/小白PE工具盘
/dev/sda2      fuseblk  209715196 42328260 167386936  21% /media/root/程序
/dev/sda1      fuseblk  209715196 13973916 195741280   7% /media/root/学业

(2) 这里我创建一个/mnt/usb_disk目录,然后将移动硬盘第一个分区挂载到/mnt/usb_disk目录下,命令如下:

root@rk3399:/# mkdir -p /mnt/usb_disk 
root@rk3399:/# mount /dev/sda1 /mnt/usb_disk/ -t ntfs -o iocharset=utf8 

其中:

  • -t ntfs:指定挂载所使用的文件系统类型,这里设置为ntfs,也就是NTFS文件系统;
  • -o iocharset=utf8:设置硬盘编码格式为utf8,否则的话移动硬盘里面的中文会显示乱码;

如果挂载的时候出现如下错误:

root@rk3399:/# mount /dev/sda1 /mnt/usb_disk/ -t ntfs -o iocharset=utf8
Mount is denied because the NTFS volume is already exclusively opened.
The volume may be already mounted, or another software may use it which
could be identified for example by the help of the 'fuser' command.

通过错误语句的输出可以知道NTFS卷被拒绝是因为已经执行打开,可能已经被挂载或者有应用程序正在使用它,可以使用fuser命令显示正在使用指定的filefile system或者socket的进程信息。

fuser -m -u /dev/sda1 使用-m -u显示正在使用/dev/sda1的进程PID以及用户名称,如我输入上述命令后显示的情况:

root@rk3399:/# fuser -m -u /dev/sda1
/dev/sda1:            5168(root)
root@rk3399:/# kill 5168

表明是PID为5168的进程正在使用它,可以使用kill命令杀死该进程,此时在使用mount命令就不会出错了。

挂载成功以后进入到/mnt/usb_disk目录下,输入ls命令查看移动硬盘文件;

root@rk3399:/# cd /mnt/usb_disk/
root@rk3399:/mnt/usb_disk# ll
total 60
drwxrwxrwx 1 root root  8192 Jun 22  2021 '$RECYCLE.BIN'/
drwxrwxrwx 1 root root 12288 Jun 22  2021  ./
drwxr-xr-x 3 root root  4096 Sep 16 15:37  ../
drwxrwxrwx 1 root root  4096 Jun 22  2021  1.led/
drwxrwxrwx 1 root root  4096 Jun 22  2021  3.myboot/
drwxrwxrwx 1 root root  4096 Jun 22  2021  4.mmu/
drwxrwxrwx 1 root root  4096 Jun 22  2021  5.nadflash/
drwxrwxrwx 1 root root  8192 Jun 22  2021  6.interupt/
drwxrwxrwx 1 root root  4096 Jun 22  2021  7.uart/
drwxrwxrwx 1 root root  4096 Jun 22  2021  8.do_bootm_linux/
drwxrwxrwx 1 root root  4096 Dec 22  2021 'System Volume Information'/

至此移动硬盘就能正常读写操作了,直接对/mnt/usb_disk目录进行操作就行了。如果要拔出移动硬盘要执行一个sync命令进行同步,然后在使用unmount进行移动硬盘卸载,命令如下所示:

root@rk3399:/mnt/usb_disk# sync 		
root@rk3399:/mnt/usb_disk# cd / 		
root@rk3399:/# umount /mnt/usb_disk 

3.5 测试Type-C触摸屏

现在我手里有一个NanoPC-T4开发板,同时还有一个15.6英寸HDMI接口显示屏,并且该显示屏支持Type-C十点触摸。

(1) 配置USB3.0 OTG0的工作模式为Host(主机),这种做法只会临时有效:

root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
otg
root@rk3399:/lib/modules/6.3.0# echo host > /sys/kernel/debug/usb/fe800000.usb/mode
root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
host

如果想永久有效,可以通过修改设备树dr-mode = host将工作配置为Host(主机);具体可以参考:Rockchip RK3399 - USB触摸屏接口驱动》

(2) 这里我们通过Type-C线(两端都是Type-C接口)将开发板USB3.0 Type-C接口(对应开发板上的Type-C/DP)与触摸屏的Type-C接口连接起来。内核输出日志信息:

[ 2358.710215] usb 4-1: new full-speed USB device number 2 using xhci-hcd
[ 2358.862485] usb 4-1: New USB device found, idVendor=222a, idProduct=0001, bcdDevice= 1.00
[ 2358.862525] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 2358.862542] usb 4-1: Product: SingWon-CTP-V1.18A
[ 2358.862555] usb 4-1: Manufacturer: UsbHID
[ 2358.862569] usb 4-1: SerialNumber: 6F6A099B1133
[ 2358.899245] input: UsbHID SingWon-CTP-V1.18A as /devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb4/4-1/4-1:1.0/0003:222A:0001.0003/input/input6
[ 2358.899571] hid-generic 0003:222A:0001.0003: input: USB HID v1.11 Device [UsbHID SingWon-CTP-V1.18A] on usb-xhci-hcd.0.auto-1/input0
[ 2358.903930] input: UsbHID SingWon-CTP-V1.18A as /devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb4/4-1/4-1:1.1/0003:222A:0001.0004/input/input7
[ 2358.966870] hid-generic 0003:222A:0001.0004: input: USB HID v1.12 Keyboard [UsbHID SingWon-CTP-V1.18A] on usb-xhci-hcd.0.auto-1/input1

此时可以在ubuntu桌面系统测试触摸屏,可以发现触摸屏可以正常实用。

USB触摸屏工作在全速模式下,xHCI控制器,USB1.1通信协议,使用的USB总线编号为4,设备编号为2。

3.6 测试USB 4口HUB集线器

这里我们将USB 4口HUB集线器里连接到开发板USB3.0 Host Type-A接口(对应开发板上的CON5)。内核输出日志信息:

[  232.825153] usb 7-1: new high-speed USB device number 4 using xhci-hcd
[  232.975149] usb 7-1: New USB device found, idVendor=14cd, idProduct=8601, bcdDevice= 0.00
[  232.975188] usb 7-1: New USB device strings: Mfr=1, Product=3, SerialNumber=0
[  232.975204] usb 7-1: Product: USB 2.0 Hub
[  232.975218] usb 7-1: Manufacturer: USB Device
[  233.011052] hub 7-1:1.0: USB hub found
[  233.011213] hub 7-1:1.0: 4 ports detected

USB键盘工作在高速模式下,xHCI控制器,USB2.0通信协议,使用的USB总线编号为7,设备编号为4。

从上面的信息可以看到这里,已经识别到了我们的USB 4口HUB集线器,VID=14cdPID=8601,并且有4个端口。

我们尝试在将USB鼠标插入HUB其中一个端口,内核输出日志信息:

[  450.823397] usb 7-1.1: new full-speed USB device number 5 using xhci-hcd
[  450.928638] usb 7-1.1: New USB device found, idVendor=046d, idProduct=c52b, bcdDevice=24.11
[  450.928677] usb 7-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  450.928694] usb 7-1.1: Product: USB Receiver
[  450.928707] usb 7-1.1: Manufacturer: Logitech
[  451.031478] input: Logitech USB Receiver as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1.1/7-1.1:1.0/0003:046D:C52B.0006/input/input11
[  451.092719] hid-generic 0003:046D:C52B.0006: input: USB HID v1.11 Keyboard [Logitech USB Receiver] on usb-xhci-hcd.1.auto-1.1/input0
[  451.099030] input: Logitech USB Receiver Mouse as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1.1/7-1.1:1.1/0003:046D:C52B.0007/input/input12
[  451.099733] input: Logitech USB Receiver Consumer Control as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1.1/7-1.1:1.1/0003:046D:C52B.0007/input/input13
[  451.160075] input: Logitech USB Receiver System Control as /devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-1/7-1.1/7-1.1:1.1/0003:046D:C52B.0007/input/input14
[  451.160547] hid-generic 0003:046D:C52B.0007: input,hiddev97: USB HID v1.11 Mouse [Logitech USB Receiver] on usb-xhci-hcd.1.auto-1.1/input1
[  451.165557] hid-generic 0003:046D:C52B.0008: hiddev98: USB HID v1.11 Device [Logitech USB Receiver] on usb-xhci-hcd.1.auto-1.1/input2

此时可以在ubuntu桌面系统测试鼠标,可以发现是可以正常使用的。

USB鼠标工作在全速模式下,xHCI控制器,USB1.1通信协议,使用的USB总线编号为7,设备编号为5。

四、 模拟USB设备

本节的目标是将我们的开发板设置成从设备,从而实现和PC的通信,既然是将开发板模拟成从设备,首先想到的就是USB3.0 OTG控制器,我们需要工作模式dr-mode 配置成otg或者peripheral

然后我们还需要实现一个USB Gadget驱动,实现方法有多种,这里我们介绍其中的两种:

  • 使用内核中提供的USB Gadget驱动,即我们在USB Gadget precomposed configurations中配置的驱动;
  • 使用内核提供的USB Gadget Configfs,它提供了一种用户配置USB Gadget的方式,通过configfs可以动态的创建和配置USB Gadaget从设备,而无需修改内核源代码或重新编译内核;

注意:使用内核提供的USB Gadget Configfs,必须配置CONFIG_CONFIGFS_FSCONFIG_USB_LIBCOMPOSITE的宏,前者为用户空间提供访问配置内核驱动的configfs文件系统,后者提供USB Gadget Composite框架;

4.1 模拟存储设备

我们需要按照前面的内核配置将USB Gadget precomposed configurations ---> Mass Storage Gadget驱动编译成模块。使用的时候直接输入命令加载驱动即可。

(1) 配置好以后重新linux内核,会得到三个.ko驱动文件:

drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_mass_storage.ko
drivers/usb/gadget/legacy/g_mass_storage.ko

(2) 重新烧录内核,启动开发板。将上述三个.ko模块拷贝到开发板/lib/modules/6.3.0(如果没有该目录,自行创建),命令如下:

root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.builtin  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.order  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/libcomposite.ko  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/function/usb_f_mass_storage.ko  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/legacy/g_mass_storage.ko  /lib/modules/6.3.0

(3) 在开发板插入一个移动硬盘到我们的USB3.0 Host Type-A接口,并使用Type-C连接线,并按照之前介绍的将/dev/sda1分区挂载到/mnt/usb_disk目录下;

root@rk3399:/# mount /dev/sda1 /mnt/usb_disk/ -t ntfs -o iocharset=utf8 

最后将开发板USB3.0 Type-C接口与PC连接起来。

(4) 配置USB3.0 OTG0的工作模式为Device(设备),这种做法只会临时有效:

root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
otg
root@rk3399:/lib/modules/6.3.0# echo device > /sys/kernel/debug/usb/fe800000.usb/mode
root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
device

如果想永久有效,可以通过修改设备树dr-mode = peripheral将工作配置为Device(设备);

(5) 需要在加载模块之前建立该模块的依赖关系,也即必须用depmod来更新一下/lib/modules/linux-6.3/modules.dep文件;

root@rk3399:/lib/modules/6.3.0# cd /lib/modules/6.3.0
root@rk3399:/lib/modules/6.3.0# depmod -a
root@rk3399:/lib/modules/6.3.0# modprobe libcomposite
root@rk3399:/lib/modules/6.3.0# modprobe usb_f_mass_storage
root@rk3399:/lib/modules/6.3.0# modprobe g_mass_storage file=/dev/sda1 removable=1

modprobeinsmode的区别在于modprobe可以动态加载依赖的驱动,而insmode必须一个一个加载。加载g_mass_storage的时候使用file参数指定使用的大容量存储设备对应的/dev/sda1。同时内核会输出如下日志:

[  390.786425] Mass Storage Function, version: 2009/09/11
[  390.786459] LUN: removable file: (no medium)
[  390.786724] LUN: removable file: /dev/sda1
[  390.786743] Number of LUNs=1
[  390.786975] g_mass_storage gadget.0: Mass Storage Gadget, version: 2009/09/11
[  390.786995] g_mass_storage gadget.0: userspace failed to provide iSerialNumber
[  390.787005] g_mass_storage gadget.0: g_mass_storage ready

通过命令可以查看我们已经加载的驱动:

root@rk3399:/lib/modules/6.3.0# lsmod
Module                  Size  Used by
#### 下面是我们刚才加载的 ######
g_mass_storage         16384  0
usb_f_mass_storage     69632  2 g_mass_storage
libcomposite           81920  2 usb_f_mass_storage,g_mass_storage
#### 下面是wifi相关的,忽略即可 #####
brcmfmac_wcc           16384  0
brcmfmac              348160  1 brcmfmac_wcc
cfg80211              901120  1 brcmfmac
brcmutil               24576  1 brcmfmac

如果加载成功的话PC就会出现一个移动硬盘,如下图所示:

(6) 我们可以直接在电脑上对这个移动硬盘进行读写操作,实际上操作的就是我们的移动硬盘,操作完成以后要退出的话执行如下命令:

root@rk3399:/lib/modules/6.3.0# rmmod g_mass_storage

4.2 模拟声卡

我们需要按照前面的内核配置将USB Gadget precomposed configurations ---> Audio Gadget驱动编译成模块。使用的时候直接输入命令加载驱动即可。

(1) 配置好以后重新linux内核,会得到5个.ko驱动文件:

drivers/usb/gadget/libcomposite.ko
drivers/usb/gadget/function/usb_f_uac1.ko
drivers/usb/gadget/function/u_audio.ko
drivers/usb/gadget/legacy/g_audio.ko

(2) 重新烧录内核,启动开发板。将上述5个.ko模块拷贝到开发板/lib/modules/6.3.0(如果没有该目录,自行创建),命令如下:

root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.builtin  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.order  /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/libcomposite.ko /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/function/usb_f_uac1.ko /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/function/u_audio.ko /lib/modules/6.3.0
root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/usb/gadget/legacy/g_audio.ko /lib/modules/6.3.0

(3) 首先按照我们之前声卡驱动移植章节介绍的,保证声卡可以正常播放。

使用Type-C连接线,将开发板USB3.0 Type-C接口与PC连接起来。

(4) 配置USB3.0 OTG0的工作模式为Device(设备),这种做法只会临时有效:

root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
otg
root@rk3399:/lib/modules/6.3.0# echo device > /sys/kernel/debug/usb/fe800000.usb/mode
root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
device

如果想永久有效,可以通过修改设备树dr-mode = peripheral将工作配置为Device(设备);

(5) 需要在加载模块之前建立该模块的依赖关系,也即必须用depmod来更新一下/lib/modules/linux-6.3/modules.dep文件;

root@rk3399:~# cd /lib/modules/6.3.0
root@rk3399:/lib/modules/6.3.0# depmod -a
root@rk3399:/lib/modules/6.3.0# modprobe libcomposite
root@rk3399:/lib/modules/6.3.0# modprobe u_audio
root@rk3399:/lib/modules/6.3.0# modprobe usb_f_uac1
root@rk3399:/lib/modules/6.3.0# modprobe g_audio

modprobeinsmode的区别在于modprobe可以动态加载依赖的驱动,而insmode必须一个一个加载。内核会输出如下日志:

[ 1863.468513] g_audio gadget.0: Linux USB Audio Gadget, version: Feb 2, 2012
[ 1863.468535] g_audio gadget.0: g_audio ready

通过命令可以查看我们已经加载的驱动:

root@rk3399:/lib/modules/6.3.0# lsmod
Module                  Size  Used by
#### 下面是我们刚才加载的 ######
g_audio                16384  0
usb_f_uac1             36864  0
u_audio                32768  2 usb_f_uac1,usb_f_uac2
libcomposite           81920  5 usb_f_uac1,u_audio,g_audio,usb_f_mass_storage,usb_f_uac2
#### 下面是wifi相关的,忽略即可 #####
brcmfmac_wcc           16384  0
brcmfmac              348160  1 brcmfmac_wcc
cfg80211              901120  1 brcmfmac
brcmutil               24576  1 brcmfmac

加载完成以后稍等一会虚拟出一个USB声卡,打开电脑的设备管理器,选择声音、视频和游戏控制器,会出现有一个名为Source/Sink设备,如下图所示:

选择该设备,并通过PC播放音乐,不过这里我测试始终没有声音输出,具体原因还不太清除;

(6) 移除驱动:

root@rk3399:/lib/modules/6.3.0# rmmod g_audio

4.3 configfs模拟U

这里我们USB Gadget Configfs来模拟一个大容量存储设备。

4.3.1 挂载configfs

首先需要挂载configfs/sys/kernel/config目录下:

root@rk3399:/# mount -t configfs none /sys/kernel/config

创建g1文件夹,创建完g1之后,会在该目录下生成很多配置目录;

root@rk3399:/# cd /sys/kernel/config/usb_gadget
root@rk3399:/sys/kernel/config/usb_gadget# mkdir -p g1
root@rk3399:/sys/kernel/config/usb_gadget# ll g1/
-rw-r--r-- 1 root root 4096 Sep 17 22:16 UDC
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bDeviceClass
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bDeviceProtocol
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bDeviceSubClass
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bMaxPacketSize0
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bcdDevice
-rw-r--r-- 1 root root 4096 Sep 17 22:16 bcdUSB
drwxr-xr-x 2 root root    0 Sep 17 22:16 configs/
drwxr-xr-x 2 root root    0 Sep 17 22:16 functions/
-rw-r--r-- 1 root root 4096 Sep 17 22:16 idProduct
-rw-r--r-- 1 root root 4096 Sep 17 22:16 idVendor
-rw-r--r-- 1 root root 4096 Sep 17 22:16 max_speed
drwxr-xr-x 2 root root    0 Sep 17 22:16 os_desc/
drwxr-xr-x 2 root root    0 Sep 17 22:16 strings/
drwxr-xr-x 2 root root    0 Sep 17 22:16 webusb/

这里的g1表示gadget 1,一个UDC对应一个gadget ,如果SoC上有多个gadget,可以创建多个gx目录。

4.3.2 加载功能接口(function)驱动

configfs开启gadget驱动测试前,需要加载usb_f_mass_storage驱动程序:

root@rk3399:/lib/modules/6.3.0# cd /lib/modules/6.3.0
root@rk3399:/lib/modules/6.3.0# depmod -a
root@rk3399:/lib/modules/6.3.0# modprobe libcomposite
root@rk3399:/lib/modules/6.3.0# modprobe usb_f_mass_storage
root@rk3399:/lib/modules/6.3.0# cd /sys/kernel/config/usb_gadget/g1
4.3.4 配置g1

(1) 设置USB协议版本;

echo 0x0200  > bcdUSB

(2) 配置PIDVID

echo 0x1a2c > idVendor
echo 0x4d7e > idProduct

(3) 创建并配置string字目录:

mkdir strings/0x409
echo "0" > strings/0x409/serialnumber
echo "SEMICO" > strings/0x409/manufacturer
echo "USB Keyboard" > strings/0x409/product

上面的配置信息大部分都是从USB键盘设备信息中拷贝过来的。

(4) 创建一个USB配置实例:

mkdir configs/c.1
mkdir configs/c.1/strings/0x409

(5) 定义配置描述符使用的字符串;

echo "mass_storage" > configs/c.1/strings/0x409/configuration

这里的mass_storage的名字不能随便起,需要根据modprobefunction驱动usb_f_mass_storage.ko来决定,否则会出现错误。

(6) 创建一个接口(一个接口对应一种功能,一般而言一个USB设备可能存在多个接口),需要注意的是,如果存在多个接口的话,扩展名必须用数字编号:

mkdir functions/mass_storage.0

(7) 配置U盘参数

先生成一个FAT32格式的img,并挂载到

mkdir -p /var/sdcard;
cd  /var/sdcard;
mkdir sda;
dd  if=/dev/zero  of=/var/sdcard/disk.img  bs=1M  count=20;
mkdosfs -F 32  disk.img
mount -t vfat -o sync  /var/sdcard/disk.img  /var/sdcard/sda
cd /sys/kernel/config/usb_gadget/g1

接着配置U盘参数;

echo "/var/sdcard/disk.img" > functions/mass_storage.0/lun.0/file
echo 1 > functions/mass_storage.0/lun.0/removable
echo 0 > functions/mass_storage.0/lun.0/nofua

(8) 绑定接口到配置c.1

ln -s functions/mass_storage.0 configs/c.1

(9) 配置USB3.0 OTG0的工作模式为Device(设备):

root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
otg
root@rk3399:/lib/modules/6.3.0# echo device > /sys/kernel/debug/usb/fe800000.usb/mode
root@rk3399:/lib/modules/6.3.0# cat /sys/kernel/debug/usb/fe800000.usb/mode
device

(10) 查看当前的UDC,可见开发板上有1个UDCfe800000.usb

root@rk3399:/config# ll /sys/class/udc/
lrwxrwxrwx  1 root root 0 Sep 17 23:37 fe800000.usb -> '../../devices/platform/usb@fe800000/fe800000.usb/udc/fe800000.usb'/

配置当前gadget对应的USB OTG控制器, 执行如下命令;

echo fe800000.usb > UDC

上述步骤写成脚本,即可加载U盘驱动。

4.3.5 验证

使用Type-C连接线,将开发板USB3.0 Type-C接口与PC连接起来。

4.3.6 卸载U

当执行如下命令时,相当于模拟U盘拔掉,PC磁盘符消失;

echo "" > UDC
4.3.7 总结

这种方式比legacy方式要先进,USB设备属性修改(如序列号、PID/VIDinterface等)、插拔模拟等等都很方便在用户态修改,你只需要在configfs文件系统下创建一下文件/文件夹或者使用echo命令修改文件内容即可。

更重要的是,这种方法,很方便地实现“复合设备”。USB复合设备是只有一个设备描述符,一个配置描述符,同时有多个接口描述符(代表多种功能),譬如一个键鼠设备,既有键盘又有鼠标。

4. 4 configfs模拟鼠标键盘

用户可以使用configfs尝试实现更多的复合设备进行测试,但USB的端点数量是有限的USB2.0协议指定只用4bit表示端点号,所以最大端点数是16对IN/OUT端点(包含默认端点0)。端点资源有限,所以不可能复合任意多个设备。

4.4.1 hid_keyboard_mouse脚本

下面我们创建一个复合键鼠设备,然后编写hid_keyboard_mouse.sh脚本,文件存放在开发板ubuntu系统/shell/hid路径下;

#!/bin/bash

gadget=g1

do_start(){ 
    has_mount=$(mount -l | grep /sys/kernel/config)
    if [[ -z  $has_mount ]];then
        mount -t configfs none /sys/kernel/config
    fi
    cd /sys/kernel/config/usb_gadget

    # 当我们创建完这个文件夹之后,系统自动的在这个文件夹中创建usb相关的内容 ,这些内容需要由创建者自己填写
    if [[ ! -d ${gadget} ]]; then
		mkdir ${gadget}
	fi
    cd ${gadget}

    cd /lib/modules/6.3.0
    depmod -a
    modprobe libcomposite
    modprobe usb_f_hid             #需要先将usb_f_hid.ko驱动拷贝过来
    cd /sys/kernel/config/usb_gadget/${gadget}

    #设置USB协议版本USB2.0
    echo 0x0200 > bcdUSB

    #定义产品的VendorID和ProductID
    echo "0x0525"  > idVendor
    echo "0xa4ac" > idProduct

    #实例化"英语"ID:
    mkdir strings/0x409

    #将开发商、产品和序列号字符串写入内核
    echo "76543210" > strings/0x409/serialnumber
    echo "mkelehk"  > strings/0x409/manufacturer
    echo "keyboard_mouse"  > strings/0x409/product

    #创建一个USB配置实例
    if [[ ! -d configs/c.1 ]]; then
        mkdir configs/c.1
    fi

    #定义配置描述符使用的字符串
	if [[ ! -d configs/c.1/strings/0x409 ]]; then
		mkdir configs/c.1/strings/0x409
	fi   

    echo "hid" > configs/c.1/strings/0x409/configuration

    #创建两个接口
    mkdir functions/hid.0   #键盘
    mkdir functions/hid.1   #鼠标

    #接口0,模拟键盘  
    echo 1 > functions/hid.0/subclass   #启动设备符
    echo 1 > functions/hid.0/protocol   #键盘协议
    echo 8 > functions/hid.0/report_length  #标识该hid设备每次发送的报表长度为8字节
    echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.0/report_desc  #配置hid描述符

	#接口1,模拟键盘鼠标
    echo 1 > functions/hid.1/subclass   #启动设备符
    echo 2 > functions/hid.1/protocol   #鼠标协议
    echo 4 > functions/hid.1/report_length  # 相对值是4
    echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x05\\x15\\x00\\x25\\x01\\x95\\x05\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x03\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x09\\x38\\x15\\x81\\x25\\x7F\\x75\\x08\\x95\\x03\\x81\\x06\\xc0\\xc0 > functions/hid.1/report_desc

    #捆绑接口到配置config.1
    ln -s functions/hid.0 configs/c.1
    ln -s functions/hid.1 configs/c.1
	
	#配置USB3.0 OTG0的工作模式为Device(设备):
	echo device > /sys/kernel/debug/usb/fe800000.usb/mode

    echo "sleep 3s"
    sleep 3s

    #将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备。
    echo fe800000.usb > UDC
}

do_stop() {
    cd /sys/kernel/config/usb_gadget/${gadaget}
    echo "" > UDC
    rmmod usb_f_hid.ko
    rmmod libcomposite.ko
}

case $1 in
    start)
        echo "Start hid gadget "
        do_start 
        ;;
    stop)
        echo "Stop hid gadget"
        do_stop
        ;;
    *)
        echo "Usage: $0 (stop | start)"
        ;;
esac

对于鼠标设备,我们配置:

  • subclass:接口子类,配置为1,表示启动设备;
  • protocol:接口协议,配置为2,鼠标协议;
  • report_length:报告长度配置为4,即表示鼠标发送的数据长度为4个字节,关于每个字节的含义可以参考:linux驱动移植-USB鼠标接口驱动

对于键盘设备,我们配置:

  • subclass:接口子类,配置为1,表示启动设备;
  • protocol:接口协议,配置为1,键盘协议;
  • report_length:报告长度配置为8,即表示键盘发送的数据长度为8个字节,关于每个字节的含义可以参考:linux驱动移植-USB键盘接口驱动

(1) 此外,对于鼠标、键盘设备,我们还配置了HID报告描述符;HID报告描述符(Report Descriptor)是HID设备中的一个描述符,它是比较复杂的一个描述符。

报告描述符,是描述一个报告以及报告里面的数据是用来干什么用的?

  • 通过它,USB HOST可以分析出报告里面的数据所表示的意思。它通过控制输入端点0返回,主机使用获取报告描述符命令来获取报告描述符,注意这个请求是发送到接口的,而不是到设备;
  • 一个报告描述符可以描述多个报告,不同的报告通过报告ID来识别,报告ID在报告最前面,即第一个字节。当报告描述符中没有规定报告ID时,报告中就没有ID字段,开始就是数据;
  • 更详细的说明请参看USB HID协议,该协议可从Http://www.usb.org下载;

关于如何编写HID报告描述符强烈推荐这篇文章:HID报告描述符教程 手把手教你编写HID报告描述符

(2) 我们如果想要查看一个HID设备的报告描述,应该怎么做呢?

查看/sys/kernel/debug/hid/目录下包含046D:C52B的文件名;

root@rk3399:/shell/hid# ls /sys/kernel/debug/hid/ | grep "046D:C52B"
0003:046D:C52B.0016
0003:046D:C52B.0017
0003:046D:C52B.0018

其中:

  • 0003描述的是hid类设备,对于hid类设备,接口描述符的bInterfaceClass字段值固定为0x03
  • 046DUSB鼠标的idVendor
  • C52BUSB鼠标的idProduct
  • 0016/0017/0018:具体含义还不是特别清楚;

这里我们选择其中一个查看报告描述符:

root@rk3399:/shell/hid# cat /sys/kernel/debug/hid/0003\:046D\:C52B.0017/rdesc
05 01 09 02 a1 01 85 02 09 01 a1 00 05 09 19 01 29 10 15 00 25 01 95 10 75 01 81 02 05 01 16 01 f8 26 ff 07 75 0c 95 02 09 30 09 31 81 06 15 81 25 7f 75 08 95 01 09 38 81 06 05 0c 0a 38 02 95 01 81 06 c0 c0 05 0c 09 01 a1 01 85 03 75 10 95 02 15 01 26 ff 02 19 01 2a ff 02 81 00 c0 05 01 09 80 a1 01 85 04 75 02 95 01 15 01 25 03 09 82 09 81 09 83 81 60 75 06 81 03 c0 06 bc ff 09 88 a1 01 85 08 19 01 29 ff 15 01 26 ff 00 75 08 95 01 81 00 c0

其中部分数据表示的含义:

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x02,                    // USAGE (Mouse)
0xa1, 0x01,                    // COLLECTION (Application)
......
0xc0,                          //   END_COLLECTION
0xc0                           // END_COLLECTION

我们使用的鼠标的报告描述符还是比较复杂的,这里就不具体解读了。

更多内容可以参考内核:

(3) 在开发板ubuntu系统运行命令模拟鼠标和键盘:

root@rk3399:/shell/hid# bash hid_keyboard_mouse.sh start
Start hid gadget
sleep 3s

此时会在/dev目录下生成了设备节点/dev/hidg0/dev/hidg1

root@rk3399:/shell# ll /dev/hidg*
crw------- 1 root root 510, 0 Sep 18 23:03 /dev/hidg0
crw------- 1 root root 510, 1 Sep 18 23:03 /dev/hidg1

如果需卸载鼠标和键盘,执行如下命令:

root@rk3399:/shell/hid# bash hid_keyboard_mouse.sh stop
4.4.2 编写应用程序

然后我们通过设备节点/dev/hidg0/dev/hidg1,就可以模拟鼠标、键盘实现与PC的通信。

编写测试应用程序hid_gadget_control.c,文件存放在ubuntu宿主机/work/sambashare/rk3399/drivers/2.hid路径下;

/* hid_gadget_test */

#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUF_LEN 512

struct options {
    const char    *opt;
    unsigned char val;
};

// 描述特殊按键,存储在BYTE0字节
static struct options kmod[] = {
    {.opt = "--left-ctrl",          .val = 0x01},
    {.opt = "--right-ctrl",         .val = 0x10},
    {.opt = "--left-shift",         .val = 0x02},
    {.opt = "--right-shift",        .val = 0x20},
    {.opt = "--left-alt",           .val = 0x04},
    {.opt = "--right-alt",          .val = 0x40},
    {.opt = "--left-meta",          .val = 0x08},
    {.opt = "--right-meta",         .val = 0x80},
    {.opt = NULL}
};

// 描述普通按键,存储在BYTE2~BYTE7字节
static struct options kval[] = {
    {.opt = "--return",     .val = 0x28},
    {.opt = "--esc",        .val = 0x29},
    {.opt = "--bckspc",     .val = 0x2a},
    {.opt = "--tab",        .val = 0x2b},
    {.opt = "--spacebar",   .val = 0x2c},
    {.opt = "--caps-lock",  .val = 0x39},
    {.opt = "--f1",         .val = 0x3a},
    {.opt = "--f2",         .val = 0x3b},
    {.opt = "--f3",         .val = 0x3c},
    {.opt = "--f4",         .val = 0x3d},
    {.opt = "--f5",         .val = 0x3e},
    {.opt = "--f6",         .val = 0x3f},
    {.opt = "--f7",         .val = 0x40},
    {.opt = "--f8",         .val = 0x41},
    {.opt = "--f9",         .val = 0x42},
    {.opt = "--f10",        .val = 0x43},
    {.opt = "--f11",        .val = 0x44},
    {.opt = "--f12",        .val = 0x45},
    {.opt = "--insert",     .val = 0x49},
    {.opt = "--home",       .val = 0x4a},
    {.opt = "--pageup",     .val = 0x4b},
    {.opt = "--del",        .val = 0x4c},
    {.opt = "--end",        .val = 0x4d},
    {.opt = "--pagedown",   .val = 0x4e},
    {.opt = "--right",      .val = 0x4f},
    {.opt = "--left",       .val = 0x50},
    {.opt = "--down",       .val = 0x51},
    {.opt = "--kp-enter",   .val = 0x58},
    {.opt = "--up",         .val = 0x52},
    {.opt = "--num-lock",   .val = 0x53},
    {.opt = NULL}
};


int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int key = 0;
    int i = 0;

    for (; tok != NULL; tok = strtok(NULL, " ")) {

            if (strcmp(tok, "--quit") == 0)
                    return -1;

            if (strcmp(tok, "--hold") == 0) {
                    *hold = 1;
                    continue;
            }

            if (key < 6) {
                    for (i = 0; kval[i].opt != NULL; i++)
                            if (strcmp(tok, kval[i].opt) == 0) {
                                    report[2 + key++] = kval[i].val;
                                    break;
                            }
                    if (kval[i].opt != NULL)
                            continue;
            }

            if (key < 6)
                    if (islower(tok[0])) {
                            report[2 + key++] = (tok[0] - ('a' - 0x04));
                            continue;
                    }

            for (i = 0; kmod[i].opt != NULL; i++)
                    if (strcmp(tok, kmod[i].opt) == 0) {
                            report[0] = report[0] | kmod[i].val;
                            break;
                    }
            if (kmod[i].opt != NULL)
                    continue;

            if (key < 6)
                    fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 8;
}

// 描述鼠标按下/松开,存在在BYTE0
static struct options mmod[] = {
    {.opt = "--b1", .val = 0x01},  // 左键按下
    {.opt = "--b2", .val = 0x02},  // 右键按下
    {.opt = "--b3", .val = 0x04},  // 中键按下 
    {.opt = NULL}
};

int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int mvt = 0;
    int i = 0;
    for (; tok != NULL; tok = strtok(NULL, " ")) {

            if (strcmp(tok, "--quit") == 0)
                    return -1;

            if (strcmp(tok, "--hold") == 0) {
                    *hold = 1;
                    continue;
            }

            for (i = 0; mmod[i].opt != NULL; i++)
                    if (strcmp(tok, mmod[i].opt) == 0) {
                            report[0] = report[0] | mmod[i].val;
                            break;
                    }
            if (mmod[i].opt != NULL)
                    continue;

            if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
                    errno = 0;
                    report[1 + mvt++] = (char)strtol(tok, NULL, 0);
                    if (errno != 0) {
                            fprintf(stderr, "Bad value:'%s'\n", tok);
                            report[1 + mvt--] = 0;
                    }
                    continue;
            }

            fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 3;
}

static struct options jmod[] = {
    {.opt = "--b1",         .val = 0x10},
    {.opt = "--b2",         .val = 0x20},
    {.opt = "--b3",         .val = 0x40},
    {.opt = "--b4",         .val = 0x80},
    {.opt = "--hat1",       .val = 0x00},
    {.opt = "--hat2",       .val = 0x01},
    {.opt = "--hat3",       .val = 0x02},
    {.opt = "--hat4",       .val = 0x03},
    {.opt = "--hatneutral", .val = 0x04},
    {.opt = NULL}
};

int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
    char *tok = strtok(buf, " ");
    int mvt = 0;
    int i = 0;

    *hold = 1;

    /* set default hat position: neutral */
    report[3] = 0x04;

    for (; tok != NULL; tok = strtok(NULL, " ")) {

            if (strcmp(tok, "--quit") == 0)
                    return -1;

            for (i = 0; jmod[i].opt != NULL; i++)
                    if (strcmp(tok, jmod[i].opt) == 0) {
                            report[3] = (report[3] & 0xF0) | jmod[i].val;
                            break;
                    }
            if (jmod[i].opt != NULL)
                    continue;

            if (!(tok[0] == '-' && tok[1] == '-') && mvt < 3) {
                    errno = 0;
                    report[mvt++] = (char)strtol(tok, NULL, 0);
                    if (errno != 0) {
                            fprintf(stderr, "Bad value:'%s'\n", tok);
                            report[mvt--] = 0;
                    }
                    continue;
            }

            fprintf(stderr, "unknown option: %s\n", tok);
    }
    return 4;
}


void print_options(char c)
{
    int i = 0;

    if (c == 'k') {
            printf("        keyboard options:\n"
                    "                --hold\n");
            for (i = 0; kmod[i].opt != NULL; i++)
                    printf("\t\t%s\n", kmod[i].opt);
            printf("\n      keyboard values:\n"
                    "                [a-z] or\n");
            for (i = 0; kval[i].opt != NULL; i++)
                    printf("\t\t%-8s%s", kval[i].opt, i % 2 ? "\n" : "");
            printf("\n");
    } else if (c == 'm') {
            printf("        mouse options:\n"
                    "                --hold\n");
            for (i = 0; mmod[i].opt != NULL; i++)
                    printf("\t\t%s\n", mmod[i].opt);
            printf("\n      mouse values:\n"
                    "                Two signed numbers\n"
                    "--quit to close\n");
    } else {
            printf("        joystick options:\n");
            for (i = 0; jmod[i].opt != NULL; i++)
                    printf("\t\t%s\n", jmod[i].opt);
            printf("\n      joystick values:\n"
                    "                three signed numbers\n"
                    "--quit to close\n");
    }
}

int main(int argc, const char *argv[])
{
    const char *filename = NULL;
    int fd = 0;
    char buf[BUF_LEN];
    int cmd_len;
    char report[8];
    int to_send = 8;
    int hold = 0;
    fd_set rfds;
    int retval, i;

    if (argc < 3) {
            fprintf(stderr, "Usage: %s devname mouse|keyboard|joystick\n",
                    argv[0]);
            return 1;
    }

    if (argv[2][0] != 'k' && argv[2][0] != 'm' && argv[2][0] != 'j')
        return 2;

    filename = argv[1];

    if ((fd = open(filename, O_RDWR, 0666)) == -1) {
            perror(filename);
            return 3;
    }

    print_options(argv[2][0]);

    while (42) {

            FD_ZERO(&rfds);
            FD_SET(STDIN_FILENO, &rfds);
            FD_SET(fd, &rfds);

            retval = select(fd + 1, &rfds, NULL, NULL, NULL);
            if (retval == -1 && errno == EINTR)
                    continue;
            if (retval < 0) {
                    perror("select()");
                    return 4;
            }

            if (FD_ISSET(fd, &rfds)) {
                    cmd_len = read(fd, buf, BUF_LEN - 1);
                    printf("recv report:");
                    for (i = 0; i < cmd_len; i++)
                            printf(" %02x", buf[i]);
                    printf("\n");
            }

            if (FD_ISSET(STDIN_FILENO, &rfds)) {
                    memset(report, 0x0, sizeof(report));
                    cmd_len = read(STDIN_FILENO, buf, BUF_LEN - 1);

                    if (cmd_len == 0)
                            break;

                    buf[cmd_len - 1] = '\0';
                    hold = 0;

                    memset(report, 0x0, sizeof(report));
                    if (argv[2][0] == 'k')
                            to_send = keyboard_fill_report(report, buf, &hold);
                    else if (argv[2][0] == 'm')
                            to_send = mouse_fill_report(report, buf, &hold);
                    else
                            to_send = joystick_fill_report(report, buf, &hold);

                    if (to_send == -1)
                            break;

                    if (write(fd, report, to_send) != to_send) {
                            perror(filename);
                            return 5;
                    }
                    if (!hold) {
                            memset(report, 0x0, sizeof(report));
                            if (write(fd, report, to_send) != to_send) {
                                    perror(filename);
                                    return 6;
                            }
                    }
            }
    }

    close(fd);
    return 0;
}

编写Makefile文件:

all:
	arm-linux-gcc -march=armv8-a -o hid_gadget_control hid_gadget_control.c
clean:
	rm -rf *.o hid_gadget_control

ubuntu宿主机下编译:

root@zhengyang:/work/sambashare/rk3399/drivers/2.hid# make
arm-linux-gcc -march=armv8-a -o hid_gadget_control hid_gadget_control.c
root@zhengyang:/work/sambashare/rk3399/drivers/2.hid# ll
-rwxr-xr-x 1 root root 79000 Sep 18 22:39 hid_gadget_control*
-rwxrw-rw- 1 root root 10526 Sep 18 22:37 hid_gadget_control.c*
-rwxrw-rw- 1 root root  3286 Sep 18 22:34 hid_keyboard_mouse.sh*
-rwxrw-rw- 1 root root   118 Sep 18 22:39 Makefile*

将编译后的hid_gadget_control拷贝到开发板ubuntu系统/shell/hid路径;

root@rk3399:/shell/hid# scp root@192.168.0.200:/work/sambashare/rk3399/drivers/2.hid/hid_gadget_control ./

使用hid_gadget_control程序时,需要指定一个hidgX设备,并设置设备类型(键盘/鼠标/游戏手柄)。

4.4.3 测试键盘

(1) 使用Type-C连接线,将开发板USB3.0 Type-C接口与PC连接起来。

在开发板ubuntu桌面环境打开一个终端输入如下命令(开发板需要先连接好键盘):

root@rk3399:/shell/hid# ./hid_gadget_control /dev/hidg0 keyboard
./hid_gadget_control: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./hid_gadget_control)

检查版本:

root@rk3399:/shell/hid# strings /lib/aarch64-linux-gnu/libc.so.6 | grep GLIBC_
GLIBC_2.17
GLIBC_2.18
GLIBC_2.22
GLIBC_2.23
GLIBC_2.24
GLIBC_2.25
GLIBC_2.26
GLIBC_2.27
GLIBC_2.28
GLIBC_2.29
GLIBC_2.30
GLIBC_PRIVATE

最高版本只到2.30,由于使用的系统为ubuntu20.04,已经升级到了系统版本的最高版本了。

解决方案:添加一个高级版本系统的源,直接升级libc6。编辑源:

root@rk3399:/shell/hid# vi /etc/apt/sources.list

添加高版本的源:

deb https://mirrors.ustc.edu.cn/ubuntu-ports/ jammy main restricted universe mulltiverse
deb-src https://mirrors.ustc.edu.cn/ubuntu-ports/ jammy main main restricted universe multiverse

运行升级(可能时间比较长稍等一会):

root@rk3399:/shell/hid#sudo apt update
root@rk3399:/shell/hid#sudo apt install libc6
root@rk3399:/shell/hid# strings /lib/aarch64-linux-gnu/libc.so.6 | grep GLIBC_2.34
GLIBC_2.34

(2) 在开发板ubuntu桌面环境打开一个终端输入如下命令(开发板需要先连接好键盘):

进入hid_gadget_control 的提示符后,可以输入任意组合的选项和值。可用的选项和值在程序启动时列出。

在键盘模式下,可以发送最多六个值。例如,通过开发板连接的键盘键入以下内容:g i s t r --left-shift,然后按回车键;

对应的报告将由HID gadget发送,此时在PCwindow系统聚焦的输入框可以得到键盘输入的内容:

另一个有趣的示例是大写锁定键测试。键入 --caps-lock 并按回车键。然后,设备将发送一个报告,你应该会收到主机的响应,对应于大写锁定键的LED状态:

--caps-lock
recv report: 03
4.4.4 测试鼠标

(1) 这里我们做个好玩的实验,使用Type-C连接线将开发板USB3.0 Type-C接口与开发板上的USB2.0 Host Type-A接口(对应开发板上的USBH3)连接起来;内核日志信息如下:

[ 4162.448207] usb 2-1: new high-speed USB device number 2 using ehci-platform
[ 4162.607147] usb 2-1: New USB device found, idVendor=0525, idProduct=a4ac, bcdDevice= 6.03
[ 4162.607187] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 4162.607204] usb 2-1: Product: keyboard_mouse
[ 4162.607218] usb 2-1: Manufacturer: mkelehk
[ 4162.607231] usb 2-1: SerialNumber: 76543210
[ 4162.613199] input: mkelehk keyboard_mouse as /devices/platform/fe3c0000.usb/usb2/2-1/2-1:1.0/0003:0525:A4AC.000A/input/input15
[ 4162.673625] hid-generic 0003:0525:A4AC.000A: input: USB HID v1.01 Keyboard [mkelehk keyboard_mouse] on usb-fe3c0000.usb-1/input0
[ 4162.676744] input: mkelehk keyboard_mouse as /devices/platform/fe3c0000.usb/usb2/2-1/2-1:1.1/0003:0525:A4AC.000B/input/input16
[ 4162.677172] hid-generic 0003:0525:A4AC.000B: input: USB HID v1.01 Mouse [mkelehk keyboard_mouse] on usb-fe3c0000.usb-1/input1

USB鼠标工作在高速模式下,eHCI控制器,USB2.0通信协议,使用的USB总线编号为2,设备编号为2。

其中Product=keyboard_mouseManufacturer=mkelehkSerialNumber=76543210idVendor=0525idProduct=a4ac,这个就是我们在hid_keyboard_mouse.sh脚本下配置的。

在连接Type-C连接线之后,/dev/event下多了两个设备节点文件event9event10

(2) 将鼠标移动到开发板ubuntu桌面系统的正中间,并打开一个开发板终端(非桌面终端)输入如下命令:

root@rk3399:/shell/hid# ./hid_gadget_control /dev/hidg1 mouse
        mouse options:
                --hold
                --b1
                --b2
                --b3

      mouse values:
                Two signed numbers
--quit to close
200 210      # 鼠标向左上移动
-200 210     # 鼠标向右上移动
200 -210     # 鼠标向左下移动
-200 -210    # 鼠标向右下移动
0 0 --b1     # 鼠标左键点击 
0 0 --b2     # 鼠标右键点击
0 0 --b3     # 鼠标中键点击

进入hid_gadget_control 的提示符后,可以输入任意组合的选项和值。可用的选项和值在程序启动时列出。

此时效果就是开发板ubuntu桌面的鼠标会移动,并触发点击事件。

需要注意的是:这里我也尝试了使用Type-C连接线将开发板USB3.0 Type-C接口与PC连接起来,但是并没有上面所说的效果。

4.5 configs模拟触摸屏

这一节我们尝试使用configfs模拟触摸屏。

4.5.1 hid_touch脚本

下面我们创建一个触摸屏设备,然后编写hid_touch.sh脚本,文件存放在开发板ubuntu系统/shell/hid路径下;

#!/bin/bash

gadget=g2

do_start(){ 
    has_mount=$(mount -l | grep /sys/kernel/config)
    if [[ -z  $has_mount ]];then
        mount -t configfs none /sys/kernel/config
    fi
    cd /sys/kernel/config/usb_gadget

    # 当我们创建完这个文件夹之后,系统自动的在这个文件夹中创建usb相关的内容 ,这些内容需要由创建者自己填写
    if [[ ! -d ${gadget} ]]; then
		mkdir ${gadget}
	fi
    cd ${gadget}

    cd /lib/modules/6.3.0
    depmod -a
    modprobe libcomposite
    modprobe usb_f_hid
    cd /sys/kernel/config/usb_gadget/${gadget}

    #设置USB协议版本USB2.0
    echo 0x0200 > bcdUSB

    #定义产品的VendorID和ProductID
    echo "0x0525"  > idVendor
    echo "0xa4ac" > idProduct

    #实例化"英语"ID:
    mkdir strings/0x409

    #将开发商、产品和序列号字符串写入内核
    echo "76543210" > strings/0x409/serialnumber
    echo "mkelehk"  > strings/0x409/manufacturer
    echo "touch_screen"  > strings/0x409/product

    #创建一个USB配置实例
    if [[ ! -d configs/c.1 ]]; then
        mkdir configs/c.1
    fi

    #定义配置描述符使用的字符串
	if [[ ! -d configs/c.1/strings/0x409 ]]; then
		mkdir configs/c.1/strings/0x409
	fi   

    echo "hid" > configs/c.1/strings/0x409/configuration

    #创建接口
    mkdir functions/hid.0   

    #触摸屏接口 配置hid描述符
    echo 0 > functions/hid.0/subclass  
    echo 0 > functions/hid.0/protocol   
    echo 5 > functions/hid.0/report_length  #标识该hid设备每次发送的报表长度为5字节
    echo -ne \\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x05\\x09\\x19\\x01\\x29\\x05\\x15\\x00\\x25\\x01\\x95\\x05\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x03\\x81\\x01\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x00\\x26\\xff\\x7f\\x35\\x00\\x46\\xff\\x7f\\x75\\x10\\x95\\x02\\x81\\x02\\xc0\\xc0 > functions/hid.0/report_desc

    #捆绑接口到配置config.1
    ln -s functions/hid.0 configs/c.1
	
	#配置USB3.0 OTG0的工作模式为Device(设备):
	echo device > /sys/kernel/debug/usb/fe800000.usb/mode

    echo "sleep 3s"
    sleep 3s

    #将gadget驱动注册到UDC上,插上USB线到电脑上,电脑就会枚举USB设备。
    echo fe800000.usb > UDC
}

do_stop() {
    cd /sys/kernel/config/usb_gadget/${gadaget}
    echo "" > UDC
    rmmod usb_f_hid.ko
    rmmod libcomposite.ko
}

case $1 in
    start)
        echo "Start hid gadget "
        do_start 
        ;;
    stop)
        echo "Stop hid gadget"
        do_stop
        ;;
    *)
        echo "Usage: $0 (stop | start)"
        ;;
esac

对于触摸屏设备,我们配置:

  • subclass:接口子类,配置为0,表示No Subclass
  • protocol:接口协议,配置为0;
  • report_length:报告长度配置为5,触摸屏与鼠标的不同点是鼠标的上报值是相对坐标,触摸屏是绝对坐标,鼠标xy轴分别需要一个字节,而触摸屏一般为16bit即两个字节;

HID设备支持多种协议。接口描述符的bInterfaceProtocol成员(接口协议)只有在bInterfaceSubClass(接口子类)支持启动接口时才有意义,否则为0。

在开发板ubuntu系统运行命令模拟触摸屏:

root@rk3399:/shell/hid# bash hid_touch.sh start
Start hid gadget
sleep 3s

此时会在/dev目录下生成了设备节点/dev/hidg0(这里实验时我重启了开发板,所以编号是0);

root@rk3399:/shell/hid# ll /dev/hidg*
crw------- 1 root root 510, 0 Sep 19 23:41 /dev/hidg0

如果需卸载触摸屏,执行如下命令:

root@rk3399:/shell/hid# bash hid_touch.sh stop
4.5.2 编写应用程序

编写测试应用程序hid_touch_control.c,文件存放在ubuntu宿主机/work/sambashare/rk3399/drivers/2.hid路径下;

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

/**
 * 触摸屏每次发送5字节报告
 * report[0]的D0就是左键,D1就是右键,D2就是中键
 * report[1]为X轴低字节
 * report[2]为X轴高字节
 * report[3]为Y轴低字节
 * report[4]为Y轴高字节,
*/

/**
 * @brief 将焦点移动到指定位置
 * 
 * @param fd 文件描述符
 * @param x x轴坐标
 * @param y y轴坐标
 */
void move_To(int fd ,int x, int y)
{
    char report[5]={0,0,0,0,0};
    report[0] = 0x00;
    report[1] = x & 0xFF;
    report[2] = (x >> 8) & 0xFF;
    report[3] = y & 0xFF;
    report[4] = (y >> 8) & 0xFF;

    // 写入失败
    if (write(fd, report, 5) != 5) {
        perror("move_To error");
        return;
    }
    usleep(50000);
}


/**
 * @brief 函数功能:画线段的函数。
 * 
  *@param fd 文件描述符
 * @param x x轴坐标
 * @param y y轴坐标
 */
void line_to(int fd, int x, int y)
{
    char report[5]={0,0,0,0,0};
    report[0] = 0x01;    //左键按下
    report[1] = x & 0xFF;
    report[2] = (x >> 8) & 0xFF;
    report[3] = y & 0xFF;
    report[4] = (y >> 8) & 0xFF;
    if (write(fd, report, 5) != 5) {
        perror("line_to error");
        return;
    }
    usleep(50000);
}

int main(int argc, char const *argv[])
{
    const char *filename = NULL;
    int fd = 0;    

    if (argc != 3) 
    {
            fprintf(stderr, "Usage: %s devname a/l\n",
                    argv[0]);
            return 1;
    }

    filename = argv[1];

    if ((fd = open(filename, O_RDWR, 0666)) == -1) {
        perror(filename);
        return 1;
    }

    if(argv[2][0] == 'a')
    {
        printf("Draw Tri-angle begin\n");
        move_To(fd, 8888, 8888); //移动到(8888,8888)
        line_to(fd, 8888, 8888); //开始画线
        line_to(fd, 12000, 12000); //画线到(12000,12000)        
        move_To(fd, 12000, 12000); //松开鼠标左键

        move_To(fd, 12000, 12000); //移动到(12000,12000)
        line_to(fd, 12000, 12000); //开始画线
        line_to(fd ,8888, 12000); //画线到(8888,12000)
        move_To(fd, 8888, 12000); //松开鼠标左键

        move_To(fd, 8888, 12000); //移动到(8888,12000)
        line_to(fd, 8888, 12000); //开始画线
        line_to(fd ,8888, 8888); //画线到(8888,8888)
        move_To(fd, 8888, 8888); //松开鼠标左键
        printf("Draw Tri-angle end\n");
    }

    if(argv[2][0] == 'l'){
        printf("Draw line begin\n");
        move_To(fd, 10000, 10000);  // 移动到(10000,10000)
        line_to(fd, 10000, 10000);  // 开始画线
        line_to(fd, 20000, 10000);  // 画线到(20000,10000)
        move_To(fd, 20000, 10000);  // 松开鼠标左键
        printf("Draw line end\n");
    }
    
    close(fd);
    return 0;
}

ubuntu宿主机下编译:

root@zhengyang:/work/sambashare/rk3399/drivers/2.hid# arm-linux-gcc -march=armv8-a -o hid_touch_control hid_touch_control.c

将编后的hid_touch_control拷贝到开发板ubuntu系统/shell/hid路径;

root@rk3399:/shell/hid# scp root@192.168.0.200:/work/sambashare/rk3399/drivers/2.hid/hid_touch_control ./

使用hid_touch_control程序时,需要指定触摸屏设备节点/devhidgX,并设置操作类型(a/l)。

4.5.3 测试触摸屏

(1) 这里我们和鼠标测试一样,使用Type-C连接线将开发板USB3.0 Type-C接口与开发板上的USB2.0 Host Type-A接口(对应开发板上的USBH3)连接起来;内核日志信息如下:

[  212.726544] usb 2-1: new high-speed USB device number 3 using ehci-platform
[  212.885619] usb 2-1: New USB device found, idVendor=0525, idProduct=a4ac, bcdDevice= 6.03
[  212.885658] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  212.885675] usb 2-1: Product: touch_screen
[  212.885696] usb 2-1: Manufacturer: mkelehk
[  212.885709] usb 2-1: SerialNumber: 76543210
[  212.890175] input: mkelehk touch_screen as /devices/platform/fe3c0000.usb/usb2/2-1/2-1:1.0/0003:0525:A4AC.0009/input/input16
[  212.890947] hid-generic 0003:0525:A4AC.0009: input: USB HID v1.01 Mouse [mkelehk touch_screen] on usb-fe3c0000.usb-1/input0

USB触摸屏工作在高速模式下,eHCI控制器,USB2.0通信协议,使用的USB总线编号为2,设备编号为3。

其中Product=touch_screenManufacturer=mkelehkSerialNumber=76543210idVendor=0525idProduct=a4ac,这个就是我们在hid_touch.sh脚本下配置的。

(2) 在开发板ubuntu桌面安装画图软件,并运行程序;

root@rk3399:/shell/hid# sudo apt install -y kolourpaint
root@rk3399:/shell/hid# kolourpaint

我们将画布调到最大,并且选择左侧的Line

(3) 打开一个开发板终端(非桌面终端)输入如下命令:

root@rk3399:/shell/hid# ./hid_touch_control /dev/hidg0 a
Draw Tri-angle begin
Draw Tri-angle end
root@rk3399:/shell/hid# ./hid_touch_control /dev/hidg0 l
Draw line begin
Draw line end

最终得到如下图:

五、调试接口

5.1 sysfs

5.1.1 USB设备

所有USB设备可以在/sys/bus/usb/devices下找到;

root@rk3399:/# ll /sys/bus/usb/devices
total 0
drwxr-xr-x 2 root root 0 Mar 15  2023 ./
drwxr-xr-x 4 root root 0 Mar 15  2023 ../
lrwxrwxrwx 1 root root 0 Mar 15  2023 1-0:1.0 -> ../../../devices/platform/fe3a0000.usb/usb1/1-0:1.0/
lrwxrwxrwx 1 root root 0 Sep 16 21:50 1-1 -> ../../../devices/platform/fe3a0000.usb/usb1/1-1/
lrwxrwxrwx 1 root root 0 Sep 16 21:50 1-1:1.0 -> ../../../devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.0/
lrwxrwxrwx 1 root root 0 Sep 16 21:50 1-1:1.1 -> ../../../devices/platform/fe3a0000.usb/usb1/1-1/1-1:1.1/
lrwxrwxrwx 1 root root 0 Mar 15  2023 2-0:1.0 -> ../../../devices/platform/fe3c0000.usb/usb2/2-0:1.0/
lrwxrwxrwx 1 root root 0 Mar 15  2023 3-0:1.0 -> ../../../devices/platform/fe3e0000.usb/usb3/3-0:1.0/
lrwxrwxrwx 1 root root 0 Sep 16 21:31 3-1 -> ../../../devices/platform/fe3e0000.usb/usb3/3-1/
lrwxrwxrwx 1 root root 0 Sep 16 21:31 3-1:1.0 -> ../../../devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.0/
lrwxrwxrwx 1 root root 0 Sep 16 21:31 3-1:1.1 -> ../../../devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.1/
lrwxrwxrwx 1 root root 0 Sep 16 21:31 3-1:1.2 -> ../../../devices/platform/fe3e0000.usb/usb3/3-1/3-1:1.2/
lrwxrwxrwx 1 root root 0 Mar 15  2023 4-0:1.0 -> '../../../devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb4/4-0:1.0'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 5-0:1.0 -> ../../../devices/platform/fe380000.usb/usb5/5-0:1.0/
lrwxrwxrwx 1 root root 0 Mar 15  2023 6-0:1.0 -> '../../../devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb6/6-0:1.0'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 7-0:1.0 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7/7-0:1.0'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 8-0:1.0 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb8/8-0:1.0'/
lrwxrwxrwx 1 root root 0 Sep 16 22:03 8-1 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb8/8-1'/
lrwxrwxrwx 1 root root 0 Sep 16 22:03 8-1:1.0 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb8/8-1/8-1:1.0'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb1 -> ../../../devices/platform/fe3a0000.usb/usb1/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb2 -> ../../../devices/platform/fe3c0000.usb/usb2/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb3 -> ../../../devices/platform/fe3e0000.usb/usb3/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb4 -> '../../../devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb4'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb5 -> ../../../devices/platform/fe380000.usb/usb5/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb6 -> '../../../devices/platform/usb@fe800000/fe800000.usb/xhci-hcd.0.auto/usb6'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb7 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb7'/
lrwxrwxrwx 1 root root 0 Mar 15  2023 usb8 -> '../../../devices/platform/usb@fe900000/fe900000.usb/xhci-hcd.1.auto/usb8'/

如上图所示,可以看到大量的USB设备。取名规则如下:

bus-port[.port]:configuration.interface

其中:

  • bus:表示USB控制器所连接的总线编号;从1开始编号;
  • port:表示总线上的硬件端口号;从0开始编号;比如1-0是根hub自身信息,当hub上有插入设备时,就会出现1-1了。
  • port:如果总线上的硬件端口连接的设备是一个HUB,那么该port表示连接在hub上的硬件端口号;从1开始编号;
  • configuration:表示设备的配置值;从1开始编号;
  • interface:表示设备的接口号;从0开始编号;

比如1-1:1.1具有以下含义:

  • 1-1:表示连接到第一个USB控制器的编号为1的硬件端口;
  • 1.1:表示设备的第一个配置(configuration)编号为1接口(interface)。

7-1.2:1.0具有以下含义:

  • 7-1.2:表示连接到第7个USB控制器的编号为1的硬件端口连接了一个hubhub编号为2的硬件端口上连接了一个设备;
  • 1.0:表示设备的第一个配置(configuration)编号为0接口(interface)。
5.1.2 USB设备信息

我们可以查看USB设备PIDVID

root@rk3399:/sys/bus/usb/devices# ls
1-0:1.0  1-1:1.0  2-0:1.0  3-1      3-1:1.1  4-0:1.0  6-0:1.0  8-0:1.0  8-1:1.0  usb2  usb4  usb6  usb8
1-1      1-1:1.1  3-0:1.0  3-1:1.0  3-1:1.2  5-0:1.0  7-0:1.0  8-1      usb1     usb3  usb5  usb7
root@rk3399:/sys/bus/usb/devices# ls 8-1/id*
8-1/idProduct  8-1/idVendor
root@rk3399:/sys/bus/usb/devices# cat 8-1/idVendor
0480
root@rk3399:/sys/bus/usb/devices# cat 8-1/idProduct
b200
root@rk3399:/sys/bus/usb/devices# cat 8-1/manufacturer
TOSHIBA
root@rk3399:/sys/bus/usb/devices# cat 8-1/product
External USB 3.0
root@rk3399:/sys/bus/usb/devices# cat 8-1/version
 3.00

VID=0480PID=b200对应的使我们移动硬盘。

1个USB设备可能会有多个接口,比如:

root@rk3399:/sys/bus/usb/devices# ls 1-1/1-1\:1.
1-1:1.0/ 1-1:1.1/

目录1-1存放的是USB设备信息,1-1:1.01-1:1.1目录下存放的相应接口的信息。

5. 2 debugfs

USB调试信息位于/sys/kernel/debug/usb

root@rk3399:~# ll /sys/kernel/debug/usb
-r--r--r--  1 root root 0 Jan  1  1970 devices
drwxr-xr-x  4 root root 0 Jan  1  1970 ehci/
drwxr-xr-x  2 root root 0 Jan  1  1970 fe800000.usb/
drwxr-xr-x  2 root root 0 Jan  1  1970 fe900000.usb/
drwxr-xr-x  2 root root 0 Jan  1  1970 fusb302-4-0022/
drwxr-xr-x  4 root root 0 Jan  1  1970 ohci/
drwxr-xr-x  2 root root 0 Jan  1  1970 tcpm-4-0022/
drwxr-xr-x  2 root root 0 Jan  1  1970 uvcvideo/
drwxr-xr-x  4 root root 0 Jan  1  1970 xhci/

其中:

  • devices:该文件列出了当前系统中已连接的USB设备的详细信息;
  • ehci:该目录包含EHCI主机控制器接口的调试信息;
  • fe800000.usb:该目录包含与fe800000.usb控制器相关的调试信息;
  • fe900000.usb:该目录包含与fe900000.usb控制器相关的调试信息。
  • fusb302-4-0022:该目录包含与fusb302-4-0022设备相关的调试信息,4为I2C总线编号,0022为I2C设备地址;
  • ohci:该目录包含OHCI主机控制器接口的调试信息;
  • tcpm-4-0022:该目录包含与tcpm-4-0022设备相关的调试信息,4为I2C总线编号,0022为I2C设备地址;
  • uvcvideo:该目录包含与uvcvideoUSB Video ClassUSB视频类)驱动程序相关的调试信息;
  • xhci:该目录包含XHCI主机控制器的调试信息;
5.2.1 查看工作模式

USB3.0 OTG0(DWC3/xHCI)控制器为例,对应设备节点usbdrd3_0: usb@fe800000

root@rk3399:~# ll /sys/kernel/debug/usb/fe800000.usb/
-rw-r--r--  1 root root 0 Jan  1  1970 link_state
-rw-r--r--  1 root root 0 Jan  1  1970 lsp_dump
-rw-r--r--  1 root root 0 Jan  1  1970 mode
-r--r--r--  1 root root 0 Jan  1  1970 regdump
-rw-r--r--  1 root root 0 Jan  1  1970 testmode

其中:

  • link_state:打印DWC3的链路状态;
  • regdump:打印DWC3控制器的寄存器状态信息;
  • mode:打印DWC3的工作模式;
  • testmode:设置DWC3HighSpeed的测试模式,用于眼图测试;

我们可以查看USB3.0 OTG0USB3.0 OTG1控制器的工作模式:

root@rk3399:~# cat /sys/kernel/debug/usb/fe800000.usb/mode      # OTG0 对应开发板Type-C/DP接口
otg
root@rk3399:~# cat /sys/kernel/debug/usb/fe900000.usb/mode      # OTG1  对应开发板CON5接口
host
5.2.2 查看xhci
root@rk3399:/# ll /sys/kernel/debug/usb/xhci/
drwxr-xr-x  6 root root 0 Jan  1  1970 xhci-hcd.0.auto/
drwxr-xr-x  6 root root 0 Jan  1  1970 xhci-hcd.1.auto/
root@rk3399:/# ll /sys/kernel/debug/usb/xhci/xhci-hcd.0.auto/
drwxr-xr-x 2 root root 0 Jan  1  1970 command-ring/
drwxr-xr-x 2 root root 0 Jan  1  1970 devices/
drwxr-xr-x 2 root root 0 Jan  1  1970 event-ring/
drwxr-xr-x 4 root root 0 Jan  1  1970 ports/
-r--r--r-- 1 root root 0 Jan  1  1970 reg-cap
-r--r--r-- 1 root root 0 Jan  1  1970 reg-ext-dbc:00
-r--r--r-- 1 root root 0 Jan  1  1970 reg-ext-legsup:00
-r--r--r-- 1 root root 0 Jan  1  1970 reg-ext-protocol:00
-r--r--r-- 1 root root 0 Jan  1  1970 reg-ext-protocol:01
-r--r--r-- 1 root root 0 Jan  1  1970 reg-op
-r--r--r-- 1 root root 0 Jan  1  1970 reg-runtime
5.2.3 查看ehci
root@rk3399:/# ll /sys/kernel/debug/usb/ehci/
drwxr-xr-x  2 root root 0 Jan  1  1970 fe380000.usb/
drwxr-xr-x  2 root root 0 Jan  1  1970 fe3c0000.usb/
root@rk3399:/# ll /sys/kernel/debug/usb/ehci/fe380000.usb/
-r--r--r-- 1 root root 0 Jan  1  1970 async
-r--r--r-- 1 root root 0 Jan  1  1970 bandwidth
-r--r--r-- 1 root root 0 Jan  1  1970 periodic
-r--r--r-- 1 root root 0 Jan  1  1970 registers
5.2.4 查看fusb302-4-0022
root@rk3399:/# ll /sys/kernel/debug/usb/fusb302-4-0022/
-r--r--r--  1 root root 0 Jan  1  1970 log
root@rk3399:/# cat /sys/kernel/debug/usb/fusb302-4-0022/log
[    3.588457] sw reset
[    3.589715] fusb302 device ID: 0x81
[    3.591218] pd := off
[    3.591222] vbus is already Off
[    3.591225] charge is already Off
[    3.591228] vconn is already Off
[    3.591588] pd header := Sink, Device
[    3.591658] cc1=Open, cc2=Open
[    3.593266] pd := off
[    3.593278] vbus is already Off
[    3.593283] charge is already Off
[    3.593288] vconn is already Off
[    3.593623] pd header := Sink, Device
[    3.593646] cc := Open
[    3.696903] start drp toggling
[    3.697640] IRQ: 0x80, a: 0x00, b: 0x00, status0: 0x83
[    3.697650] IRQ: VBUS_OK, vbus=On
[    3.698289] IRQ: 0x80, a: 0x00, b: 0x00, status0: 0x83
[    3.698298] IRQ: VBUS_OK, vbus=On
5.2.5 查看tcpm-4-0022
root@rk3399:/# ll /sys/kernel/debug/usb/tcpm-4-0022/
-r--r--r--  1 root root 0 Jan  1  1970 log
root@rk3399:/# cat /sys/kernel/debug/usb/tcpm-4-0022/log
[    3.589722] Setting usb_comm capable false
[    3.591231] Setting voltage/current limit 0 mV 0 mA
[    3.591241] polarity 0
[    3.591297] Requesting mux state 0, usb-role 0, orientation 0
[    3.591601] state change INVALID_STATE -> SNK_UNATTACHED [rev1 NONE_AMS]
[    3.591661] CC1: 0 -> 0, CC2: 0 -> 0 [state SNK_UNATTACHED, polarity 0, disconnected]
[    3.591669] 4-0022: registered
[    3.591681] Setting usb_comm capable false
[    3.593293] Setting voltage/current limit 0 mV 0 mA
[    3.593312] polarity 0
[    3.593318] Requesting mux state 0, usb-role 0, orientation 0
[    3.593641] cc:=0
[    3.595138] pending state change PORT_RESET -> PORT_RESET_WAIT_OFF @ 100 ms [rev1 NONE_AMS]
[    3.695257] state change PORT_RESET -> PORT_RESET_WAIT_OFF [delayed 100 ms]
[    3.695272] state change PORT_RESET_WAIT_OFF -> SNK_UNATTACHED [rev1 NONE_AMS]
[    3.695282] Start toggling
[    3.697687] VBUS

5.3 更改USB3.0 OTG工作模式

我们使用的开发板USB3.0 Type-C PHY1USB2.0 OTG PHY1物理接口被设计为USB3.0 Host Type-A,即VBUS被配置为了常供电,所以只能作为主设备为从设备供电,usbdrd_dwc3_1dr_mode属性只能配置为host

如果你的开发板USB3.0 Type-C PHY1USB2.0 OTG PHY1物理接口被设计为USB3.0 OTG Type-A的话,就可以尝试将USB3.0 OTG1控制器工作模式也设置为otg(同时支持主机/设备)。

不过Rockchip官方手册介绍中描述到:USB Gadget driver内核架构(可能只针对低版本内核比如4.19)只支持一个USB控制器配置为otg,所以如果你有兴趣的话,可以测试一下看看能不能同时将USB3.0 OTG0USB3.0 OTG1控制器都配置为otg模式,具体流程参考下面。

5.3.1 修改usbdrd_dwc3_1设备节点

修改arch/arm64/boot/dts/rockchip/rk3399-evb.dts设备节点usbdrd_dwc3_1设备节点:

&usbdrd_dwc3_1 {
	/* 配置dr_mode为otg */
	dr_mode = "otg";
	status = "okay";
};

如果VBUS为常供电(也即系统开机后,VBUS一直为高),则不需要配置vbus-supply属性,但需要增加如下的属性,否则,会出现 USB无法正常连接的情况。这里应修改对应的u2phy1_otg节点:

&u2phy1_otg {
	rockchip,vbus-always-on;
	/* phy-supply = <&vcc5v2_host>; */
	status = "okay";
};
5.3.2 测试

重新编译内核并烧录,查看USB3.0 OTG0USB3.0 OTG1控制器的工作模式;

root@rk3399:~# cat /sys/kernel/debug/usb/fe800000.usb/mode      # OTG0 对应开发板Type-C/DP接口
otg
root@rk3399:~# cat /sys/kernel/debug/usb/fe900000.usb/mode      # OTG1  对应开发板CON5接口
otg

更改工作模式为Host(主机),这种做法只会临时有效:

root@rk3399:/lib/modules/6.3.0# echo host > /sys/kernel/debug/usb/fe800000.usb/mode   
root@rk3399:/lib/modules/6.3.0# echo host > /sys/kernel/debug/usb/fe900000.usb/mode 

然后将开发板CON5Type-C/DP这两个接口接上USB设备测试。

参考文章

[1] PD快充 - fusb302驱动调试笔记

[2] RK3399 Android7.1 USB模块中的控制器和PHY

[3] RK3399ANDROID7.1 USB TYPE-A的配置

[4] USB gadget configfs的原理及单片机实现

[5] Rockchip_Developer_Guide_USB_CN.pdf

[6] Rockchip_RK3399_Developer_Guide_USB_DTS_CN.pdf

[7] RK平台如何配置USB功能

[8] linux usb gadget驱动详解(一)

[9] linux usb gadget驱动详解(二)

[10] ipkvmRK3568 usb gadget

[11] linux usb gadget hid模拟鼠标键盘configfs

[12] LINUX嵌入式模拟键盘 2022-05-21

[13] usb hid gadget驱动

[14 ] RK3399驱动开发 | 18 - 使用usb3.0作为device模拟u

[15] linux driver probe deferral 机制

[16] usb gadget configfs 验证

[17] 6.61、USB如何模拟HID设备

[18] USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)

[19] 基于rk3308实现 linux usb slave hid设备模拟键盘,鼠标等

[20] zynqusb gadget模拟网口、U盘、串口(转)

[21] RK3399 模拟成鼠标触摸屏

[22] raw-gadget-USB Raw Gadget-Linux USB Gadget子系统的底层接口

[23] xairy/raw-gadge

[24] USB Support

[25] 找到一篇关于Linux usb gadget非常使用的说明和使用教程

posted @ 2023-09-17 01:17  大奥特曼打小怪兽  阅读(3300)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步