linux串口驅動框架分析(轉)


轉:https://www.oipapio.com/cn/article-7191558

 

只分析串口驅動,和console相關的部分暫時省去。

內核串口部分需要用到tty部分,tty包含了tty內核和tty線路規程。這些是在串口代碼之前初始化。

1.tty_ldisc線路規程的初始化

只要是函數

driver/tty/tty_io.c     

console_init()

  1. void __init console_init(void)  
  2. {  
  3.     initcall_t *call;  
  4.   
  5.     /* Setup the default TTY line discipline. */  
  6.     tty_ldisc_begin();  
  7.   
  8.     /* 
  9.      * set up the console device so that later boot sequences can 
  10.      * inform about problems etc.. 
  11.      */  
  12.     call = __con_initcall_start;  
  13.     while (call < __con_initcall_end) {  
  14.         (*call)();  
  15.         call++;  
  16.     }  
  17. }  
 

此處和tty相關的就是函數tty_ldisc_begin(),內核通過此函數來初始化tty線路規程的相關操作。

  1. void tty_ldisc_begin(void)  
  2. {  
  3.     /* Setup the default TTY line discipline. */  
  4.     (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);  
  5. }  
 


tty_ldisc_begin函數原來是對tty_register_ldisc的封裝,從名字就可以看出來這個是對tty線路規程的初始化操作(內核線路規程的簡稱是ldisc)

此處先看函數調用的兩個參數,其中第一個參數N_TTY是一個宏,第二個參數是內核tty線路規程的操作方法集

  1. #define N_TTY       0  
  2. #define N_SLIP      1  
  3. #define N_MOUSE     2  
 
  1. struct tty_ldisc_ops tty_ldisc_N_TTY = {  
  2.     .magic           = TTY_LDISC_MAGIC,  
  3.     .name            = "n_tty",  
  4.     .open            = n_tty_open,  
  5.     .close           = n_tty_close,  
  6.     .flush_buffer    = n_tty_flush_buffer,  
  7.     .chars_in_buffer = n_tty_chars_in_buffer,  
  8.     .read            = n_tty_read,  
  9.     .write           = n_tty_write,  
  10.     .ioctl           = n_tty_ioctl,  
  11.     .set_termios     = n_tty_set_termios,  
  12.     .poll            = n_tty_poll,  
  13.     .receive_buf     = n_tty_receive_buf,  
  14.     .write_wakeup    = n_tty_write_wakeup  
  15. };  
 
        

然后具體的tty_register_ldisc函數源碼如下:

  1. int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)  
  2. {  
  3.     unsigned long flags;  
  4.     int ret = 0;  
  5.   
  6.     if (disc < N_TTY || disc >= NR_LDISCS)  
  7.         return -EINVAL;  
  8.   
  9.     spin_lock_irqsave(&tty_ldisc_lock, flags);  
  10.     tty_ldiscs[disc] = new_ldisc;  
  11.     new_ldisc->num = disc;  
  12.     new_ldisc->refcount = 0;  
  13.     spin_unlock_irqrestore(&tty_ldisc_lock, flags);  
  14.   
  15.     return ret;  
  16. }  
 

可以發現tty線路規程的操作很好理解。內核定義一個tty_ldiscs數組,然后根據數組下標來存放對應的線路規程的操作集,而這里的數組下標表示的就是具體的協議,在頭文件中已經通過宏定義好了。例如N_TTY 0。

所以可以發現

ldisc[0] 存放的是N_TTY對應的線路規程操作集

ldisc[1]存放的是N_SLIP對應的線路規程操作集

ldisc[2]存放的就是N_MOUSE對應的線路規程操作集

依次類推。

此處就是ldisc[N_TTY] = tty_ldisc_N_TTY。

然后函數退出,最后返回到console_init()函數。

假如串口作為console,則console_init()會通過剩余的代碼調用s3c_serial_console_init()執行console相關的初始化了。

  1. while (call < __con_initcall_end) {  
  2.     (*call)();  
  3.     call++;  
  4. }  
 

由於本文只分析不做console的串口驅動,所以這部分代碼和串口無關。console_init()函數和串口相關的操作就是

之前調用的tty_ldisc_begin函數了。這個tty_ldisc初始化到這一步就暫時放在一邊,待到后面通過open函數中再來調用

此處已注冊的的tty_disc線路規程

2.tty_driver初始化 uart_driver中uart_state的初始化

2.1tty_driver相關部分的初始化

初始化線路規程后,系統開始初始化tty_driver。

而tty_driver需要初始化很多字段例如

tty_driver.owner

tty_driver.driver_name
tty_driver.name
tty_driver.major
tty_driver.minor_start   
tty_driver.type     
這些字段根據不同的tty_driver需要初始化成不同的參數。

根據LDD3的解釋,內核的tty_driver有三種:控制台、串口、pty。這三種對應着三種不同的tty_driver,而這三種tty_driver對應的上述字段是不同的。

那么這些參數來源哪里呢?來源於程序員的定義,編譯之初確定好這些參數后,內核最后會將其賦值給tty_driver。

那這些參數定義在哪里呢?定義在uart_driver中,這個就是驅動需要修改的一個數據結構。

這就相當於在tty_driver外面就加了一層uart_driver。而uart_driver中的部分字段和tty_driver中的部分字段數值是一樣的。

此處的uart_driver定義如下:

  1. static struct uart_driver s3c24xx_uart_drv = {  
  2.     .owner      = THIS_MODULE,  
  3.     .dev_name   = "s3c2410_serial",  
  4.     .nr     = CONFIG_SERIAL_SAMSUNG_UARTS,  
  5.     .cons       = S3C24XX_SERIAL_CONSOLE,  
  6.     .driver_name    = S3C24XX_SERIAL_NAME,  
  7.     .major      = S3C24XX_SERIAL_MAJOR,  
  8.     .minor      = S3C24XX_SERIAL_MINOR,  
  9. };  
 

內核通過

driver/serial/samsung.c文件中的

s3c24xx_modinit()函數來進行tty_driver的初始化,參數就是韋uart_driver結構的s3c24xx_uart_drv。

  1. static int __init s3c24xx_serial_modinit(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = uart_register_driver(&s3c24xx_uart_drv);  
  6.     if (ret < 0) {  
  7.         printk(KERN_ERR "failed to register UART driver\n");  
  8.         return -1;  
  9.     }  
  10.   
  11.     return 0;  
  12. }  
 

uart_register_driver函數如下:

  1. int uart_register_driver(struct uart_driver *drv)  
  2. {  
  3.     struct tty_driver *normal;  
  4.     int i, retval;  
  5.   
  6.     BUG_ON(drv->state);  
  7.   
  8.     /* 
  9.      * Maybe we should be using a slab cache for this, especially if 
  10.      * we have a large number of ports to handle. 
  11.      */  
  12.     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);  
  13.     if (!drv->state)  
  14.         goto out;  
  15.   
  16.     normal = alloc_tty_driver(drv->nr);  
  17.     if (!normal)  
  18.         goto out_kfree;  
  19.   
  20.     drv->tty_driver = normal;  
  21.     normal->owner        = drv->owner;  
  22.     normal->driver_name  = drv->driver_name;  
  23.     normal->name     = drv->dev_name;  
  24.     normal->major        = drv->major;  
  25.     normal->minor_start  = drv->minor;  
  26.     normal->type     = TTY_DRIVER_TYPE_SERIAL;  
  27.     normal->subtype      = SERIAL_TYPE_NORMAL;  
  28.     normal->init_termios = tty_std_termios;  
  29.     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  
  30.     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;  
  31.     normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;  
  32.     normal->driver_state    = drv;  
  33.     <span style="color: rgb(255, 0, 0);">tty_set_operations(normal, &uart_ops);</span>  
  34.   
  35.     /* 
  36.      * Initialise the UART state(s). 
  37.      */  
  38.     for (i = 0; i < drv->nr; i++) {  
  39.         struct uart_state *state = drv->state + i;  
  40.         struct tty_port *port = &state->port;  
  41.   
  42.         tty_port_init(port);  
  43.         port->ops = &uart_port_ops;  
  44.         port->close_delay     = 500; /* .5 seconds */  
  45.         port->closing_wait    = 30000;   /* 30 seconds */  
  46.         tasklet_init(&state->tlet, uart_tasklet_action,  
  47.                  (unsigned long)state);  
  48.     }  
  49.   
  50.     <span style="color: rgb(255, 0, 0);">retval = tty_register_driver(normal);</span>  
  51.     if (retval >= 0)  
  52.         return retval;  
  53.   
  54.     put_tty_driver(normal);  
  55. out_kfree:  
  56.     kfree(drv->state);  
  57. out:  
  58.     return -ENOMEM;  
  59. }  
 

這個函數就是用來初始化tty_drive的,並且可以看見tty_driver中的字段初始化的數值就是函數傳遞進來的uart_driver中的對應字段。

此外此函數還需要注意的地方都已經標紅。

其中,通過    tty_set_operations(normal, &uart_ops);

指定了tty_driver的操作集tty_operation為uart_ops,假如是其他的tty_driver相信這個ops也是需要修改的。

uart_ops定義如下

  1. static const struct tty_operations uart_ops = {  
  2.     .open       = uart_open,  
  3.     .close      = uart_close,  
  4.     .write      = uart_write,  
  5.     .put_char   = uart_put_char,  
  6.     .flush_chars    = uart_flush_chars,  
  7.     .write_room = uart_write_room,  
  8.     .chars_in_buffer= uart_chars_in_buffer,  
  9.     .flush_buffer   = uart_flush_buffer,  
  10.     .ioctl      = uart_ioctl,  
  11.     .throttle   = uart_throttle,  
  12.     .unthrottle = uart_unthrottle,  
  13.     .send_xchar = uart_send_xchar,  
  14.     .set_termios    = uart_set_termios,  
  15.     .set_ldisc  = uart_set_ldisc,  
  16.     .stop       = uart_stop,  
  17.     .start      = uart_start,  
  18.     .hangup     = uart_hangup,  
  19.     .break_ctl  = uart_break_ctl,  
  20.     .wait_until_sent= uart_wait_until_sent,  
  21. #ifdef CONFIG_PROC_FS  
  22.     .proc_fops  = &uart_proc_fops,  
  23. #endif  
  24.     .tiocmget   = uart_tiocmget,  
  25.     .tiocmset   = uart_tiocmset,  
  26.     .get_icount = uart_get_icount,  
  27. #ifdef CONFIG_CONSOLE_POLL  
  28.     .poll_init  = uart_poll_init,  
  29.     .poll_get_char  = uart_poll_get_char,  
  30.     .poll_put_char  = uart_poll_put_char,  
  31. #endif  
  32. };  
 
        

然后函數通過

  1. retval = tty_register_driver(normal);  

 

實現tty_driver的"注冊"。具體的注冊函數如下:

  1. int tty_register_driver(struct tty_driver *driver)  
  2. {  
  3.     int error;  
  4.     int i;  
  5.     dev_t dev;  
  6.     void **p = NULL;  
  7.     struct device *d;  
  8.   
  9.     if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {  
  10.         p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);  //注意這一句,為tty_driver分配了一個tty_struct *指針數組,並且使用的kzalloc方式也就是說。初始化的時候該數組內容是零!這點在tty_open判斷的時候會用到!  
  11.         if (!p)   
  12.             return -ENOMEM;  
  13.     }  
  14.   
  15.     if (!driver->major) {  
  16.         error = alloc_chrdev_region(&dev, driver->minor_start,  
  17.                         driver->num, driver->name);  
  18.         if (!error) {  
  19.             driver->major = MAJOR(dev);  
  20.             driver->minor_start = MINOR(dev);  
  21.         }  
  22.     } else {  
  23.         dev = MKDEV(driver->major, driver->minor_start);  
  24.         error = register_chrdev_region(dev, driver->num, driver->name);  
  25.     }  
  26.     if (error < 0) {  
  27.         kfree(p);  
  28.         return error;  
  29.     }  
  30.   
  31.     if (p) {  
  32.         driver->ttys = (struct tty_struct **)p;  
  33.         driver->termios = (struct ktermios **)(p + driver->num);  
  34.     } else {  
  35.         driver->ttys = NULL;  
  36.         driver->termios = NULL;  
  37.     }  
  38.   
  39.     <span style="color: rgb(255, 0, 0);">cdev_init(&driver->cdev, &tty_fops);</span>  
  40.     driver->cdev.owner = driver->owner;  
  41.     error = cdev_add(&driver->cdev, dev, driver->num);  
  42.     if (error) {  
  43.         unregister_chrdev_region(dev, driver->num);  
  44.         driver->ttys = NULL;  
  45.         driver->termios = NULL;  
  46.         kfree(p);  
  47.         return error;  
  48.     }  
  49.   
  50.     mutex_lock(&tty_mutex);  
  51.     <span style="color: rgb(255, 0, 0);">list_add(&driver->tty_drivers, &tty_drivers);</span>  
  52.     mutex_unlock(&tty_mutex);  
  53.   
  54.     if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {  
  55.         for (i = 0; i < driver->num; i++) {  
  56.             d = tty_register_device(driver, i, NULL);  
  57.             if (IS_ERR(d)) {  
  58.                 error = PTR_ERR(d);  
  59.                 goto err;  
  60.             }  
  61.         }  
  62.     }  
  63.     proc_tty_register_driver(driver);  
  64.     driver->flags |= TTY_DRIVER_INSTALLED;  
  65.     return 0;  
  66.   
  67. err:  
  68.     for (i--; i >= 0; i--)  
  69.         tty_unregister_device(driver, i);  
  70.   
  71.     mutex_lock(&tty_mutex);  
  72.     list_del(&driver->tty_drivers);  
  73.     mutex_unlock(&tty_mutex);  
  74.   
  75.     unregister_chrdev_region(dev, driver->num);  
  76.     driver->ttys = NULL;  
  77.     driver->termios = NULL;  
  78.     kfree(p);  
  79.     return error;  
  80. }  
 

該“注冊”主要包含了如下工作:

1.根據主設備號、次設備號,注冊一個字符驅動,其操作集為tty_fops

  1. static const struct file_operations tty_fops = {  
  2.     .llseek     = no_llseek,  
  3.     .read       = tty_read,  
  4.     .write      = tty_write,  
  5.     .poll       = tty_poll,  
  6.     .unlocked_ioctl = tty_ioctl,  
  7.     .compat_ioctl   = tty_compat_ioctl,  
  8.     .open       = tty_open,  
  9.     .release    = tty_release,  
  10.     .fasync     = tty_fasync,  
  11. };  


並在/dev文件夾下生成對應的設備文件節點。

2.將tty_driver添加到tty_driver鏈表tty_drivers中,內核通過維護tty_drivers鏈表來維護tty_driver。

前面提到了內核的tty_driver主要包含三大類console、串口、pty。

完成之后tty_driver初始化也結束,然后進去s3c24xx的uart相關的初始化。

2.uart_driver的部分初始化

此函數關於uart_driver部分的初始化主要是初始化了uart_driver中的uart_state這個成員。

uart_state也是一個結構體,其中包含uart_pot、tty_port兩個成員。而系統中對具體硬件的操作就是通過uart_port.ops

來操作的,這個uart_port需要重點留意下,但是在此函數中就只初始化了tty_port這個成員,uart_port的初始化

在后面的platform_driver.probe函數中初始化,這也正好說明了uart_port是和具體硬件精密相關的。

3.uart_port初始化

uart_port的初始化是通過platform_driver中的probe函數來完成的。如何調用到probe函數的就不說了。

probe函數如下

  1. static int s3c2440_serial_probe(struct platform_device *dev)  
  2. {  
  3.     dbg("s3c2440_serial_probe: dev=%p\n", dev);  
  4.     return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);  
  5. }  
 

其第二個參數s3c2440_uart_inf如下

  1. static struct s3c24xx_uart_info s3c2440_uart_inf = {  
  2.     .name       = "Samsung S3C2440 UART",  
  3.     .type       = PORT_S3C2440,  
  4.     .fifosize   = 64,  
  5.     .rx_fifomask    = S3C2440_UFSTAT_RXMASK,  
  6.     .rx_fifoshift   = S3C2440_UFSTAT_RXSHIFT,  
  7.     .rx_fifofull    = S3C2440_UFSTAT_RXFULL,  
  8.     .tx_fifofull    = S3C2440_UFSTAT_TXFULL,  
  9.     .tx_fifomask    = S3C2440_UFSTAT_TXMASK,  
  10.     .tx_fifoshift   = S3C2440_UFSTAT_TXSHIFT,  
  11.     .get_clksrc = s3c2440_serial_getsource,  
  12.     .set_clksrc = s3c2440_serial_setsource,  
  13.     .reset_port = s3c2440_serial_resetport,  
  14. };  
 

先簡單介紹下uart_port結構的初始化。uart_port和系統中具體的硬件對應,每個uart都有自己對應的uart_port。而這個結構體的初始化由點類似於

前面說的tty_driver,並不是直接初始化tty_driver的,而是在tty_driver外面包了一層uart_driver。此處也是的在uart_port外面包了一層struct s3c24xx_uart_port 。

這樣s3c24xx_uart_port除了包含uart_port這個重要結構外還可以存放s3c24xx系列串口的特有信息。

uart_port的初始化信息主要來源於struct s3c24xx_uart_info,如上所示。這些數值會在probe函數中賦值給uart_port用於初始化。

初始化uart_port之后就是調用函數將這個初始化好的uart_port和前面的uart_driver關聯上。因為uart_driver中即有tty相關的tty_driver又有好友uart_port信息的uart_state

,這樣tty和uart就有可能關聯上了。下面從probe函數開始分析具體代碼。

 

s3c24xx_serial_probe函數如下:

  1. int s3c24xx_serial_probe(struct platform_device *dev,  
  2.              struct s3c24xx_uart_info *info)  
  3. {  
  4.     struct s3c24xx_uart_port *ourport;  
  5.     int ret;  
  6.   
  7.     dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);  
  8.   
  9.     <span style="color: rgb(255, 0, 0);">ourport = &s3c24xx_serial_ports[probe_index];</span>  
  10.     probe_index++;  
  11.   
  12.     dbg("%s: initialising port %p...\n", __func__, ourport);  
  13.   
  14.     <span style="color: rgb(255, 0, 0);">ret = s3c24xx_serial_init_port(ourport, info, dev);</span>  
  15.     if (ret < 0)  
  16.         goto probe_err;  
  17.   
  18.     dbg("%s: adding port\n", __func__);  
  19.     <span style="color: rgb(255, 0, 0);">uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);</span>  
  20.     platform_set_drvdata(dev, &ourport->port);  
  21.   
  22.     ret = device_create_file(&dev->dev, &dev_attr_clock_source);  
  23.     if (ret < 0)  
  24.         printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);  
  25.   
  26.     ret = s3c24xx_serial_cpufreq_register(ourport);  
  27.     if (ret < 0)  
  28.         dev_err(&dev->dev, "failed to add cpufreq notifier\n");  
  29.   
  30.     return 0;  
  31.   
  32.  probe_err:  
  33.     return ret;  
  34. }  
 

函數總重要的語句全部標紅了。先分析第一句

  1. <span style="color: rgb(255, 0, 0);">ourport = &s3c24xx_serial_ports[probe_index];</span>  

這個主要是從串口數組s3c24xx_serial_ports中選定初始化的串口。這里需要注意的uart_port中的ops,標紅的那句。這里是具體操作s3c24xx串口的操作函數集。

后面具體的write  read等函數最終調用的是這里操作集,都是最底層的硬件操作。

  1. static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {  
  2.     [0] = {  
  3.         .port = {  
  4.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),  
  5.             .iotype     = UPIO_MEM,  
  6.             .irq        = IRQ_S3CUART_RX0,  
  7.             .uartclk    = 0,  
  8.             .fifosize   = 16,  
  9.             <span style="color: rgb(255, 0, 0);">.ops       = &s3c24xx_serial_ops,</span>  
  10.             .flags      = UPF_BOOT_AUTOCONF,  
  11.             .line       = 0,  
  12.         }  
  13.     },  
  14.     [1] = {  
  15.         .port = {  
  16.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),  
  17.             .iotype     = UPIO_MEM,  
  18.             .irq        = IRQ_S3CUART_RX1,  
  19.             .uartclk    = 0,  
  20.             .fifosize   = 16,  
  21.             .ops        = &s3c24xx_serial_ops,  
  22.             .flags      = UPF_BOOT_AUTOCONF,  
  23.             .line       = 1,  
  24.         }  
  25.     },  
  26. #if CONFIG_SERIAL_SAMSUNG_UARTS > 2  
  27.   
  28.     [2] = {  
  29.         .port = {  
  30.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),  
  31.             .iotype     = UPIO_MEM,  
  32.             .irq        = IRQ_S3CUART_RX2,  
  33.             .uartclk    = 0,  
  34.             .fifosize   = 16,  
  35.             .ops        = &s3c24xx_serial_ops,  
  36.             .flags      = UPF_BOOT_AUTOCONF,  
  37.             .line       = 2,  
  38.         }  
  39.     },  
  40. #endif  
  41. #if CONFIG_SERIAL_SAMSUNG_UARTS > 3  
  42.     [3] = {  
  43.         .port = {  
  44.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),  
  45.             .iotype     = UPIO_MEM,  
  46.             .irq        = IRQ_S3CUART_RX3,  
  47.             .uartclk    = 0,  
  48.             .fifosize   = 16,  
  49.             .ops        = &s3c24xx_serial_ops,  
  50.             .flags      = UPF_BOOT_AUTOCONF,  
  51.             .line       = 3,  
  52.         }  
  53.     }  
  54. #endif  
  55. };  
 

確定將要初始化的uart后,通過函數

  1. ret = s3c24xx_serial_init_port(ourport, info, dev);  


進行初始化了。根據前面介紹的,他的初始化的信息主要來源於那個s3c2440_uart_inf 結構體中的信息。

uart_port函數具體的初始化就不展開分析了,里面都是些賦值的操作。

接着分析uart_port和uart_driver關聯的操作。這一步是通過函數

  1. uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);  

實現的。可以發現這個函數的第一個參數uart_driver是s3c24xx_uart_drv,和前面tty_driver初始化中提到的通過uart_register_driver注冊的uart_driver是一樣的!

這樣uart_driver中的uart_state也就初始化好了,其中uart_state包含了uart_port的信息。這個uart_port是和具體的串口硬件相關的。

總結

到這一步所有的tty和uart初始化的部分算是完成了。但是目前還不能進行read、write操作。

因為還有一個重要的函數tty_open沒有分析,作為tty設備的uart,這個tty_open函數也是非常重要的,下篇分析。

初始化的工作主要是

1.初始化uart_driver結構體,包括初始化uart_driver結構體中的tty_driver uart_state。

2.uart_state部分的初始化主要是初始化其中的uart_port,這部分的初始化在probe函數總完成

修改驅動需要設計的數據結構

1.uart_driver

uart_driver中的數據用於初始化tty_driver

2.s3c24xx_uart_info

用於初始化uart_port

3.s3c24xx_uart_port或者說uart_port。

uart_port的初始化,在s3c24xx的uart驅動中uart_port是內嵌在s3c24xx_uart_port中的

4.uart_ops

最底層的硬件操作


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM