日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux SPI框架

發(fā)布時(shí)間:2023/12/1 linux 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux SPI框架 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

水平有限,描述不當(dāng)之處還請(qǐng)指出,轉(zhuǎn)載請(qǐng)注明出處http://blog.csdn.net/vanbreaker/article/details/7733476

====

Linux的SPI子系統(tǒng)采用主機(jī)驅(qū)動(dòng)和外設(shè)驅(qū)動(dòng)分離的思想,首先主機(jī)SPI控制器是一種平臺(tái)設(shè)備,因此它以platform的方式注冊(cè)進(jìn)內(nèi)核,外設(shè)的信息是以boardinfo形式靜態(tài)定義的,在創(chuàng)建spi_master時(shí),會(huì)根據(jù)外設(shè)的bus_num和主機(jī)的bus_num是否相等,來選擇是否將該外設(shè)掛接在該SPI主控制器下。先看SPI子系統(tǒng)中幾個(gè)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):

struct spi_master用來描述一個(gè)SPI主控制器

struct spi_master { struct device dev; s16 bus_num; /*總線編號(hào)*/ u16 num_chipselect;/*支持的外設(shè)數(shù)量*/ u16 dma_alignment; int (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用于將消息添加到隊(duì)列*/ void (*cleanup)(struct spi_device *spi); }; struct spi_device用來描述一個(gè)SPI從設(shè)備 struct spi_device { struct device dev; struct spi_master *master; /*從設(shè)備所屬的SPI主控器*/ u32 max_speed_hz; /*最大傳輸頻率*/ u8 chip_select; /*片選號(hào),用于區(qū)別其他從設(shè)備*/ u8 mode; /*傳輸模式*/ /*各個(gè)mode的定義*/ #define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPOL 0x02 /* clock polarity */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ #define SPI_MODE_1 (0|SPI_CPHA) #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) #define SPI_CS_HIGH 0x04 /* chipselect active high? */ #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ #define SPI_3WIRE 0x10 /* SI/SO signals shared */ #define SPI_LOOP 0x20 /* loopback mode */ u8 bits_per_word; /*每個(gè)字的比特?cái)?shù)*/ int irq; /*所使用的中斷*/ void *controller_state; void *controller_data; char modalias[32]; /*設(shè)備名,在和從設(shè)備驅(qū)動(dòng)匹配時(shí)會(huì)用到*/ }; struct spi_driver用來描述一個(gè)SPI從設(shè)備的驅(qū)動(dòng),它的形式和struct platform_driver是一致的 struct spi_driver { int (*probe)(struct spi_device *spi); int (*remove)(struct spi_device *spi); void (*shutdown)(struct spi_device *spi); int (*suspend)(struct spi_device *spi, pm_message_t mesg); int (*resume)(struct spi_device *spi); struct device_driver driver; }; SPI子系統(tǒng)初始化的第一步就是將SPI總線注冊(cè)進(jìn)內(nèi)核,并且在/sys下創(chuàng)建一個(gè)spi_master的類,以后注冊(cè)的從設(shè)備都將掛接在該總線下 static int __init spi_init(void) { int status; buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } status = bus_register(&spi_bus_type);//注冊(cè)SPI總線 if (status < 0) goto err1; status = class_register(&spi_master_class);//注冊(cè)spi_master類 if (status < 0) goto err2; return 0;
err2: bus_unregister(
&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; }

我們來看spi_bus_type的定義

struct bus_type spi_bus_type = { .name = "spi", .dev_attrs = spi_dev_attrs, .match = spi_match_device, .uevent = spi_uevent, .suspend = spi_suspend, .resume = spi_resume, };

來看掛接在SPI總線下的從設(shè)備和從設(shè)備驅(qū)動(dòng)是如何匹配的,也就是spi_match_device函數(shù)

static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); return strcmp(spi->modalias, drv->name) == 0; }

這里可以看到是將struct device_driver中的name字段與struct spi_device中的modalias字段進(jìn)行匹配

這里已經(jīng)完成了SPI子系統(tǒng)初始化的第一步,也就是注冊(cè)SPI總線,這一步是和平臺(tái)無關(guān)的,第二步是和平臺(tái)相關(guān)的初始化,下一節(jié)再做介紹。

?====

上節(jié)介紹了SPI子系統(tǒng)中的一些重要數(shù)據(jù)結(jié)構(gòu)和SPI子系統(tǒng)初始化的第一步,也就是注冊(cè)SPI總線。這節(jié)介紹針對(duì)于s3c24xx平臺(tái)的SPI子系統(tǒng)初始化,在看具體的代碼之前,先上一張自己畫的圖,幫助理清初始化的主要步驟

顯然,SPI是一種平臺(tái)特定的資源,所以它是以platform平臺(tái)設(shè)備的方式注冊(cè)進(jìn) 內(nèi)核的,因此它的struct platform_device結(jié)構(gòu)是已經(jīng)靜態(tài)定義好了的,現(xiàn)在只待它的struct platform_driver注冊(cè),然后和platform_device匹配。

初始化的入口:

static int __init s3c24xx_spi_init(void) { return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe); }

platform_driver_probe()會(huì)調(diào)用 platform_driver_register()來注冊(cè)驅(qū)動(dòng),然后在注冊(cè)的過程中尋求匹配的platform_device,一旦匹配成功,便會(huì)調(diào) 用probe函數(shù),也就是s3c24xx_spi_probe(),在看這個(gè)函數(shù)之前,還得介紹幾個(gè)相關(guān)的數(shù)據(jù)結(jié)構(gòu)。

struct s3c2410_spi_info是一個(gè)板級(jí)結(jié)構(gòu),也是在移植時(shí)就定義好的,在初始化spi_master時(shí)用到,platform_device-->dev-->platform_data會(huì)指向這個(gè)結(jié)構(gòu)。

struct s3c2410_spi_info { int pin_cs; /* simple gpio cs */ unsigned int num_cs; /* total chipselects */ int bus_num;/* bus number to use. */ void (*gpio_setup)(struct s3c2410_spi_info *spi, int enable); void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); };

struct s3c24xx_spi用來具體描述s3c24xx平臺(tái)上一個(gè)SPI控制器

struct s3c24xx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; struct completion done; void __iomem *regs; int irq; int len; int count; void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); /* data buffers */ const unsigned char *tx; unsigned char *rx; struct clk *clk; struct resource *ioarea; struct spi_master *master; struct spi_device *curdev; struct device *dev; struct s3c2410_spi_info *pdata; };

struct spi_bitbang用于控制實(shí)際的數(shù)據(jù)傳輸

struct spi_bitbang { struct workqueue_struct *workqueue; /*工作隊(duì)列*/ struct work_struct work; spinlock_t lock; struct list_head queue; u8 busy; u8 use_dma; u8 flags; /* extra spi->mode support */ struct spi_master *master; /*bitbang所屬的master*/ /*用于設(shè)置設(shè)備傳輸時(shí)的時(shí)鐘,字長等*/ int (*setup_transfer)(struct spi_device *spi, struct spi_transfer *t); void (*chipselect)(struct spi_device *spi, int is_on); #define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ #define BITBANG_CS_INACTIVE 0 /*針對(duì)于平臺(tái)的傳輸控制函數(shù)*/ int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t); /* txrx_word[SPI_MODE_*]() just looks like a shift register */ u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits); };

?下面來看s3c24xx_spi_probe()函數(shù)的實(shí)現(xiàn)

static int __init s3c24xx_spi_probe(struct platform_device *pdev) { struct s3c2410_spi_info *pdata; struct s3c24xx_spi *hw; struct spi_master *master; struct resource *res; int err = 0; /*創(chuàng)建spi_master,并將spi_master->private_data指向s3c24xx_spi*/ master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) { dev_err(&pdev->dev, "No memory for spi_master\n"); err = -ENOMEM; goto err_nomem; } hw = spi_master_get_devdata(master);//獲取s3c24xx_spimemset(hw, 0, sizeof(struct s3c24xx_spi)); hw->master = spi_master_get(master); hw->pdata = pdata = pdev->dev.platform_data; hw->dev = &pdev->dev; if (pdata == NULL) { dev_err(&pdev->dev, "No platform data supplied\n"); err = -ENOENT; goto err_no_pdata; } platform_set_drvdata(pdev, hw); init_completion(&hw->done); /* setup the master state. */ /*片選數(shù)和SPI主控制器編號(hào)是在platform_data中已經(jīng)定義好了的*/ master->num_chipselect = hw->pdata->num_cs; master->bus_num = pdata->bus_num; /* setup the state for the bitbang driver */ /*設(shè)置bitbang的所屬master和控制傳輸?shù)南嚓P(guān)函數(shù)*/ hw->bitbang.master = hw->master; hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; hw->bitbang.chipselect = s3c24xx_spi_chipsel; hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; hw->bitbang.master->setup = s3c24xx_spi_setup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); /* find and map our resources */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); err = -ENOENT; goto err_no_iores; } hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, pdev->name); if (hw->ioarea == NULL) { dev_err(&pdev->dev, "Cannot reserve region\n"); err = -ENXIO; goto err_no_iores; } /*映射SPI控制寄存器*/ hw->regs = ioremap(res->start, (res->end - res->start)+1); if (hw->regs == NULL) { dev_err(&pdev->dev, "Cannot map IO\n"); err = -ENXIO; goto err_no_iomap; } /*獲取中斷號(hào)*/ hw->irq = platform_get_irq(pdev, 0); if (hw->irq < 0) { dev_err(&pdev->dev, "No IRQ specified\n"); err = -ENOENT; goto err_no_irq; } /*注冊(cè)中斷*/ err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); if (err) { dev_err(&pdev->dev, "Cannot claim IRQ\n"); goto err_no_irq; } hw->clk = clk_get(&pdev->dev, "spi"); if (IS_ERR(hw->clk)) { dev_err(&pdev->dev, "No clock for device\n"); err = PTR_ERR(hw->clk); goto err_no_clk; } /* setup any gpio we can */ if (!pdata->set_cs) { if (pdata->pin_cs < 0) { dev_err(&pdev->dev, "No chipselect pin\n"); goto err_register; } err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev)); if (err) { dev_err(&pdev->dev, "Failed to get gpio for cs\n"); goto err_register; } hw->set_cs = s3c24xx_spi_gpiocs;//設(shè)定片選函數(shù)gpio_direction_output(pdata->pin_cs, 1); } else hw->set_cs = pdata->set_cs;
s3c24xx_spi_initialsetup(hw);
/* register our spi controller */ /* 注冊(cè)主機(jī)SPI控制器 */ err = spi_bitbang_start(&hw->bitbang); if (err) { dev_err(&pdev->dev, "Failed to register SPI master\n"); goto err_register; } return 0; err_register: if (hw->set_cs == s3c24xx_spi_gpiocs) gpio_free(pdata->pin_cs); \
clk_disable(hw
->clk); clk_put(hw->clk); err_no_clk: free_irq(hw->irq, hw); err_no_irq: iounmap(hw->regs);
}

-

int spi_bitbang_start(struct spi_bitbang *bitbang) { int status; if (!bitbang->master || !bitbang->chipselect) return -EINVAL; /*初始化一個(gè)struct work,處理函數(shù)為bitbang_work*/ INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); INIT_LIST_HEAD(&bitbang->queue); /*檢測bitbang中的函數(shù)是否都定義了,如果沒定義,則默認(rèn)使用spi_bitbang_xxx*/ if (!bitbang->master->transfer) bitbang->master->transfer = spi_bitbang_transfer; if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; if (!bitbang->master->setup) { if (!bitbang->setup_transfer) bitbang->setup_transfer = spi_bitbang_setup_transfer; bitbang->master->setup = spi_bitbang_setup; bitbang->master->cleanup = spi_bitbang_cleanup; } } else if (!bitbang->master->setup) return -EINVAL; /* this task is the only thing to touch the SPI bits */ bitbang->busy = 0; /*創(chuàng)建bitbang的工作隊(duì)列*/ bitbang->workqueue = create_singlethread_workqueue( dev_name(bitbang->master->dev.parent)); if (bitbang->workqueue == NULL) { status = -EBUSY; goto err1; } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ /*注冊(cè)spi_master*/ status = spi_register_master(bitbang->master); if (status < 0) goto err2; return status; err2: destroy_workqueue(bitbang->workqueue); err1: return status; } 下一個(gè)關(guān)鍵函數(shù)就是spi_register_master(),用于注冊(cè)spi_master int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); struct device *dev = master->dev.parent; int status = -ENODEV; int dynamic = 0;
if (!dev) return -ENODEV; /* even if it's just one always-selected device, there must * be at least one chipselect */ if (master->num_chipselect == 0)//片選數(shù)不能為0 return -EINVAL; /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { /* FIXME switch to an IDR based scheme, something like * I2C now uses, so we can't run out of "dynamic" IDs */ master->bus_num = atomic_dec_return(&dyn_bus_id); dynamic = 1; } /* register the device, then userspace will see it. * registration fails if the bus ID is in use. */ dev_set_name(&master->dev, "spi%u", master->bus_num); status = device_add(&master->dev);//添加spi_master設(shè)備 if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* populate children from any spi device tables */ scan_boardinfo(master);//遍歷板級(jí)信息,尋找可以掛接在該spi_master下的從設(shè)備 status = 0; done: return status; }

-

static void scan_boardinfo(struct spi_master *master) { struct boardinfo *bi; mutex_lock(&board_lock); list_for_each_entry(bi, &board_list, list) { struct spi_board_info *chip = bi->board_info; unsigned n; for (n = bi->n_board_info; n > 0; n--, chip++) { if (chip->bus_num != master->bus_num) continue; /* NOTE: this relies on spi_new_device to * issue diagnostics when given bogus inputs */ /*bus_num相等則創(chuàng)建新設(shè)備*/ (void) spi_new_device(master, chip); } } mutex_unlock(&board_lock); }

spi_board_info是板級(jí)信息,是在移植時(shí)就寫好的,并且要將其注冊(cè)

struct spi_board_info { char modalias[32]; /*名字*/ const void *platform_data; void *controller_data; int irq; /*中斷號(hào)*/ u32 max_speed_hz; /*最高傳輸速率*/ u16 bus_num; /*所屬的spi_master編號(hào)*/ u16 chip_select; /*片選號(hào)*/ u8 mode; /*傳輸模式*/ };

最后一步就是將相應(yīng)的從設(shè)備注冊(cè)進(jìn)內(nèi)核

struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) { struct spi_device *proxy; int status; /* NOTE: caller did any chip->bus_num checks necessary. * * Also, unless we change the return value convention to use * error-or-pointer (not NULL-or-pointer), troubleshootability * suggests syslogged diagnostics are best here (ugh). */ /*創(chuàng)建SPI_device*/ proxy = spi_alloc_device(master); if (!proxy)return NULL;
WARN_ON(strlen(chip
->modalias) >= sizeof(proxy->modalias)); /*初始化*/ proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; /*將新設(shè)備添加進(jìn)內(nèi)核*/ status = spi_add_device(proxy); if (status < 0) { spi_dev_put(proxy); return NULL; } return proxy; } 本節(jié)以spidev設(shè)備驅(qū)動(dòng)為例,來闡述SPI數(shù)據(jù)傳輸?shù)倪^程。spidev是內(nèi)核中一個(gè)通用的設(shè)備驅(qū)動(dòng),我們注冊(cè)的從設(shè)備都可以使用該驅(qū)動(dòng),只需在注冊(cè) 時(shí)將從設(shè)備的modalias字段設(shè)置為"spidev",這樣才能和spidev驅(qū)動(dòng)匹配成功。我們要傳輸?shù)臄?shù)據(jù)有時(shí)需要分為一段一段的(比如先發(fā)送, 后讀取,就需要兩個(gè)字段),每個(gè)字段都被封裝成一個(gè)transfer,N個(gè)transfer可以被添加到message中,作為一個(gè)消息包進(jìn)行傳輸。當(dāng)用 戶發(fā)出傳輸數(shù)據(jù)的請(qǐng)求時(shí),message并不會(huì)立刻傳輸?shù)綇脑O(shè)備,而是由之前定義的transfer()函數(shù)將message放入一個(gè)等待隊(duì)列中,這些 message會(huì)以FIFO的方式有workqueue調(diào)度進(jìn)行傳輸,這樣能夠避免SPI從設(shè)備同一時(shí)間對(duì)主SPI控制器的競爭。和之前一樣,還是習(xí)慣先 畫一張圖來描述數(shù)據(jù)傳輸?shù)闹饕^程。在使用spidev設(shè)備驅(qū)動(dòng)時(shí),需要先初始化spidev. spidev是以字符設(shè)備的形式注冊(cè)進(jìn)內(nèi)核的。 static int __init spidev_init(void) { int status; /* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */ BUILD_BUG_ON(N_SPI_MINORS > 256); /*將spidev作為字符設(shè)備注冊(cè)*/ status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); if (status < 0) return status; /*創(chuàng)建spidev類*/ spidev_class = class_create(THIS_MODULE, "spidev"); if (IS_ERR(spidev_class)) { unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); return PTR_ERR(spidev_class); } /*注冊(cè)spidev的driver,可與modalias字段為"spidev"的spi_device匹配*/ status = spi_register_driver(&spidev_spi); if (status < 0) { class_destroy(spidev_class); unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); } return status; } 與相應(yīng)的從設(shè)備匹配成功后,則調(diào)用spidev中的probe函數(shù) static int spidev_probe(struct spi_device *spi) { struct spidev_data *spidev; int status; unsigned long minor; /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; /* Initialize the driver data */ spidev->spi = spi;//設(shè)定spi spin_lock_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock); minor = find_first_zero_bit(minors, N_SPI_MINORS);//尋找沒被占用的次設(shè)備號(hào) if (minor < N_SPI_MINORS) { struct device *dev; /*計(jì)算設(shè)備號(hào)*/ spidev->devt = MKDEV(SPIDEV_MAJOR, minor); /*在spidev_class下創(chuàng)建設(shè)備*/ dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d", spi->master->bus_num, spi->chip_select); status = IS_ERR(dev) ? PTR_ERR(dev) : 0; } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; } if (status == 0) { set_bit(minor, minors);//將minors的相應(yīng)位置位,表示該位對(duì)應(yīng)的次設(shè)備號(hào)已被占用 list_add(&spidev->device_entry, &device_list);//將創(chuàng)建的spidev添加到device_list } mutex_unlock(&device_list_lock); if (status == 0) spi_set_drvdata(spi, spidev); elsekfree(spidev); return status; } 然后就可以利用spidev模塊提供的接口來實(shí)現(xiàn)主從設(shè)備之間的數(shù)據(jù)傳輸了。我們以spidev_write()函數(shù)為例來分析數(shù)據(jù)傳輸?shù)倪^程,實(shí)際上spidev_read()和其是差不多的,只是前面的一些步驟不一樣,可以參照上圖。 static ssize_t
spidev_write(
struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; ssize_t status = 0; unsigned long missing; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; mutex_lock(&spidev->buf_lock); //將用戶要發(fā)送的數(shù)據(jù)拷貝到spidev->buffer missing = copy_from_user(spidev->buffer, buf, count); if (missing == 0) {//全部拷貝成功,則調(diào)用spidev_sysn_write() status = spidev_sync_write(spidev, count); } else status = -EFAULT; mutex_unlock(&spidev->buf_lock); return status; } -
static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = {//設(shè)置傳輸字段 .tx_buf = spidev->buffer, .len = len, }; struct spi_message m;//創(chuàng)建message spi_message_init(&m); spi_message_add_tail(&t, &m);//將transfer添加到message中 return spidev_sync(spidev, &m); } 我們來看看struct spi_transfer和struct spi_message是如何定義的 struct spi_transfer { /* it's ok if tx_buf == rx_buf (right?) * for MicroWire, one buffer must be null * buffers must work with dma_*map_single() calls, unless * spi_message.is_dma_mapped reports a pre-existing mapping */ const void *tx_buf;//發(fā)送緩沖區(qū) void *rx_buf;//接收緩沖區(qū) unsigned len; //傳輸數(shù)據(jù)的長度 dma_addr_t tx_dma; dma_addr_t rx_dma; unsigned cs_change:1; //該位如果為1,則表示當(dāng)該transfer傳輸完后,改變片選信號(hào) u8 bits_per_word;//字比特?cái)?shù) u16 delay_usecs; //傳輸后的延時(shí) u32 speed_hz; //指定的時(shí)鐘 struct list_head transfer_list;//用于將該transfer鏈入message }; - struct spi_message { struct list_head transfers;//用于鏈接spi_transfer struct spi_device *spi; //指向目的從設(shè)備 unsigned is_dma_mapped:1; /* REVISIT: we might want a flag affecting the behavior of the * last transfer ... allowing things like "read 16 bit length L" * immediately followed by "read L bytes". Basically imposing * a specific message scheduling algorithm. * * Some controller drivers (message-at-a-time queue processing) * could provide that as their default scheduling algorithm. But * others (with multi-message pipelines) could need a flag to * tell them about such special cases. */ /* completion is reported through a callback */ void (*complete)(void *context);//用于異步傳輸完成時(shí)調(diào)用的回調(diào)函數(shù) void *context; //回調(diào)函數(shù)的參數(shù) unsigned actual_length; //實(shí)際傳輸?shù)拈L度 int status; /* for optional use by whatever driver currently owns the * spi_message ... between calls to spi_async and then later * complete(), that's the spi_master controller driver. */ struct list_head queue; //用于將該message鏈入bitbang等待隊(duì)列 void *state; }; 繼續(xù)跟蹤源碼,進(jìn)入spidev_sync(),從這一步開始,read和write就完全一樣了 spidev_sync(struct spidev_data *spidev, struct spi_message *message) {DECLARE_COMPLETION_ONSTACK(done); int status; message->complete = spidev_complete;//設(shè)置回調(diào)函數(shù) message->context = &done; spin_lock_irq(&spidev->spi_lock); if (spidev->spi == NULL) status = -ESHUTDOWN; else status = spi_async(spidev->spi, message);//調(diào)用spi核心層的函數(shù)spi_async() spin_unlock_irq(&spidev->spi_lock); if (status == 0) { wait_for_completion(&done); status = message->status; if (status == 0) status = message->actual_length; } return status; } - ? static inline int spi_async(struct spi_device *spi, struct spi_message *message) {message->spi = spi; /*調(diào)用master的transfer函數(shù)將message放入等待隊(duì)列*/ return spi->master->transfer(spi, message); } s3c24xx平臺(tái)下的transfer函數(shù)是在bitbang_start()函數(shù)中定義的,為bitbang_transfer() int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) { struct spi_bitbang *bitbang; unsigned long flags; int status = 0; m->actual_length = 0; m->status = -EINPROGRESS; bitbang = spi_master_get_devdata(spi->master); spin_lock_irqsave(&bitbang->lock, flags); if (!spi->max_speed_hz) status = -ENETDOWN; else { list_add_tail(&m->queue, &bitbang->queue);//將message添加到bitbang的等待隊(duì)列 queue_work(bitbang->workqueue, &bitbang->work);//調(diào)度運(yùn)行work } spin_unlock_irqrestore(&bitbang->lock, flags); return status; } 這里可以看到transfer函數(shù)不負(fù)責(zé)實(shí)際的數(shù)據(jù)傳輸,而是將message添加到 等待隊(duì)列中。同樣在spi_bitbang_start()中,有這樣一個(gè)定義INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函數(shù)會(huì)被調(diào)度運(yùn)行,類似于底半部機(jī)制 static void bitbang_work(struct work_struct *work) { struct spi_bitbang *bitbang = container_of(work, struct spi_bitbang, work);//獲取bitbang unsigned long flags; spin_lock_irqsave(&bitbang->lock, flags); bitbang->busy = 1; while (!list_empty(&bitbang->queue)) {//等待隊(duì)列不為空 struct spi_message *m; struct spi_device *spi; unsigned nsecs; struct spi_transfer *t = NULL; unsigned tmp; unsigned cs_change; int status; int (*setup_transfer)(struct spi_device *, struct spi_transfer *); /*取出等待隊(duì)列中的的第一個(gè)message*/ m = container_of(bitbang->queue.next, struct spi_message, queue); list_del_init(&m->queue);//將message從隊(duì)列中刪除 spin_unlock_irqrestore(&bitbang->lock, flags); /* FIXME this is made-up ... the correct value is known to * word-at-a-time bitbang code, and presumably chipselect() * should enforce these requirements too? */ nsecs = 100; spi = m->spi; tmp = 0; cs_change = 1; status = 0; setup_transfer = NULL; /*遍歷message中的所有傳輸字段,逐一進(jìn)行傳輸*/ list_for_each_entry (t, &m->transfers, transfer_list) { /* override or restore speed and wordsize */ if (t->speed_hz || t->bits_per_word) { setup_transfer = bitbang->setup_transfer; if (!setup_transfer) { status = -ENOPROTOOPT; break; } } /*調(diào)用setup_transfer根據(jù)transfer中的信息進(jìn)行時(shí)鐘、字比特?cái)?shù)的設(shè)定*/ if (setup_transfer) { status = setup_transfer(spi, t); if (status < 0) break; } /* set up default clock polarity, and activate chip; * this implicitly updates clock and spi modes as * previously recorded for this device via setup(). * (and also deselects any other chip that might be * selected ...) */ if (cs_change) {//使能外設(shè)的片選 bitbang->chipselect(spi, BITBANG_CS_ACTIVE); ndelay(nsecs); } cs_change = t->cs_change;//這里確定進(jìn)行了這個(gè)字段的傳輸后是否要改變片選狀態(tài)
if (!t->tx_buf && !t->rx_buf && t->len) { status = -EINVAL; break; } /* transfer data. the lower level code handles any * new dma mappings it needs. our caller always gave * us dma-safe buffers. */ if (t->len) { /* REVISIT dma API still needs a designated * DMA_ADDR_INVALID; ~0 might be better. */ if (!m->is_dma_mapped) t->rx_dma = t->tx_dma = 0; /*調(diào)用針對(duì)于平臺(tái)的傳輸函數(shù)txrx_bufs*/ status = bitbang->txrx_bufs(spi, t); } if (status > 0) m->actual_length += status; if (status != t->len) { /* always report some kind of error */ if (status >= 0) status = -EREMOTEIO; break; } status = 0; /* protocol tweaks before next transfer */ /*如果要求在傳輸完一個(gè)字段后進(jìn)行delay,則進(jìn)行delay*/ if (t->delay_usecs) udelay(t->delay_usecs); if (!cs_change) continue; /*最后一個(gè)字段傳輸完畢了,則跳出循環(huán)*/ if (t->transfer_list.next == &m->transfers) break; /* sometimes a short mid-message deselect of the chip * may be needed to terminate a mode or command */ ndelay(nsecs); bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); } m->status = status; m->complete(m->context); /* restore speed and wordsize */ if (setup_transfer) setup_transfer(spi, NULL); /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably * be for this chip too. */ if (!(status == 0 && cs_change)) { ndelay(nsecs); bitbang->chipselect(spi, BITBANG_CS_INACTIVE); ndelay(nsecs); } spin_lock_irqsave(&bitbang->lock, flags); } bitbang->busy = 0; spin_unlock_irqrestore(&bitbang->lock, flags); } 只要bitbang->queue等待隊(duì)列不為空,就表示相應(yīng)的SPI主控制器上還有 傳輸任務(wù)沒有完成,因此bitbang_work()會(huì)被不斷地調(diào)度執(zhí)行。 bitbang_work()中的工作主要是兩個(gè)循環(huán),外循環(huán)遍歷等待隊(duì)列中的message,內(nèi)循環(huán)遍歷message中的transfer,在 bitbang_work()中,傳輸總是以transfer為單位的。當(dāng)選定了一個(gè)transfer后,便會(huì)調(diào)用transfer_txrx()函數(shù), 進(jìn)行實(shí)際的數(shù)據(jù)傳輸,顯然這個(gè)函數(shù)是針對(duì)于平臺(tái)的SPI控制器而實(shí)現(xiàn)的,在s3c24xx平臺(tái)中,該函數(shù)為s3c24xx_spi_txrx();

static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) { struct s3c24xx_spi *hw = to_hw(spi);
dev_dbg(
&spi->dev, "txrx: tx %p, rx %p, len %d\n", t->tx_buf, t->rx_buf, t->len); hw->tx = t->tx_buf;//獲取發(fā)送緩沖區(qū) hw->rx = t->rx_buf;//獲取讀取緩存區(qū) hw->len = t->len; //獲取數(shù)據(jù)長度 hw->count = 0; init_completion(&hw->done);//初始化完成量 /* send the first byte */ /*只發(fā)送第一個(gè)字節(jié),其他的在中斷中發(fā)送(讀取)*/ writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); wait_for_completion(&hw->done); return hw->count; } -
static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) { /*如果tx不為空,也就是說當(dāng)前是從主機(jī)向從機(jī)發(fā)送數(shù)據(jù),則直接將tx[count]發(fā)送過去, 如果tx為空,也就是說當(dāng)前是從從機(jī)向主機(jī)發(fā)送數(shù)據(jù),則向從機(jī)寫入0*/ return hw->tx ? hw->tx[count] : 0; } 負(fù)責(zé)SPI數(shù)據(jù)傳輸?shù)闹袛嗪瘮?shù):

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev) { struct s3c24xx_spi *hw = dev; unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); unsigned int count = hw->count; /*沖突檢測*/ if (spsta & S3C2410_SPSTA_DCOL) { dev_dbg(hw->dev, "data-collision\n"); complete(&hw->done); goto irq_done; } /*設(shè)備忙檢測*/ if (!(spsta & S3C2410_SPSTA_READY)) { dev_dbg(hw->dev, "spi not ready for tx?\n"); complete(&hw->done); goto irq_done; } hw->count++; if (hw->rx)//讀取數(shù)據(jù)到緩沖區(qū) hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); count++; if (count < hw->len)//向從機(jī)寫入數(shù)據(jù) writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT); else//count == len,一個(gè)字段發(fā)送完成,喚醒完成量 complete(&hw->done); irq_done: return IRQ_HANDLED; } 這里可以看到一點(diǎn),即使tx為空,也就是說用戶申請(qǐng)的是從從設(shè)備讀取數(shù)據(jù),也要不斷地向從設(shè)備寫入數(shù)據(jù),只不過寫入從設(shè)備的是無效數(shù)據(jù)(0),這樣做得目的是為了維持SPI總線上的時(shí)鐘。至此,SPI框架已分析完畢。

?

轉(zhuǎn)載于:https://www.cnblogs.com/tfanalysis/articles/3766212.html

總結(jié)

以上是生活随笔為你收集整理的Linux SPI框架的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

3d黄动漫免费看 | 亚洲精品美女久久久久 | 五月婷婷色丁香 | 亚洲一级片av | 中文字幕免费国产精品 | 日日弄天天弄美女bbbb | 日本久久电影网 | 99操视频 | 国产黄色精品在线观看 | 六月丁香激情网 | 国语麻豆 | 在线观看亚洲专区 | 久久午夜羞羞影院 | 99视频免费在线观看 | 人人玩人人添人人 | 黄色小说免费在线观看 | 99re8这里有精品热视频免费 | 国产精品伦一区二区三区视频 | 亚洲视频精品在线 | 深爱五月激情五月 | 国内久久久| 激情av在线资源 | 欧美精品少妇xxxxx喷水 | 三级在线视频播放 | 精品视频123区在线观看 | 九色在线| 国产原创在线 | 欧美精品在线视频 | 精品视频在线播放 | 一区在线观看视频 | 99热亚洲精品 | 伊人久久精品久久亚洲一区 | 香蕉日日| 国产精品成人一区二区三区 | 视频在线一区 | www.婷婷色| 久草电影在线 | 国产综合久久 | 波多野结衣电影一区二区 | 中文在线字幕免 | 久久午夜国产精品 | 成年人黄色大片在线 | 98福利在线 | 国产精品久久久久久久久久白浆 | 国产午夜精品福利视频 | 日女人电影 | 亚洲精品黄网站 | 一区二区视频免费在线观看 | 黄视频色网站 | 亚洲夜夜网 | 国产成人精品一区二区在线 | 婷婷天天色 | av中文字幕免费在线观看 | 日韩电影一区二区三区 | 在线观看麻豆av | 免费看片网址 | 日韩剧情 | 亚洲激情校园春色 | 中文字幕高清av | 国语久久 | 国产明星视频三级a三级点| 亚洲视频一 | 黄色精品国产 | 亚洲高清视频一区二区三区 | 欧美亚洲三级 | 久久精品一区二区三区国产主播 | 黄色精品一区二区 | 亚洲一区二区三区毛片 | 久久久久 | 成人黄色大片在线观看 | 国产精品一区二区三区免费看 | 成人久久精品视频 | 亚洲精品91天天久久人人 | 久久精品视频免费观看 | 国产免费嫩草影院 | 久久免费播放 | 国产精品久久久久久久久大全 | 久久久999精品视频 国产美女免费观看 | 99视频在线免费播放 | 久艹视频免费观看 | 国产成人免费观看久久久 | 婷婷丁香花 | 操操操天天操 | 久久国产精品久久精品 | 三级av免费 | 91专区在线观看 | 天堂素人在线 | 在线免费色视频 | 国产一级一级国产 | 精品国内 | 久草视频观看 | 成人在线超碰 | 久久精品一二三区 | 欧美亚洲xxx| 99riav1国产精品视频 | 国产精品视频免费观看 | 亚洲欧美视频在线 | 日韩精品一区二区三区中文字幕 | 免费观看一级视频 | 91精品国自产拍天天拍 | 中字幕视频在线永久在线观看免费 | 婷婷中文字幕 | 99在线免费视频 | 超碰人人在线观看 | 91在线日本 | 国色天香永久免费 | 久久综合九色综合欧美就去吻 | 欧美亚洲成人免费 | 久久99久久久久久 | 天天干,狠狠干 | 亚洲免费av在线 | 久久综合给合久久狠狠色 | 国产裸体永久免费视频网站 | 国产69久久久欧美一级 | 久久综合免费 | 免费精品人在线二线三线 | 麻豆视频免费入口 | 亚洲狠狠干 | 久久国产免 | 国产高清av免费在线观看 | 国产福利精品在线观看 | 午夜久久美女 | 成人理论在线观看 | www.亚洲视频.com | 国产免费一区二区三区最新6 | 97电影在线看视频 | 在线观看黄色国产 | 久99视频 | 日韩欧美精品在线 | 伊人导航| 亚欧洲精品视频在线观看 | 91视频免费国产 | 久久精品免费 | 国产欧美日韩一区 | 色先锋资源网 | 色视频成人在线观看免 | www.超碰97.com| 深夜福利视频一区二区 | 五月婷婷一区二区三区 | 国产成人精品电影久久久 | 视频成人| 中文字幕免费中文 | 天天干.com | 日韩免费视频观看 | 精品专区一区二区 | 久久久69| 911香蕉| 又黄又刺激视频 | 在线看一区二区 | 香蕉精品视频在线观看 | 在线激情小视频 | 久久国产精品一区二区三区 | 亚洲三级毛片 | 国产韩国日本高清视频 | 97超视频免费观看 | 国产一卡二卡在线 | 激情www | 婷婷亚洲五月色综合 | 91系列在线 | 日韩欧美一区二区三区在线观看 | 国产精品成人一区二区三区吃奶 | 免费在线中文字幕 | 成人免费一区二区三区在线观看 | 久久精品一 | 国产美女免费视频 | 欧美黄污视频 | av电影一区 | 国产日韩三级 | 国产小视频免费在线观看 | 国内免费的中文字幕 | 国产一区在线不卡 | 国产99免费视频 | 黄色网www| 国产一区私人高清影院 | 亚洲人成人在线 | 五月天婷婷免费视频 | 国产成人精品一区二区在线 | 97成人精品视频在线播放 | 国产黄色理论片 | 中国一级片在线观看 | 精品久久久久久综合 | 免费黄色激情视频 | 亚洲天堂网在线观看视频 | 欧美一区二区在线 | 一级黄色在线视频 | 欧美极品xxxx | 日韩精品无 | 中文字幕在线观看一区 | 日韩欧美视频一区 | 国内外成人在线视频 | 成年人免费在线观看网站 | 久久久91精品国产一区二区三区 | 亚洲精品国产视频 | 在线免费观看黄网站 | 国产午夜精品av一区二区 | 色婷婷www | 国产精品美乳一区二区免费 | 一区二区视频电影在线观看 | 亚洲午夜在线视频 | 亚洲人成在线观看 | 国产亚洲精品久久久久久 | 97人人看| 亚洲精品国产欧美在线观看 | 五月婷婷视频在线 | 精品久久国产一区 | 欧美日韩高清免费 | 国产精品美女久久 | 四虎在线观看 | 成人app在线播放 | 亚洲综合网| 国产婷婷在线观看 | 中文字幕日本在线观看 | 一区二区三区精品在线视频 | 一区二区三区久久精品 | 麻豆视频成人 | 黄免费在线观看 | 色999精品 | 91女人18片女毛片60分钟 | 成人福利在线播放 | 91视频在线自拍 | 婷婷综合久久 | 黄色激情网址 | 国产精品电影在线 | 精品一区二区精品 | 2023亚洲精品国偷拍自产在线 | 黄色免费网 | 国产精品2区 | 欧美日韩一区二区三区在线观看视频 | av短片在线观看 | 最新婷婷色 | av一级二级| 99热官网 | 黄色三级网站 | 亚洲涩综合 | 亚洲精品午夜久久久久久久 | 91超碰在线播放 | 欧美精品久久久久久 | 久久久久久久久久国产精品 | 天天干天天操天天搞 | 在线成人小视频 | 中文av在线播放 | 一区二区三区免费在线播放 | 日韩视频 一区 | 97碰碰精品嫩模在线播放 | 色吊丝av中文字幕 | 久久久久久久久久久久久久电影 | 五月婷婷丁香激情 | 97人人澡人人爽人人模亚洲 | 五月婷婷六月综合 | 操操操操网 | 精品视频| 91久久精品一区二区二区 | 免费性网站 | 成人免费在线播放视频 | 成年人免费电影 | 激情综合色综合久久 | 综合色播| 亚洲清纯国产 | 国产专区第一页 | 91污视频在线 | 成人观看| 国内丰满少妇猛烈精品播 | 人人澡人人澡人人 | 国内精品久久久久影院优 | 亚洲精品玖玖玖av在线看 | 婷婷亚洲综合 | 国产婷婷视频在线 | 免费在线观看成年人视频 | 麻豆传媒视频在线免费观看 | 97免费视频在线 | 欧美日韩高清在线一区 | 久久国产精品电影 | 男女啪啪视屏 | 久久99网站 | 欧美日韩高清一区二区 国产亚洲免费看 | 亚洲国产无 | 国产三级精品三级在线观看 | 九九视频免费 | 午夜免费电影院 | 久久久久北条麻妃免费看 | 91麻豆精品| 国产视频一区在线免费观看 | 91精品对白一区国产伦 | 色播五月激情五月 | av性网站 | 综合精品久久久 | 欧美精彩视频在线观看 | a黄色| 天天视频亚洲 | 五月婷婷视频在线观看 | 91丨九色丨勾搭 | 天天干夜夜想 | 久久久久久免费视频 | 五月婷婷一级片 | 久久人人爽人人爽人人 | 九九九九九九精品任你躁 | 国产日韩精品一区二区在线观看播放 | 日本精品中文字幕在线观看 | 久久久五月婷婷 | 天天操月月操 | 欧美精彩视频在线观看 | 高潮久久久久久久久 | 极品美女被弄高潮视频网站 | 精品美女久久 | 天天干天天草 | 激情视频二区 | 精品夜夜嗨av一区二区三区 | 黄色在线网站噜噜噜 | 黄色大片日本免费大片 | 中文字幕精品一区二区三区电影 | 久av在线 | 国产手机在线观看 | 国产乱对白刺激视频不卡 | 国产精品mv在线观看 | 日韩精品不卡 | 国产福利精品在线观看 | 亚洲资源在线观看 | 亚洲乱码在线 | 成人在线一区二区 | 97视频人人 | 日韩av黄 | 久久美女精品 | 国产精品九九热 | 亚洲欧美乱综合图片区小说区 | 亚洲欧美在线观看视频 | 麻豆成人精品视频 | 亚洲精品永久免费视频 | 国产亚洲精品久久久网站好莱 | 日韩在线视频观看 | 色偷偷男人的天堂av | 91麻豆精品一区二区三区 | 中文字幕在线观看91 | 射射射综合网 | 亚洲美女视频在线观看 | 91高清在线 | 国产成年免费视频 | 色婷婷久久久综合中文字幕 | 亚洲黄色成人 | 日韩精品一区二区三区丰满 | 麻豆国产在线视频 | 热热热热热色 | 久久久久久久99 | 91天堂素人约啪 | 五月天天天操 | 99精品视频网站 | 狠狠ri| 91爱爱免费观看 | 亚洲日本一区二区在线 | 麻豆系列在线观看 | 在线观看视频日韩 | 免费国产一区二区 | 精品久久久久久一区二区里番 | 成年人黄色免费网站 | 国产一级性生活 | 亚洲人人av | 日韩网 | 欧洲色综合 | 91资源在线观看 | 最近免费中文字幕 | 欧美五月婷婷 | 激情欧美在线观看 | 国产精品毛片一区视频播不卡 | 日韩精品在线视频 | 人人讲下载 | 久久久网站 | 热久久电影 | 91精品国产91热久久久做人人 | 在线观看av国产 | www夜夜操 | 久久手机免费观看 | 久久免费激情视频 | 在线观看亚洲精品视频 | 91视频a | 婷婷伊人网 | a级国产乱理论片在线观看 特级毛片在线观看 | 精品国精品自拍自在线 | 中文在线字幕免费观看 | 色com | 国产高清一 | 激情五月网站 | 国产精品麻豆91 | 成人中文字幕av | 国产精品免费视频一区二区 | 夜又临在线观看 | 日韩欧美国产激情在线播放 | 在线v片免费观看视频 | 99久久精品免费看国产一区二区三区 | 中文字幕亚洲欧美日韩 | 国产精品久久久久久久久久尿 | 免费久久网站 | 91一区二区三区在线观看 | 一区二区三区中文字幕在线观看 | 日韩精品综合在线 | 大胆欧美gogo免费视频一二区 | 99精品久久精品一区二区 | 精品国产aⅴ麻豆 | 欧美激情综合色综合啪啪五月 | 99久久影院| 欧美性久久久久久 | 在线免费av网 | 欧美日韩久久一区 | 91激情小视频 | 久久久久久久影视 | 韩国一区二区三区在线观看 | 中文字幕网址 | 成人国产在线 | 成人视屏免费看 | 精品国产1区二区 | 日韩婷婷| 国产91国语对白在线 | 手机看片国产日韩 | 亚洲第一中文网 | 美女av免费看 | 成人中文字幕在线 | 91传媒在线观看 | 麻豆91精品视频 | 久99久在线 | 69国产盗摄一区二区三区五区 | 91视频在线网址 | 天天干,天天干 | av资源免费观看 | 中文字幕成人一区 | 中文字幕中文字幕 | 中文字幕91视频 | 久久高清国产视频 | 久久成人人人人精品欧 | 波多野结衣动态图 | 在线三级中文 | 亚洲97在线| 日韩精选在线观看 | 91成人黄色 | 国产成人精品福利 | 日本久久视频 | 偷拍精品一区二区三区 | 国产vs久久 | 6080yy午夜一二三区久久 | 亚洲一级影院 | 亚洲美女视频在线 | 天天射,天天干 | 国产片免费在线观看视频 | 精品国产区 | 又黄又刺激的视频 | av一级一片 | 欧美日韩一区二区在线观看 | 欧美一级片免费 | 国产性天天综合网 | 免费试看一区 | 成在人线av | 色婷婷视频网 | 91桃色在线播放 | 精品久久免费看 | av大全在线观看 | 婷婷六月综合亚洲 | 成年人免费观看在线视频 | 色成人亚洲 | 精品一区二三区 | 日日夜夜网 | 五月激情综合婷婷 | 中文字幕在线一二 | 成人黄色小说网 | 欧美视屏一区二区 | 久草在线免费电影 | 日韩激情久久 | 91精品老司机久久一区啪 | 网址你懂的在线观看 | 久久综合九色综合欧美狠狠 | a级片久久久 | 欧美看片| 天天插天天色 | 日日夜夜精品视频天天综合网 | 91激情视频在线观看 | 久久精选 | 国产精品原创av片国产免费 | 国产精品久久久 | 91免费网站在线观看 | 成人免费观看在线视频 | 天天狠狠操| 人人爽人人澡人人添人人人人 | 成在线播放 | 精品国产一区二区三区久久久蜜月 | 制服丝袜亚洲 | 九九九热精品免费视频观看 | 亚洲黄电影 | 欧美精品久久久久久久久老牛影院 | 久久久久久毛片精品免费不卡 | 午夜在线免费视频 | 成片免费观看视频大全 | 狠狠的操狠狠的干 | 国产精品久久久久aaaa | 天天操天天谢 | 国产 日韩 欧美 在线 | 国产免码va在线观看免费 | 亚洲欧美视频在线 | 国产无遮挡猛进猛出免费软件 | 亚洲美女精品 | 天天操天天爽天天干 | 国精产品999国精产品岳 | 亚洲欧美国内爽妇网 | 国产不卡一区二区视频 | 日韩精品一区二区三区中文字幕 | 亚洲人成免费网站 | 日韩动漫免费观看高清完整版在线观看 | 一级黄色毛片 | 深夜激情影院 | 国产极品尤物在线 | 香蕉看片 | 成人毛片100免费观看 | 国产精品 日韩精品 | 成人av影视 | 超碰官网| 久久久久久国产一区二区三区 | 97在线观看免费观看高清 | 99免费在线视频 | 国产成人高清在线 | 国产色综合天天综合网 | 91福利视频久久久久 | 91粉色视频 | 日韩精品视频免费在线观看 | 欧美综合国产 | 国产精品国产三级国产aⅴ9色 | 日韩一区二区免费播放 | 国产成人在线精品 | 日韩大陆欧美高清视频区 | 日本视频精品 | aaa亚洲精品一二三区 | 狠狠操影视 | av大全在线观看 | 色噜噜狠狠色综合中国 | 91av电影在线观看 | 成人蜜桃视频 | 99久久精品无码一区二区毛片 | www.亚洲| 国产专区一 | 亚洲精品资源在线观看 | 黄色成人av | 婷婷激情5月天 | 亚洲国产综合在线 | 成人a在线观看高清电影 | 国产激情小视频在线观看 | 精品亚洲成人 | 伊人天天狠天天添日日拍 | 最近免费中文字幕 | 看片在线亚洲 | 亚洲精品视频免费在线 | 五月天婷亚洲天综合网精品偷 | www成人精品 | 97超碰人人澡 | 五月天精品视频 | av电影亚洲| 玖玖视频在线 | 午夜视频免费在线观看 | 欧美精品中文 | 91av在线电影 | 日韩理论电影在线观看 | 亚洲一区二区精品3399 | 久久国产精彩视频 | 色姑娘综合 | 91最新地址永久入口 | av成人免费网站 | 91精品一区二区三区蜜臀 | 波多野结衣在线观看视频 | 蜜桃视频在线视频 | 99精品久久久久久久久久综合 | 18岁免费看片 | 综合久久五月天 | 欧美另类高潮 | 在线视频 亚洲 | 国产精品久久久久亚洲影视 | 精品在线视频播放 | 麻豆视频在线观看 | 91女子私密保健养生少妇 | 久久久免费精品视频 | 黄色片网站av | 国产免费二区 | 久久伊人婷婷 | 国产精品涩涩屋www在线观看 | 免费国产黄线在线观看视频 | 懂色av懂色av粉嫩av分享吧 | 狠狠的日日 | 精品一区av | 天天射天天舔天天干 | 一级片黄色片网站 | 亚洲国产成人在线播放 | 久久这里只有精品首页 | 婷婷综合亚洲 | 国产韩国日本高清视频 | 亚洲成人精品久久 | 天天色天天干天天 | 久久人人做| 久久香蕉一区 | 91自拍成人 | 高清av网| 久久99热久久99精品 | 精品久久久久久一区二区里番 | 国产在线观看不卡 | 亚洲精品永久免费视频 | 黄色精品久久久 | 一本一道久久a久久精品 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 日韩在线视频国产 | 久久久久综合视频 | 国产精品99精品 | 日韩1级片 | 国产精品视频在线看 | 久久免费资源 | 久久av电影| 精品国产乱码久久久久久三级人 | 久久久久国产成人免费精品免费 | 久久99亚洲网美利坚合众国 | 亚洲第一成网站 | 97国产精品免费 | 91漂亮少妇露脸在线播放 | 四虎在线免费视频 | 福利一区二区在线 | 91香蕉久久| 国产在线自| 99在线视频播放 | av在线短片 | 97日日 | 国产美女黄网站免费 | 免费a现在观看 | 国产破处在线播放 | 日韩中文字幕免费在线观看 | 欧美a级在线免费观看 | 91黄站| 国产玖玖精品视频 | 五月天丁香亚洲 | 久久电影中文字幕视频 | 欧美激情视频一区 | 日本电影黄色 | 久青草影院 | 国产专区视频 | 四虎影视成人永久免费观看亚洲欧美 | 在线免费国产 | 日本女人逼 | 人人澡超碰碰97碰碰碰软件 | 91精品在线免费观看 | 97超碰在| 一本一本久久a久久精品综合 | 国产九色91| 免费av 在线 | 伊人资源站 | 麻豆久久久 | 欧美精品中文在线免费观看 | 国产18精品乱码免费看 | 69av视频在线 | 中文字幕日韩电影 | 国产精品男女视频 | 欧美精品一区二区蜜臀亚洲 | 中文字幕国产在线 | 久草成人在线 | 国产一区影院 | 色综合天天综合 | 91看片成人| 国产免费叼嘿网站免费 | 中文字幕欧美日韩va免费视频 | 成年人免费av网站 | 国产美腿白丝袜足在线av | 午夜精品久久久久久久久久久久久久 | 视频一区二区免费 | 亚洲综合爱 | 91中文字幕在线观看 | 一区二区中文字幕在线观看 | 激情婷婷 | 国产美女视频免费 | 欧美精品久久久久久久久久丰满 | 国产精品原创 | 国产精品欧美一区二区三区不卡 | 日韩一区二区三区在线看 | 久久久久久久久网站 | 国产麻豆精品95视频 | 精品国自产在线观看 | 中文字幕免费在线看 | 成人国产精品一区 | 日韩视频二区 | 欧美性色综合 | 波多野结衣网址 | 免费av免费观看 | 欧美日韩一区二区在线观看 | 国产精品美女视频网站 | 激情视频国产 | 免费在线播放黄色 | 人人超碰97 | av在线一级 | 久久露脸国产精品 | 日本中文乱码卡一卡二新区 | 91九色视频在线 | 操碰av | 激情狠狠干 | 国产精品白虎 | 日韩精品视频久久 | 99久久精品国产一区二区三区 | 国产精品久久久久久电影 | 成人av一区二区兰花在线播放 | a色视频| 又黄又网站 | 国产免费又黄又爽 | 亚洲免费不卡 | 麻豆传媒视频在线免费观看 | 欧美一级性生活片 | 黄色软件在线看 | 成人小视频在线观看免费 | 婷婷亚洲激情 | 国产资源精品在线观看 | 成人精品久久 | 天天综合网久久综合网 | 日韩一级成人av | 亚洲精品在线视频网站 | 美女久久视频 | 久久伊人国产精品 | 久久国产露脸精品国产 | 国产精品久久久久久久电影 | 久久不卡国产精品一区二区 | 久草免费在线视频观看 | 国产精品亚洲精品 | 国产一区欧美二区 | av黄色av| 97国产视频 | 亚洲激情在线播放 | 久久综合九色九九 | 午夜精品久久久久久99热明星 | 丁香在线 | 天天操天天操天天爽 | .国产精品成人自产拍在线观看6 | 国产精品2020 | 国产精品女视频 | 综合久久久久 | aaa免费毛片 | 欧美二区在线播放 | 人人搞人人爽 | 国产永久网站 | 精品在线二区 | 国产精品麻豆91 | 精品自拍网| 亚洲福利精品 | 一级黄色在线免费观看 | 成人av免费在线播放 | 九九热免费视频在线观看 | 久久综合色一综合色88 | 天堂黄色片 | 美女一区网站 | 天天操天天干天天插 | 中文字幕日本电影 | 色五月激情五月 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 在线观看中文字幕第一页 | 久久久久久国产精品 | 成人免费网站在线观看 | 中文字幕视频一区 | 天天操人| 在线99热 | 国产中文字幕在线播放 | 久久精品www人人爽人人 | 又湿又紧又大又爽a视频国产 | 国产 字幕 制服 中文 在线 | 911亚洲精品第一 | 激情久久影院 | 成人小视频在线免费观看 | 婷婷色综合 | 欧美日韩大片在线观看 | 97福利在线观看 | 日韩高清激情 | 国产在线一卡 | 久操视频在线播放 | 狠狠久久| 99热99热 | 日韩高清二区 | 天天爽天天爽天天爽 | 久久精品国产精品亚洲 | 国产免费视频在线 | 天天草夜夜 | 玖玖视频在线 | 欧美在线资源 | 免费看黄色大全 | 91九色在线播放 | 国产成人久久精品77777综合 | 99成人精品 | 国产一区福利在线 | 午夜精品一区二区三区在线观看 | 欧美精品在线观看免费 | 日韩欧美精品在线 | 夜夜躁日日躁狠狠躁 | 久久久久免费精品国产 | 99久久99久久精品国产片果冰 | 免费日韩视 | 国产精品 中文在线 | 在线观看爱爱视频 | 99精品免费久久久久久久久 | 久久久久久国产精品免费 | 久久精品国产精品亚洲精品 | 在线免费观看国产视频 | 九九视频精品免费 | 狠狠色丁香婷婷综合久小说久 | 国产精品igao视频网网址 | 天堂va在线观看 | 国产精品激情在线观看 | 日韩视频www | 久久久免费看视频 | 久久久视频在线 | 91视频在线免费下载 | 韩国av一区二区三区在线观看 | 亚洲国产精品va在线 | 久久精品这里热有精品 | 操操操日日日 | 福利在线看片 | 国产精品一区一区三区 | 久久视频在线视频 | 在线观看视频日韩 | 成人黄色电影在线播放 | 欧美极品少妇xbxb性爽爽视频 | 成年人在线观看视频免费 | 97超碰中文字幕 | 成人国产精品免费观看 | 国产一区二区在线观看视频 | av电影在线观看完整版一区二区 | aaaaaa毛片| av东方在线 | 久久久久99精品国产片 | 国产精品美女 | 最近中文字幕完整高清 | 久久亚洲精品电影 | 精品免费久久久久 | 久久国产视频网站 | 午夜美女av | 狠狠干天天 | 久久a免费视频 | 黄色avwww| 久久久久亚洲精品成人网小说 | 国产一级片免费视频 | 国产精品第72页 | 亚洲成人黄色在线观看 | 天天干天天看 | 激情喷水| 日韩久久精品一区二区三区 | 色婷婷97 | 99免费在线播放99久久免费 | 夜夜操狠狠干 | 成人av高清在线 | 热久在线| 国产精品久久久一区二区 | 日韩综合视频在线观看 | 美女黄久久| 91尤物在线播放 | 一级c片| 丁香六月天婷婷 | 开心激情婷婷 | 婷婷丁香花五月天 | 久久夜色精品国产欧美乱 | 久久久www成人免费精品 | 天天摸日日操 | 国产精品美女免费 | 四川bbb搡bbb爽爽视频 | 91视频88av| 国产一区二区成人 | 97成人资源 | 久久中文字幕视频 | 日日夜夜天天人人 | 亚洲电影在线看 | 久久精品99国产 | www国产亚洲| 日韩av福利在线 | 人人爱人人爽 | 麻豆国产精品va在线观看不卡 | 一区二区三区在线影院 | 毛片一二区 | 日韩中文幕 | 久久久免费精品视频 | 国产视频 久久久 | 久草在线手机视频 | 日韩av伦理片 | 欧美三级在线播放 | 欧美精品久久久久久久久免 | 婷婷精品国产欧美精品亚洲人人爽 | 在线观看视频一区二区三区 | 久久精品国产成人精品 | 国产成人久久av977小说 | 成人免费xxxxxx视频 | 久久综合久久综合久久 | 日韩av不卡在线播放 | 在线亚洲人成电影网站色www | 天天干天天草 | 97精品国产一二三产区 | 97在线观 | 久久99精品视频 | 亚洲更新最快 | 国产中文字幕久久 | 欧美精品乱码久久久久久 | 亚州性色 | 国产字幕av | 国产精品午夜免费福利视频 | 狠狠色丁香婷婷综合久小说久 | 欧洲高潮三级做爰 | 欧美日韩1区 | 婷婷激情影院 | 91av美女| 免费成人在线观看 | 免费网站在线观看成人 | 久久呀 | 色中射| 99久久精品免费看国产一区二区三区 | 国产电影黄色av | 成人观看视频 | 亚洲精品乱码久久久久v最新版 | 久久免费成人精品视频 | 一区二区视频在线免费观看 | av在线电影网站 | 国产97色 | 久久免费国产精品 | 久在线观看视频 | 国产在线视频不卡 | 国产尤物在线观看 | 日日夜夜免费精品视频 | 日韩久久精品一区二区 | 麻豆一二三精选视频 | 精品在线播放视频 | 日韩资源在线 | 日本91在线| 狠狠色伊人亚洲综合成人 | 五月天婷婷免费视频 | 欧美一区免费观看 | 香蕉视频在线看 | 国产精品一区二区果冻传媒 | 日韩欧美在线观看一区二区 | 日韩精品五月天 | 久久久精品成人 | 青青久草在线视频 | 欧美日韩综合在线 | 欧美日韩午夜 | 日韩一级成人av | 婷婷丁香激情网 | 欧美二区三区91 | 国产精品一区二区久久国产 | 国产福利一区二区在线 | 欧美色图88 | 西西444www大胆高清视频 | 综合色天天 | 国内外激情视频 | 亚洲黄色小说网址 | 中文在线免费一区三区 | 91av视频在线观看免费 | 国产精品久久久久毛片大屁完整版 | 一区二区激情视频 | 美女在线国产 | 少妇bbbb揉bbbb日本 | 精品免费观看视频 | 九九视频精品免费 | 国产精品久久网站 | 狠狠色丁香久久婷婷综合_中 | 日韩日韩日韩日韩 | 免费观看一区二区 | 国产亚洲精品久久久久久移动网络 | www.天天操| 久久久麻豆精品一区二区 | 99超碰在线播放 | 不卡的av电影在线观看 | 国产精品国产三级国产 | 爱爱av在线| 国内精品国产三级国产aⅴ久 | 日韩欧美在线观看一区二区三区 | 人人看97 | 日韩精品欧美视频 | 美州a亚洲一视本频v色道 | 玖玖精品视频 | 亚洲欧美国产视频 | 999电影免费在线观看2020 | 激情综合六月 | 亚洲精品视频大全 | 国产亚洲精品福利 | 最近中文字幕国语免费av | 午夜久久久久久久久 | 综合天堂av久久久久久久 | 久久久久亚洲最大xxxx | 免费看国产一级片 | 天天射天天添 | 国产精品青草综合久久久久99 | 国精产品满18岁在线 | 在线导航福利 | 久久精品牌麻豆国产大山 | 视频在线91| 2020天天干夜夜爽 | 日韩中文字幕亚洲一区二区va在线 | 97视频人人免费看 | av电影中文| 国产黑丝一区二区 | 九九热久久免费视频 | 在线看一区二区 | 亚洲精品免费在线观看视频 | 国产亚洲精品久 | 精品一区精品二区 | 97超碰人人爱 | 免费看色的网站 | 在线av资源 | 国产精品久久久久久久久婷婷 | 中文字幕在线日 | 亚洲精品av中文字幕在线在线 | 久草在线最新免费 | 综合伊人久久 | 伊人在线视频 |