久综合色-久综合网-玖草影视-玖草资源在线-亚洲黄色片子-亚洲黄色片在线观看

Hello! 歡迎來到小浪云!


Linux 設備樹 devicetree(DTS)入門


avatar
小浪云 2025-01-06 121

ARM devicetree的來源

在過去的arm linux中,存在大量的冗余代碼。這些設備代碼與特定公司的單板啟動或運行細節緊密耦合,無法被重用或移植。同時,內核缺乏引導標準,導致代碼不斷膨脹。最終,由于tony lindgren向linus發送了一封郵件,請求提交omap平臺代碼的修改,并附上了修改內容以及如何解決合并沖突的方法,使得linus怒不可遏地抱怨道:“該死。伙計們,這整個arm的事情真是個討厭的麻煩。”(linus對arm的代碼肯定已經忍耐了很久了)。

經過討論后,對ARM平臺相關代碼做出了一些規范:

  1. ARM的核心代碼仍然存放在arch/arm目錄下;
  2. ARM SoC核心架構代碼存放在arch/arm目錄下;
  3. ARM SoC周邊外設模塊的驅動存放在drivers目錄下;
  4. ARM SoC特定的代碼存放在arch/arm/mach-xxx目錄下;
  5. ARM SoC板級特定的代碼被移除,由Device Tree機制來傳遞硬件拓撲和硬件資源信息。

從本質上講,Device Tree改變了以前將硬件設備配置信息硬編碼到內核代碼中的方式,改為使用引導加載程序傳遞一個描述性的數據結構。

DTS知識介紹

Arm系統啟動,硬件設備可以通過DTS(devicetree)或ACPI引導初始化,這里只講DTS方式,ACPI是由BIOS配置。

Linux 設備樹 devicetree(DTS)入門

如上圖,一般來說,arm內核通過dts引導啟動,需要內核Image、dtb和Filesystem,其中dtb是由dts通過dtc工具生成,里面包括初始化設備的硬件信息。內核Image啟動過程中會解析dtb中內容,并根據信息初始化設備平臺。這里提一句,dts由雖然由用戶配置,但是配置必須與硬件信息相匹配,否則會出現初始化失敗或設備部分功能不正常的問題。

DTS描述

Device Tree由一系列被命名的結點(node)和屬性(Property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中),CPU的數量和類別、內存基地址和大、timer時鐘、外設連接、中斷配置、串口等。內核在啟動過程中會解析每個node的硬件配置信息,根據這些信息初始化設備。

舉例,如下是arm gicv3中斷控制器的節點配置信息(來源Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt):

gic:?interrupt-controller@2cf00000?{ ?compatible?=?"arm,gic-v3"; ?#interrupt-cells?=?; ?#address-cells?=?; ?#size-cells?=?; ?ranges; ?interrupt-controller; ?reg?=?,??//?GICD ?,?//?GICR ?,?//?GICC ?,?//?GICH ?;??//?GICV ?interrupts?=?; ? ?msi-controller; ?mbi-ranges?=?; ? ?gic-its@2c200000?{ ?compatible?=?"arm,gic-v3-its"; ?msi-controller; ?#msi-cells?=?; ?reg?=?; ?}; ?}; 

gic: interrupt-controller@2cf00000:表示node節點信息,interrupt-controller@2cf00000是node節點的名稱,一般命名規范是 設備名@基地址或設備名,其中冒號前面gic可以看過node的“小名”,后續關聯node節點可以直接”&gic”;

{}中內容:interrupt-controller的屬性,包括:

1)compatible = “arm,gic-v3”; compatible名稱,gicv3驅動代碼匹配”arm,gic-v3”后才會執行初始化probe代碼;

2)#interrupt-cells = ; 表示interrupt由3部分組成,對應interrupts = ,分別是中斷類型(1表示PPI),中斷號9和中斷觸發方式(4表示上升沿觸發);

3)#address-cells = ;

#size-cells = ; 表示地址信息用64位表示,比如reg = 表示GICD的基地址是0x02f000000(其中0x是高32bit,0x2f000000是低32bit),size是0x10000;

4)msi-controller; 表示msi,此字符串含義可以具體查看驅動代碼,驅動會get到這個字符串,然后做判斷,有會走A,沒有就跳過。

5)gic-its@2c200000 表示its節點的配置,gic-v3引入了its來接收SPI中斷;

如上,便是dts的基本配置,更加詳細的介紹可以參考linux內核源碼:Documentation/devicetree/,每個硬件設備配置格式和屬性都有描述。

重要點

一般來說,Linux菜鳥級別只要會看dts即可,linux驅動開發才需要掌握dts的每行含義,因為任一行出錯,都可能導致驅動某項功能失效。下面我列一些經常遇到的問題:

1.dts和dtb如何轉換

內核源碼下scripts/dtc/dtc,dtc工具只有內核編譯時才會編譯,所以如果希望得到,先編譯一遍內核即可。

轉換命令:

dts轉dtb:dtc –I dts –O dtb test.dts –o test.dtb

dtb轉dts: dtc –I dtb –O dts test.dtb –o test.dts

2.dts中node節點如何查看,每行又是什么意義

這個問題經常遇到,不知道設備節點配置的意義,其實每個配置都可以根據compatible在Documentation/devicetree/中查到說明,只是不搞內核不知道這個方法,比如dts中看到:

v2m_serial0:?uart@090000?{ ??compatible?=?"arm,pl011",?"arm,primecell"; ?reg?=?; ?interrupts?=?; ?clocks?=?,?; ?clock-names?=?"uartclk",?"apb_pclk"; ?}; 

到linux內核源碼運行:

cuibixuan@ubuntu:~/git/linux/Documentation/devicetree/bindings$?cd?Documentation/devicetree/bindings/ cuibixuan@ubuntu:~/git/linux/Documentation/devicetree/bindings$?grep?"arm,pl011"?-rn?* clock/hi3660-clock.txt:41:?compatible?=?"arm,pl011",?"arm,primecell"; clock/hi3670-clock.txt:37:?compatible?=?"arm,pl011",?"arm,primecell"; clock/lsi,axm5516-clks.txt:22:?compatible?=?"arm,pl011",?"arm,primecell"; clock/hix5hd2-clock.txt:25:?compatible?=?"arm,pl011",?"arm,primecell"; dma/ste-dma40.txt:130:?compatible?=?"arm,pl011",?"arm,primecell"; dma/snps-dma.txt:65:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/axis,artpec6-pinctrl.txt:71:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/axis,artpec6-pinctrl.txt:80:?compatible?=?"arm,pl011",?"arm,primecell"; pinctrl/ste,nomadik.txt:141:?compatible?=?"arm,pl011",?"arm,primecell"; serial/pl011.txt:4:-?compatible:?must?be?"arm,primecell",?"arm,pl011",?"zte,zx296702-uart" serial/pl011.txt:44:?compatible?=?"arm,pl011",?"arm,primecell"; vim?serial/pl011.txt 

如下圖,有說明,有舉例,很清晰

Linux 設備樹 devicetree(DTS)入門

如果還不懂,或者找不到,那么恭喜你,你要看驅動代碼了

cuibixuan@ubuntu:~/git/linux$?cd?drivers/tty/serial/ cuibixuan@ubuntu:~/git/linux/drivers/tty/serial$?grep?"arm,pl011"?-rn?* amba-pl011.c:2473:OF_EARLYCON_DECLARE(pl011,?"arm,pl011",?pl011_early_console_setup); 

3.內核初始化設備驅動,根據compatible來決定是否初始化

compatible的字符串,是驅動匹配的關鍵,如果匹配不到,那么就不會初始化。這點設計非常棒,在編譯Image完畢后,用戶還可以根據dts選配啟動哪些硬件。

一直有人有疑問,既然內核都有config選項來決定了,為什么還要dts來再加一道門禁呢。你可以設想下,如果內核啟動配置了哪些config就初始化哪些功能,那么啟動要多么繁瑣呀,大部分都是你不知道的功能都在啟動,萬一失敗了,還得查看哪里問題,至少編譯一次內核。尤其是嵌入式設備,要精簡,更要達到“我只關心我配置的設備”的目的。

好了扯遠了,以上面串口驅動代碼舉例(提示:驅動代碼的開頭是probe函數,一般翻到代碼底部即可,上面都是功能的實現),

static?const?Struct?of_device_id?sbsa_uart_of_match[]?=?{ ?{?.compatible?=?"arm,sbsa-uart",?}, ?{}, }; MODULE_DEVICE_TABLE(of,?sbsa_uart_of_match); ? static?struct?platform_driver?arm_sbsa_uart_platform_driver?=?{ ?.probe?=?sbsa_uart_probe, ?.remove??=?sbsa_uart_remove, ?.driver?=?{ ?.name?=?"sbsa-uart", ?.of_match_table?=?of_match_ptr(sbsa_uart_of_match), ?.acpi_match_table?=?ACPI_PTR(sbsa_uart_acpi_match), ?.suppress_bind_attrs?=?IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), ?}, }; ? … ? static?int?__init?pl011_init(void) { ?printk(KERN_INFO?"Serial:?AMBA?PL011?UART?driver "); ? ?if?(platform_driver_register(&arm_sbsa_uart_platform_driver)) ?pr_warn("could?not?register?SBSA?UART?platform?driver "); ?return?amba_driver_register(&pl011_driver); } 

內核通過platform_driver_register()來注冊設備,arm_sbsa_uart_platform_driver是初始化成struct platform_driver的結構體,結構體指定了設備的probe,remove等鉤子函數,.driver記錄設備的name,.compatible = “arm,sbsa-uart”(of_match_table來匹配),這里多提一句,.of_match_table是dts啟動匹配字符串”arm,sbsa-uart”,.acpi_match_table是ACPI啟動匹配PTR。

如上,如果dts中node節點有compatible帶”arm,sbsa-uart”,那么就會執行指定的鉤子函數.probe = sbsa_uart_probe,函數再進行node節點其他參數的解析(說是解析,就是get字符串或數值,在進行對應初始化或讀寫寄存器)。

4.reg=和interrupts=里面數值代表什么?

reg和interrupt數值都有具體的含義,上文提到:

#interrupt-cells = ; 表示interrupt由3部分組成,對應interrupts = ,分別是中斷類型(1表示PPI),中斷號9和中斷觸發方式(4表示高電平觸發);

這里再補充一下:

interrupts = ,分別是

中斷類型:0表示SPI,1表示PPI

中斷號9,其中PPI是[0-15],SPI范圍[32-1019]

中斷觸發方式:

1 = low-to-high edge triggered

2 = high-to-low edge triggered

4 = active high edge triggered

8 = active low edge triggered

恩,翻譯一下就時上升沿觸發、下降沿觸發、高電平觸發、低電平觸發。

#address-cells = ;

#size-cells = ; 表示地址信息用64位表示,比如reg = 表示GICD的基地址是0x02f000000(其中0x是高32bit,0x2f000000是第32bit),size是0x10000;

舉例, = 0x12f000000,= 0x100000001

5.dts支持include

對于可復用的描述節點,支持以include方式被多個dts包含,可放在dtsi文件,在dts中以

#include “uart.dtsi”包含使用。

dts文件:Foundation-platform.dts

默認變量

一般dts首個{}前面的信息表示全局默認變量,意思即node無特殊配置,則默認采用這里的配置

????????#address-cells?=?;?//地址長度64bit ????????#size-cells?=?;??//size長度32bit ????????model?=?"V2P-AARCH64";?//model名稱 ????????compatible?=?"arm,vexpress,v2p-aarch64",?"arm,vexpress"; ????????interrupt-parent?=?;?//中斷parent是gic 

CPU配置

????????cpus?{ ????????????????#address-cells?=?;? ????????????????#size-cells?=?;  //?配置cpu0-cpu3的信息,最終啟動4核 ????????????????cpu@0?{ ????????????????????????device_type?=?"cpu";??//設備類型:cpu ????????????????????????compatible?=?"arm,armv8";?//表示armv8的cpu ????????????????????????reg?=?;?//cpu信息 ????????????????????????enable-method?=?"spin-table";?//采用spintable方式拉起從核 ????????????????????????cpu-release-addr?=?;?//cpu初啟動的pc指針存放位置 ????????????????}; ????????????????cpu@1?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????????????cpu@2?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????????????cpu@3?{ ????????????????????????device_type?=?"cpu"; ????????????????????????compatible?=?"arm,armv8"; ????????????????????????reg?=?; ????????????????????????enable-method?=?"spin-table"; ????????????????????????cpu-release-addr?=?; ????????????????}; ????????}; 

內存配置

????????memory@80000000?{ ????????????????device_type?=?"memory"; //此節點配置內存,內存2塊: //起始地址:0x80000000,?長度0x80000000; //起始地址:0x880000000,?長度0x80000000; ????????????????reg?=?; ????????}; 

中斷控制器

????????gic:?interrupt-controller@2c001000?{ //?中斷控制器使用?cortex?a15,gic ????????????????compatible?=?"arm,cortex-a15-gic"; ????????????????#interrupt-cells?=?; ????????????????#address-cells?=?; ????????????????interrupt-controller; //?GICD,GICH等信息,具體參考Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt //-?reg?:?Specifies?base?physical?address(s)?and?size?of?the?GIC?registers.?The ??first?region?is?the?GIC?distributor?register?base?and?size.?The?2nd?region?is ??the?GIC?cpu?interface?register?base?and?size. ????????????????reg?=?, ??????????????????????, ??????????????????????, ??????????????????????; //?虛擬化使用,-?interrupts?:?VGIC?maintenance?interrupt. //*?GIC?virtualization?extensions?(VGIC) ????????????????interrupts?=?; ????????}; 

PMU

????????pmu?{ //?使用armv8?pmuv3 ????????????????compatible?=?"arm,armv8-pmuv3"; //?pmu的中斷配置,這段配置完畢,即可以使用arm?cpu的pmu功能 ????????????????interrupts?=?; ????????}; 

Timer:

????????timer?{ //定時器配置 ????????????????compatible?=?"arm,armv8-timer"; ????????????????interrupts?=?, ?????????????????????????????, ?????????????????????????????, ?????????????????????????????; ????????????????clock-frequency?=?; ????????}; 

串口配置

????????????????iofpga@3,00000000?{ ????????????????????????compatible?=?"arm,amba-bus",?"simple-bus"; ????????????????????????#address-cells?=?; ????????????????????????#size-cells?=?; ????????????????????????ranges?=?;  ????????????????????????sysreg@010000?{ ????????????????????????????????compatible?=?"arm,vexpress-sysreg"; ????????????????????????????????reg?=?; ????????????????????????};  ????????????????????????v2m_serial0:?uart@090000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell";?//串口驅動 ????????????????????????????????reg?=?;?//串口基地址和長度 ????????????????????????????????interrupts?=?;??//串口中斷號 ????????????????????????????????clocks?=?,?;?//串口波特率 ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????};  ????????????????????????v2m_serial1:?uart@0a0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????};  ????????????????????????v2m_serial2:?uart@0b0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????};  ????????????????????????v2m_serial3:?uart@0c0000?{ ????????????????????????????????compatible?=?"arm,pl011",?"arm,primecell"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????????????clocks?=?,?; ????????????????????????????????clock-names?=?"uartclk",?"apb_pclk"; ????????????????????????};  ????????????????????????virtio_block@0130000?{ ????????????????????????????????compatible?=?"virtio,mmio"; ????????????????????????????????reg?=?; ????????????????????????????????interrupts?=?; ????????????????????????}; ????????????????}; ????????};  ????????/*?chosen?*/ }; 

從device_node中獲取信息:

int?of_property_read_u8_array(const?struct?device_node?*np,?const?char?*propname,u8?*out_values,?size_t?sz); int?of_property_read_u16_array(const?struct?device_node?*np,?const?char?*propname,u16?*out_values,?size_t?sz); int?of_property_read_u32_array(const?struct?device_node?*np,?const?char?*propname,u32?*out_values,?size_t?sz); 

從設備結點np中讀取屬性名為propname,類型為8、16、32、位整型數組的屬性值,并放入out_values,sz指明了要讀取的個數。

static?inline?int?of_property_read_u8(const?struct?device_node?*np,const?char?*propname,u8?*out_value)? static?inline?int?of_property_read_u16(const?struct?device_node?*np,const?char?*propname,u8?*out_value)? static?inline?int?of_property_read_u32(const?struct?device_node?*np,const?char?*propname,u8?*out_value) 

從設備結點np中讀取屬性名為propname,類型為8、16、32位的屬性值,并放入out_values。實際上這里調用的就是sz為1的XXX_array函數。

int?of_property_read_u32_index(const?struct?device_node?*np,const?char*propname,u32?index,?u32?*out_value) 

從設備結點np中讀取屬性名為propname的屬性值中第index個u32數值給out_value

int?of_property_read_u64(conststruct?device_node?*np,?const?char?*propname,u64?*out_value) 

從設備結點np中讀取屬性名為propname,類型為64位的屬性值,并放入out_values

int?of_property_read_string(struct?device_node?*np,?const?char?*propname,const?char**out_string) 

從設備結點np中讀取屬性名為propname的字符串型屬性值

int?of_property_read_string_index(struct?device_node?*np,?const?char?*propname,intindex,?const?char?**output) 

從設備結點np中讀取屬性名為propname的字符串型屬性值數組中的第index個字符串

int?of_property_count_strings(struct?device_node?*np,?const?char?*propname) 

從設備結點np中讀取屬性名為propname的字符串型屬性值的個數

unsigned?int?irq_of_parse_and_map(struct?device_node?*dev,?int?index) 

從設備節點dev中讀取第index個irq號

int?of_irq_to_Resource(struct?device_node?*dev,?int?index,?struct?resource?*r) 

從設備節點dev中讀取第index個irq號,并填充一個irq資源結構體

int?of_irq_count(struct?device_node?*dev) 

獲取設備節點dev的irq個數

static?inline?bool?of_property_read_bool(const?struct?device_node?*np,const?char?*propname); 

如果設備結點np含有propname屬性,則返回true,否則返回false。一般用于檢查空屬性是否存在。

struct?property*?of_find_property(const?struct?device_node?*np,const?char?*name,int?*lenp) 

根據name參數,在指定的設備結點np中查找匹配的property,并返回這個property

const?void?*?of_get_property(const?struct?device_node?*np,?const?char?*name,int?*lenp) 

根據name參數,在指定的設備結點np中查找匹配的property,并返回這個property的屬性值

struct?device_node*?of_get_parent(const?struct?device_node?*node) 

獲得node節點的父節點的device node

int?of_device_is_compatible(const?struct?device_node?*device,const?char?*compat); 

判斷設備結點device的compatible屬性是否包含compat指定的字符串

從of_allnodes中查找信息:

struct?device_node*?of_find_node_by_path(const?char?*path) 根據路徑參數,在全局鏈表of_allnodes中,查找匹配的device_node 
struct?device_node*?of_find_node_by_name(struct?device_node?*from,const?char?*name) 則根據name在全局鏈表of_allnodes中查找匹配的device_node,若from=NULL表示從頭開始查找 
struct?device_node*?of_find_node_by_type(struct?device_node?*from,const?char?*type) 

根據設備類型在全局鏈表of_allnodes中查找匹配的device_node

struct?device_node?*?of_find_compatible_node(struct?device_node?*from,?const?char*type,?const?char,*compatible); 

根據compatible的屬性值在全局鏈表of_allnodes中查找匹配的device_node,大多數情況下,from、type為NULL。

struct?device_node*?of_find_node_with_property(struct?device_node?*from,const?char?*prop_name) 

根據節點屬性的name在全局鏈表of_allnodes中查找匹配的device_node

struct?device_node*?of_find_node_by_phandle(phandle?handle) 

根據phandle在全局鏈表of_allnodes中查找匹配的device_node

雜:

void?__iomem*?of_iomap(struct?device_node?*node,?int?index); 

通過設備結點直接進行設備內存區間的 ioremap(),index是內存段的索引。若設備結點的reg屬性有多段,可通過index標示要ioremap的是哪一段,只有1段的情況,index為0

unsigned?long?__init?of_get_flat_dt_root(void) 

用來查找在dtb中的根節點,好像返回的都是0

int?of_alias_get_id(struct?device_node?*np,?const?char?*stem) 

獲取節點np對應的aliasid號

struct?device_node*?of_node_get(struct?device_node?*node) void?of_node_put(struct?device_node?*node) 

device node計數增加/減少

const?struct?of_device_id*?of_match_node(const?struct?of_device_id?*matches,const?struct?device_node*node) 

將matches數組中of_device_id結構的name和type與device node的compatible和type匹配,返回匹配度最高的of_device_id結構

platform_device和resource相關:

int?of_address_to_resource(struct?device_node?*dev,?int?index,struct?resource?*r) 

根據設備節點dev的reg屬性值,填充資源結構體r。Index參數指明了使用reg屬性中第幾個屬性值,一般設置為0,表示第一個。

struct?platform_device*?of_device_alloc(struct?device_node?*np,const?char?*bus_id,struct?device?*parent) 

根據device node,bus_id以及父節點創建該設備的platform_device結構,同時會初始化它的resource成員。

int?of_platform_bus_probe(struct?device_node?*root,const?struct?of_device_id?*matches,struct?device?*parent) 

遍歷of_allnodes中的節點掛接到of_platform_bus_type總線上,由于此時of_platform_bus_type總線上還沒有驅動,所以此時不進行匹配

int?of_platform_populate(struct?device_node?*root,const?struct?of_device_id?*matches,const?struct?of_dev_auxdata?*lookup,struct?device?*parent) 

遍歷of_allnodes中的所有節點,生成并初始化所以節點的platform_device結構

struct?platform_device*?of_find_device_by_node(struct?device_node?*np) 

根據device_node查找返回該設備對應的platform_device結構

相關閱讀

主站蜘蛛池模板: 久久久全国免费视频 | 亚洲另类视频在线观看 | 成人免费视频一区二区三区 | 拍拍拍又黄又爽无挡视频免费 | 欧美经典成人在观看线视频 | 欧美一级毛片在线看视频 | 免费人成在线观看视频不卡 | 国产精品黄网站免费观看 | 中文字幕一区二区三区有限公司 | 在线a毛片免费视频观看 | 亚洲日韩中文字幕天堂不卡 | 国产精品亚洲一区二区在线观看 | 亚洲成a人片在线观 | 亚洲精品一区二区三区在线看 | 免费被黄网站在观看 | 99亚洲精品视频 | 欧美.亚洲.日本一区二区三区 | 久久97视频| 依依成人综合网 | 免费一区二区三区视频狠狠 | 欧美jizzhd极品欧美 | 日韩最新中文字幕 | 久久亚洲国产精品 | 免费看毛片网 | 亚洲一区二区三区视频 | 99久久精品视香蕉蕉er热资源 | 国产激情一区二区三区 | 亚洲天堂男 | 久草网站在线 | 亚洲一区亚洲二区 | 亚州免费视频 | 在线99 | 国内交换一区二区三区 | 一级做a爱久久久久久久 | 日韩国产成人精品视频 | 欧美高清一区二区 | 中文字幕乱码中文乱码综合 | 女人精aaaa片一级毛片女女 | 久久草在线 | 国产欧美精品一区二区三区四区 | 大伊香蕉精品视频在线天堂 |