20230213在AIO-3568J开发板在原厂Android12下跑通ap6275s

2023-05-16

20230213在AIO-3568J开发板在原厂Android12下跑通ap6275s
2023/2/13 8:59


一、从AIO-3568的Android11的kernel中抠出来AP6275S的驱动:
1、
Z:\android12-rk3588-new\kernel-4.19\arch\arm64\configs\rockchip_defconfig
# CONFIG_WLAN_VENDOR_QUANTENNA is not set
CONFIG_WL_ROCKCHIP=y
CONFIG_WIFI_BUILD_MODULE=y
CONFIG_AP6XXX=m
CONFIG_RTL8723CS=m
CONFIG_RTL8821CS=m
CONFIG_RTL8822BS=m
CONFIG_LTE=y
修改为:
CONFIG_WL_ROCKCHIP=y
CONFIG_WIFI_BUILD_MODULE=y
#CONFIG_AP6XXX=m
# CONFIG_AP6XXX is not set
CONFIG_AP6275S=m

CONFIG_RTL8723CS=m
CONFIG_RTL8821CS=m
CONFIG_RTL8822BS=m
CONFIG_LTE=y

 


2、
Z:\android12-rk3588-new\kernel-4.19\drivers\net\wireless\rockchip_wlan\rkwifi\Kconfig
config AP6XXX_WIFI6
    tristate "support wifi6(80211ax)"
    #depends on MMC && WLAN_80211
    select CFG80211
    select MAC80211
    help
      This driver supports wifi6 for ap6xxx chipset.

      This driver uses the kernel's wireless extensions subsystem.

      If you choose to build a module, it'll be called dhd. Say M if
      unsure.


config AP6275S
    tristate "support wifi6 ap6275s(80211ax)"
    #depends on MMC && WLAN_80211
    select CFG80211
    select MAC80211
    ---help---
      This driver supports wifi6 for ap6275s chipset.

      This driver uses the kernel's wireless extensions subsystem.

      If you choose to build a module, it'll be called dhd. Say M if
      unsure.

config AP6XXX_INDEP_POWER
    tristate "support WiFi keepalive during host shutdown"
    select CFG80211
    select MAC80211

endchoice

 


3、
Z:\android12-rk3588-new\kernel-4.19\drivers\net\wireless\rockchip_wlan\rkwifi\Makefile

# SPDX-License-Identifier: GPL-2.0
#rkwifi packed Makefile
# (gwl)

obj-$(CONFIG_AP6XXX) += bcmdhd/
obj-$(CONFIG_AP6XXX_WIFI6) += bcmdhd_wifi6/
obj-$(CONFIG_AP6275S) += ap6275s/
obj-$(CONFIG_AP6XXX_INDEP_POWER) += bcmdhd_indep_power/

.PHONY: clean

clean:
    find . -name '*.o*' -exec rm -f {} \; 
 

 


4、【全部拷贝过来!】
Z:\android12-rk3588-new\kernel-4.19\drivers\net\wireless\rockchip_wlan\rkwifi\ap6275s\Makefile

 

 


二、
Z:\android12-rk3588-new\kernel-4.19\arch\arm64\boot\dts\rockchip\rk3568-evb.dtsi
1、
    wireless_wlan: wireless-wlan {
        compatible = "wlan-platdata";
        rockchip,grf = <&grf>;
        wifi_chip_type = "ap6398s";

        status = "okay";
    };
修改为:
    wireless_wlan: wireless-wlan {
        compatible = "wlan-platdata";
        rockchip,grf = <&grf>;
        wifi_chip_type = "ap6398s";
        pinctrl-names = "default";
        pinctrl-0 = <&wifi_host_wake_irq>;
        WIFI,host_wake_irq = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;

        status = "okay";
    };

 


2、
 /*
  * There are 10 independent IO domains in RK3566/RK3568, including PMUIO[0:2] and VCCIO[1:7].
  * 1/ PMUIO0 and PMUIO1 are fixed-level power domains which cannot be configured;
  * 2/ PMUIO2 and VCCIO1,VCCIO[3:7] domains require that their hardware power supply voltages
  *    must be consistent with the software configuration correspondingly
  *    a/ When the hardware IO level is connected to 1.8V, the software voltage configuration
  *       should also be configured to 1.8V accordingly;
  *    b/ When the hardware IO level is connected to 3.3V, the software voltage configuration
  *       should also be configured to 3.3V accordingly;
  * 3/ VCCIO2 voltage control selection (0xFDC20140)
  *    BIT[0]: 0x0: from GPIO_0A7 (default)
  *    BIT[0]: 0x1: from GRF
  *    Default is determined by Pin FLASH_VOL_SEL/GPIO0_A7:
  *    L:VCCIO2 must supply 3.3V
  *    H:VCCIO2 must supply 1.8V
  */
&pmu_io_domains {
    status = "okay";
    pmuio2-supply = <&vcc3v3_pmu>;
    vccio1-supply = <&vccio_acodec>;
    vccio3-supply = <&vccio_sd>;
    vccio4-supply = <&vcc_3v3>;
    vccio5-supply = <&vcc_3v3>;
    vccio6-supply = <&vcc_3v3>;

    vccio7-supply = <&vcc_3v3>;
};
修改为:、
&pmu_io_domains {
    status = "okay";
    pmuio1-supply = <&vcc3v3_pmu>;
    pmuio2-supply = <&vcc3v3_pmu>;
    vccio1-supply = <&vccio_acodec>;
    vccio3-supply = <&vccio_sd>;
    vccio4-supply = <&vcc_1v8>;
    vccio5-supply = <&vcc_3v3>;
    vccio6-supply = <&vcc_1v8>;
    vccio7-supply = <&vcc_3v3>;
};


三、
Z:\android12-rk3588-new\kernel-4.19\arch\arm64\boot\dts\rockchip\rk3568-evb2-lp4x-v10.dtsi
1、
    ext_cam_clk: external-camera-clock {
        compatible = "fixed-clock";
        clock-frequency = <24000000>;
        clock-output-names = "CLK_CAMERA_24MHZ";
        #clock-cells = <0>;
    };
};


// firefly U mast add here for AP6275S
&sdmmc2 {
    max-frequency = <150000000>;
    supports-sdio;
    bus-width = <4>;
    disable-wp;
    cap-sd-highspeed;
    cap-sdio-irq;
    keep-power-in-suspend;
    pinctrl-names = "default";
    pinctrl-0 = <&sdmmc2m0_bus4 &sdmmc2m0_cmd &sdmmc2m0_clk>;
    sd-uhs-sdr104;
    mmc-pwrseq = <&sdio_pwrseq>;
    non-removable;
    status = "disabled";
};

&uart8 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart8m0_xfer &uart8m0_ctsn>;
};

&spi1 {
    status = "disabled";
    max-freq = <48000000>;
    dev-port = <0>;
    pinctrl-0 = <&spi1m1_pins>;
    pinctrl-1 = <&spi1m1_pins_hs>;

    spi_wk2xxx: spi_wk2xxx@00{
        status = "disabled";
        compatible = "firefly,spi-wk2xxx";
        reg = <0x00>;
        spi-max-frequency = <10000000>;
        //power-gpio = <&pca9555 PCA_IO1_7 GPIO_ACTIVE_HIGH>;
        //reset-gpio = <&pca9555 PCA_IO1_1 GPIO_ACTIVE_HIGH>;
        irq-gpio = <&gpio0 RK_PA6 IRQ_TYPE_EDGE_FALLING>;
        cs-gpio = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>;
        /* rk3399 driver support SPI_CPOL | SPI_CPHA | SPI_CS_HIGH */
        //spi-cpha;     /* SPI mode: CPHA=1 */
        //spi-cpol;     /* SPI mode: CPOL=1 */
        //spi-cs-high;
    };
};
// firefly

&combphy0_us {
    status = "okay";
};

&combphy1_usq {
    status = "okay";
};

&combphy2_psq {
    status = "okay";
};

 


2、
&combphy0_us {
    status = "okay";
};

&combphy1_usq {
    status = "okay";
};

&combphy2_psq {
    status = "okay";
};


// for AIO-3568J, you must add this Block for USB3
// firefly
&csi2_dphy0 {
        status = "okay";
};

&csi2_dphy1 {
        status = "disabled";
};

&csi2_dphy2 {
        status = "disabled";
};

/*
// firefly


 * video_phy0 needs to be enabled
 * when dsi0 is enabled
 */
&dsi0 {
    status = "okay";
};

&dsi0_in_vp0 {
    status = "disabled";
};

 

如果没有这里,在你配置AP6275S的时钟为内置时钟之后,USB3就会打不开了!

 

 

[  807.048984] read descriptors
[  807.049025] read strings
[  808.088976] read descriptors
[  808.089018] read strings
[  808.089867] init: processing action (sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1) from (/system/etc/init/hw/init.usb.configfs.rc:20)
[  808.091168] init: Command 'symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:22) took 0ms and failed: symlink() failed: File exists
[  808.091339] init: Command 'write /config/usb_gadget/g1/UDC ${sys.usb.controller}' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:23) took 0ms and failed: property 'sys.usb.controller' doesn't exist while expanding '${sys.usb.controller}'
[  809.139059] read descriptors
[  809.139102] read strings
[  809.139957] init: processing action (sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1) from (/system/etc/init/hw/init.usb.configfs.rc:20)
[  809.141255] init: Command 'symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:22) took 0ms and failed: symlink() failed: File exists
[  809.141432] init: Command 'write /config/usb_gadget/g1/UDC ${sys.usb.controller}' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:23) took 0ms and failed: property 'sys.usb.controller' doesn't exist while expanding '${sys.usb.controller}'
[  810.188992] read descriptors
[  810.189030] read strings
[  810.189891] init: processing action (sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1) from (/system/etc/init/hw/init.usb.configfs.rc:20)
[  810.191162] init: Command 'symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:22) took 0ms and failed: symlink() failed: File exists
[  810.191335] init: Command 'write /config/usb_gadget/g1/UDC ${sys.usb.controller}' action=sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1 (/system/etc/init/hw/init.usb.configfs.rc:23) took 0ms and failed: property 'sys.usb.controller' doesn't exist while expanding '${sys.usb.controller}'
[  811.232331] read descriptors
[  811.232374] read strings
[  811.233188] init: processing action (sys.usb.config=adb && sys.usb.configfs=1 && sys.usb.ffs.ready=1) from (/system/etc/init/hw/init.usb.configfs.rc:20)
[  812.282315] read descriptors


3、
&dsi1_panel {
    power-supply = <&vcc3v3_lcd1_n>;
};

// ToyBrick U must close this block for AP6275S
///*
//&gmac0 {
//    phy-supply = <&qsgmii_3v3>;
//    phy-mode = "qsgmii";
//    rockchip,xpcs = <&xpcs>;
//
//    assigned-clocks = <&cru SCLK_GMAC0_RX_TX>;
//    assigned-clock-parents = <&gmac0_xpcsclk>;
//
//    power-domains = <&power RK3568_PD_PIPE>;
//    phys = <&combphy1_usq PHY_TYPE_QSGMII>;
//    phy-handle = <&qsgmii_phy0>;
//
//    status = "okay";
//};
//
//&gmac1 {
//    phy-supply = <&qsgmii_3v3>;
//    phy-mode = "qsgmii";
//
//    snps,reset-gpio = <&gpio3 RK_PD5 GPIO_ACTIVE_LOW>;
//    snps,reset-active-low;
//    /* Reset time is 20ms, 100ms for rtl8211f */
//    snps,reset-delays-us = <0 20000 100000>;
//
//    assigned-clocks = <&cru SCLK_GMAC1_RX_TX>;
//    assigned-clock-parents = <&gmac1_xpcsclk>;
//
//    pinctrl-names = "default";
//    pinctrl-0 = <&gmac1m1_miim>;
//
//    power-domains = <&power RK3568_PD_PIPE>;
//    phy-handle = <&qsgmii_phy1>;
//
//    status = "okay";
//};
//*/

/*
 * power-supply should switche to vcc3v3_lcd1_n
 * when mipi panel is connected to dsi1.
 */
&gt1x {
    power-supply = <&vcc3v3_lcd0_n>;
};

 


4、
&pcie2x1 {
    reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>;
    vpcie3v3-supply = <&vcc3v3_pcie>;
    status = "okay";
};

&pcie3x2 {
    reset-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>;
    vpcie3v3-supply = <&vcc3v3_pcie>;
    status = "okay";
};

// fireffly
//&hym8563 {
//    status = "okay";
//};
//
//&mc3230 {
//    status = "okay";
//};

//&uart3 {
//    status = "okay";
//};

&uart4 {
        status = "okay";
};

&spi1 {
    status = "okay";
};

//&spi_wk2xxx {
//    status = "okay";
//};
// firefly

&pcie30phy {
    status = "okay";
};
// fireffly

&pinctrl {
    headphone {
        hp_det: hp-det {
            rockchip,pins = <4 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
        };
    };

 


5、
    sii902x {
        sii902x_hdmi_int: sii902x-hdmi-int {
            rockchip,pins = <3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };


// ToyBrick U must close this block for AP6275S (BT)
/*
    sdio-pwrseq {
        wifi_enable_h: wifi-enable-h {
            rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
        };

        wifi_32k: wifi-32k {
            rockchip,pins = <2 RK_PC6 1 &pcfg_pull_none>;
        };
    };
*/


    wireless-wlan {
        wifi_host_wake_irq: wifi-host-wake-irq {
            rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_down>;
        };
    };

    //wireless-bluetooth {
    //    uart1_gpios: uart1-gpios {
    //        rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
    //    };
    //};

    usb {
        vcc_hub_power_en: vcc-hub-power-en {
            rockchip,pins = <3 RK_PA2 RK_FUNC_GPIO &pcfg_pull_none>;
        };

        vcc_hub_reset_en: vcc-hub-reset-en {
            rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };

};

 


6、
// ToyBrick U must close this block for AP6275S
//&pmu_io_domains {
//    vccio6-supply = <&vcc_3v3>;
//};
//
//&pwm3 {
//    status = "okay";
//
//    compatible = "rockchip,remotectl-pwm";
//    remote_pwm_id = <3>;
//    handle_cpu_id = <1>;
//    remote_support_psci = <0>;
//    pinctrl-names = "default";
//    pinctrl-0 = <&pwm3_pins>;
//
//    ir_key1 {
//        rockchip,usercode = <0x4040>;
//        rockchip,key_table =
//            <0xf2    KEY_REPLY>,
//            <0xba    KEY_BACK>,
//            <0xf4    KEY_UP>,
//            <0xf1    KEY_DOWN>,
//            <0xef    KEY_LEFT>,
//            <0xee    KEY_RIGHT>,
//            <0xbd    KEY_HOME>,
//            <0xea    KEY_VOLUMEUP>,
//            <0xe3    KEY_VOLUMEDOWN>,
//            <0xe2    KEY_SEARCH>,
//            <0xb2    KEY_POWER>,
//            <0xbc    KEY_MUTE>,
//            <0xec    KEY_MENU>,
//            <0xbf    0x190>,
//            <0xe0    0x191>,
//            <0xe1    0x192>,
//            <0xe9    183>,
//            <0xe6    248>,
//            <0xe8    185>,
//            <0xe7    186>,
//            <0xf0    388>,
//            <0xbe    0x175>;
//    };
//
//    ir_key2 {
//        rockchip,usercode = <0xff00>;
//        rockchip,key_table =
//            <0xf9    KEY_HOME>,
//            <0xbf    KEY_BACK>,
//            <0xfb    KEY_MENU>,
//            <0xaa    KEY_REPLY>,
//            <0xb9    KEY_UP>,
//            <0xe9    KEY_DOWN>,
//            <0xb8    KEY_LEFT>,
//            <0xea    KEY_RIGHT>,
//            <0xeb    KEY_VOLUMEDOWN>,
//            <0xef    KEY_VOLUMEUP>,
//            <0xf7    KEY_MUTE>,
//            <0xe7    KEY_POWER>,
//            <0xfc    KEY_POWER>,
//            <0xa9    KEY_VOLUMEDOWN>,
//            <0xa8    KEY_VOLUMEDOWN>,
//            <0xe0    KEY_VOLUMEDOWN>,
//            <0xa5    KEY_VOLUMEDOWN>,
//            <0xab    183>,
//            <0xb7    388>,
//            <0xe8    388>,
//            <0xf8    184>,
//            <0xaf    185>,
//            <0xed    KEY_VOLUMEDOWN>,
//            <0xee    186>,
//            <0xb3    KEY_VOLUMEDOWN>,
//            <0xf1    KEY_VOLUMEDOWN>,
//            <0xf2    KEY_VOLUMEDOWN>,
//            <0xf3    KEY_SEARCH>,
//            <0xb4    KEY_VOLUMEDOWN>,
//            <0xbe    KEY_SEARCH>;
//    };
//
//    ir_key3 {
//        rockchip,usercode = <0x1dcc>;
//        rockchip,key_table =
//            <0xee    KEY_REPLY>,
//            <0xf0    KEY_BACK>,
//            <0xf8    KEY_UP>,
//            <0xbb    KEY_DOWN>,
//            <0xef    KEY_LEFT>,
//            <0xed    KEY_RIGHT>,
//            <0xfc    KEY_HOME>,
//            <0xf1    KEY_VOLUMEUP>,
//            <0xfd    KEY_VOLUMEDOWN>,
//            <0xb7    KEY_SEARCH>,
//            <0xff    KEY_POWER>,
//            <0xf3    KEY_MUTE>,
//            <0xbf    KEY_MENU>,
//            <0xf9    0x191>,
//            <0xf5    0x192>,
//            <0xb3    388>,
//            <0xbe    KEY_1>,
//            <0xba    KEY_2>,
//            <0xb2    KEY_3>,
//            <0xbd    KEY_4>,
//            <0xf9    KEY_5>,
//            <0xb1    KEY_6>,
//            <0xfc    KEY_7>,
//            <0xf8    KEY_8>,
//            <0xb0    KEY_9>,
//            <0xb6    KEY_0>,
//            <0xb5    KEY_BACKSPACE>;
//    };
//};
//
//&pwm7 {
//    status = "disabled";
//};
//
//&route_dsi0 {
//    status = "okay";
//    connect = <&vp1_out_dsi0>;
//};
// ToyBrick

 


7、
&sdio_pwrseq {
    clocks = <&pmucru CLK_RTC_32K>;
    pinctrl-0 = <&wifi_enable_h &wifi_32k>;

    /*
     * On the module itself this is one of these (depending
     * on the actual card populated):
     * - SDIO_RESET_L_WL_REG_ON
     * - PDN (power down when low)
     */
    reset-gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_LOW>;

};

&sdmmc1 {
    max-frequency = <150000000>;
    supports-sdio;
    bus-width = <4>;
    disable-wp;
    cap-sd-highspeed;
    cap-sdio-irq;
    keep-power-in-suspend;
    mmc-pwrseq = <&sdio_pwrseq>;
    non-removable;
    pinctrl-names = "default";
    pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>;
    sd-uhs-sdr104;
    status = "okay";
};

&sdmmc2 {
    status = "disabled";
};

&uart1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>;
};
修改为:
&sdio_pwrseq {
    status = "okay";
    reset-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_LOW>;
    post-power-on-delay-ms = <100>;

};


// ToyBrick U must close this block for AP6275S
//&sdmmc1 {
//    max-frequency = <150000000>;
//    supports-sdio;
//    bus-width = <4>;
//    disable-wp;
//    cap-sd-highspeed;
//    cap-sdio-irq;
//    keep-power-in-suspend;
//    mmc-pwrseq = <&sdio_pwrseq>;
//    non-removable;
//    pinctrl-names = "default";
//    pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>;
//    sd-uhs-sdr104;
//    status = "okay";
//};

&sdmmc2 {
    status = "okay";
};

&uart1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>;
};

 


8、
&vcc3v3_lcd1_n {
    gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
    enable-active-high;
};

&wireless_wlan {
    pinctrl-names = "default";
    pinctrl-0 = <&wifi_host_wake_irq>;
    WIFI,host_wake_irq = <&gpio2 RK_PB2 GPIO_ACTIVE_HIGH>;

};

&wireless_bluetooth {
    compatible = "bluetooth-platdata";
    clocks = <&pmucru CLK_RTC_32K>;
    clock-names = "ext_clock";
    //wifi-bt-power-toggle;
    uart_rts_gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default", "rts_gpio";
    pinctrl-0 = <&uart1m0_rtsn>;
    pinctrl-1 = <&uart1_gpios>;
    BT,reset_gpio    = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>;
    BT,wake_gpio     = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>;
    BT,wake_host_irq = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>;

    status = "okay";
};

&xpcs {
    status = "okay";
};
修改为:
&vcc3v3_lcd1_n {
    gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
    enable-active-high;
};


&wireless_wlan {
    wifi_chip_type = "ap6275s";
    status = "okay";

};

&wireless_bluetooth {
    BT,wake_gpio     = <&gpio3 RK_PB5 GPIO_ACTIVE_HIGH>;
    BT,wake_host_irq = <&gpio2 RK_PD7 GPIO_ACTIVE_HIGH>;

    status = "okay";
};

&xpcs {
    status = "okay";
};

相关的原理图:

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

20230213在AIO-3568J开发板在原厂Android12下跑通ap6275s 的相关文章

随机推荐