当前位置: 首页 > news >正文

常州建设局网站为什么打不开衡阳网站建设制作

常州建设局网站为什么打不开,衡阳网站建设制作,抽奖机网站怎么做的,营销型网站建设合同范本水平有限#xff0c;描述不当之处还请指出#xff0c;转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7733476 Linux的SPI子系统采用主机驱动和外设驱动分离的思想#xff0c;首先主机SPI控制器是一种平台设备#xff0c;因此它以platform的方式注册进内…水平有限描述不当之处还请指出转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7733476 Linux的SPI子系统采用主机驱动和外设驱动分离的思想首先主机SPI控制器是一种平台设备因此它以platform的方式注册进内核外设的信息是以boardinfo形式静态定义的在创建spi_master时会根据外设的bus_num和主机的bus_num是否相等来选择是否将该外设挂接在该SPI主控制器下。先看SPI子系统中几个关键的数据结构 struct spi_master用来描述一个SPI主控制器 struct spi_master { struct device dev; s16 bus_num; /*总线编号*/ u16 num_chipselect;/*支持的外设数量*/ u16 dma_alignment; int (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用于将消息添加到队列*/ void (*cleanup)(struct spi_device *spi); }; struct spi_device用来描述一个SPI从设备 struct spi_device { struct device dev; struct spi_master *master; /*从设备所属的SPI主控器*/ u32 max_speed_hz; /*最大传输频率*/ u8 chip_select; /*片选号用于区别其他从设备*/ u8 mode; /*传输模式*/ /*各个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; /*每个字的比特数*/ int irq; /*所使用的中断*/ void *controller_state; void *controller_data; char modalias[32]; /*设备名在和从设备驱动匹配时会用到*/ }; struct spi_driver用来描述一个SPI从设备的驱动它的形式和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子系统初始化的第一步就是将SPI总线注册进内核并且在/sys下创建一个spi_master的类以后注册的从设备都将挂接在该总线下 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);//注册SPI总线 if (status 0) goto err1; status class_register(spi_master_class);//注册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总线下的从设备和从设备驱动是如何匹配的也就是spi_match_device函数 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字段进行匹配 这里已经完成了SPI子系统初始化的第一步也就是注册SPI总线这一步是和平台无关的第二步是和平台相关的初始化下一节再做介绍。   上节介绍了SPI子系统中的一些重要数据结构和SPI子系统初始化的第一步也就是注册SPI总线。这节介绍针对于s3c24xx平台的SPI子系统初始化在看具体的代码之前先上一张自己画的图帮助理清初始化的主要步骤 显然SPI是一种平台特定的资源所以它是以platform平台设备的方式注册进 内核的因此它的struct platform_device结构是已经静态定义好了的现在只待它的struct platform_driver注册然后和platform_device匹配。 初始化的入口 static int __init s3c24xx_spi_init(void) { return platform_driver_probe(s3c24xx_spi_driver, s3c24xx_spi_probe); } platform_driver_probe()会调用 platform_driver_register()来注册驱动然后在注册的过程中寻求匹配的platform_device,一旦匹配成功便会调 用probe函数也就是s3c24xx_spi_probe()在看这个函数之前还得介绍几个相关的数据结构。 struct s3c2410_spi_info是一个板级结构也是在移植时就定义好的在初始化spi_master时用到platform_device--dev--platform_data会指向这个结构。 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平台上一个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用于控制实际的数据传输 struct spi_bitbang { struct workqueue_struct *workqueue; /*工作队列*/ 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*/ /*用于设置设备传输时的时钟字长等*/ 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 /*针对于平台的传输控制函数*/ 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()函数的实现 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; /*创建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. */ /*片选数和SPI主控制器编号是在platform_data中已经定义好了的*/ master-num_chipselect hw-pdata-num_cs; master-bus_num pdata-bus_num; /* setup the state for the bitbang driver */ /*设置bitbang的所属master和控制传输的相关函数*/ 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; } /*获取中断号*/ 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; } /*注册中断*/ 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;//设定片选函数gpio_direction_output(pdata-pin_cs, 1); } else hw-set_cs pdata-set_cs; s3c24xx_spi_initialsetup(hw); /* register our spi controller */ /* 注册主机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; /*初始化一个struct work,处理函数为bitbang_work*/ INIT_WORK(bitbang-work, bitbang_work); spin_lock_init(bitbang-lock); INIT_LIST_HEAD(bitbang-queue); /*检测bitbang中的函数是否都定义了如果没定义则默认使用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; /*创建bitbang的工作队列*/ 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 */ /*注册spi_master*/ status spi_register_master(bitbang-master); if (status 0) goto err2; return status; err2: destroy_workqueue(bitbang-workqueue); err1: return status; } 下一个关键函数就是spi_register_master(),用于注册spi_master int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id ATOMIC_INIT((115) - 1); struct device *dev master-dev.parent; int status -ENODEV; int dynamic 0; if (!dev) return -ENODEV; /* even if its just one always-selected device, there must * be at least one chipselect */ if (master-num_chipselect 0)//片选数不能为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 cant 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设备 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);//遍历板级信息寻找可以挂接在该spi_master下的从设备 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相等则创建新设备*/ (void) spi_new_device(master, chip); } } mutex_unlock(board_lock); } spi_board_info是板级信息是在移植时就写好的并且要将其注册 struct spi_board_info { char modalias[32]; /*名字*/ const void *platform_data; void *controller_data; int irq; /*中断号*/ u32 max_speed_hz; /*最高传输速率*/ u16 bus_num; /*所属的spi_master编号*/ u16 chip_select; /*片选号*/ u8 mode; /*传输模式*/ }; 最后一步就是将相应的从设备注册进内核 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). */ /*创建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; /*将新设备添加进内核*/ status spi_add_device(proxy); if (status 0) { spi_dev_put(proxy); return NULL; } return proxy; } 本节以spidev设备驱动为例来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动我们注册的从设备都可以使用该驱动只需在注册 时将从设备的modalias字段设置为spidev,这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送 后读取就需要两个字段)每个字段都被封装成一个transfer,N个transfer可以被添加到message中作为一个消息包进行传输。当用 户发出传输数据的请求时message并不会立刻传输到从设备而是由之前定义的transfer()函数将message放入一个等待队列中这些 message会以FIFO的方式有workqueue调度进行传输这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样还是习惯先 画一张图来描述数据传输的主要过程。在使用spidev设备驱动时需要先初始化spidev. spidev是以字符设备的形式注册进内核的。 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作为字符设备注册*/ status register_chrdev(SPIDEV_MAJOR, spi, spidev_fops); if (status 0) return status; /*创建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); } /*注册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; } 与相应的从设备匹配成功后则调用spidev中的probe函数 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;//设定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);//寻找没被占用的次设备号 if (minor N_SPI_MINORS) { struct device *dev; /*计算设备号*/ spidev-devt MKDEV(SPIDEV_MAJOR, minor); /*在spidev_class下创建设备*/ 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的相应位置位表示该位对应的次设备号已被占用 list_add(spidev-device_entry, device_list);//将创建的spidev添加到device_list } mutex_unlock(device_list_lock); if (status 0) spi_set_drvdata(spi, spidev); elsekfree(spidev); return status; } 然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程实际上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); //将用户要发送的数据拷贝到spidev-buffer missing copy_from_user(spidev-buffer, buf, count); if (missing 0) {//全部拷贝成功则调用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 {//设置传输字段 .tx_buf spidev-buffer, .len len, }; struct spi_message m;//创建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 { /* its 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;//发送缓冲区 void *rx_buf;//接收缓冲区 unsigned len; //传输数据的长度 dma_addr_t tx_dma; dma_addr_t rx_dma; unsigned cs_change:1; //该位如果为1则表示当该transfer传输完后改变片选信号 u8 bits_per_word;//字比特数 u16 delay_usecs; //传输后的延时 u32 speed_hz; //指定的时钟 struct list_head transfer_list;//用于将该transfer链入message }; - struct spi_message { struct list_head transfers;//用于链接spi_transfer struct spi_device *spi; //指向目的从设备 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);//用于异步传输完成时调用的回调函数 void *context; //回调函数的参数 unsigned actual_length; //实际传输的长度 int status; /* for optional use by whatever driver currently owns the * spi_message ... between calls to spi_async and then later * complete(), thats the spi_master controller driver. */ struct list_head queue; //用于将该message链入bitbang等待队列 void *state; }; 继续跟踪源码进入spidev_sync(),从这一步开始read和write就完全一样了 spidev_sync(struct spidev_data *spidev, struct spi_message *message) {DECLARE_COMPLETION_ONSTACK(done); int status; message-complete spidev_complete;//设置回调函数 message-context done; spin_lock_irq(spidev-spi_lock); if (spidev-spi NULL) status -ESHUTDOWN; else status spi_async(spidev-spi, message);//调用spi核心层的函数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; /*调用master的transfer函数将message放入等待队列*/ return spi-master-transfer(spi, message); } s3c24xx平台下的transfer函数是在bitbang_start()函数中定义的为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的等待队列 queue_work(bitbang-workqueue, bitbang-work);//调度运行work } spin_unlock_irqrestore(bitbang-lock, flags); return status; } 这里可以看到transfer函数不负责实际的数据传输而是将message添加到 等待队列中。同样在spi_bitbang_start()中有这样一个定义INIT_WORK(bitbang-work, bitbang_work);因此bitbang_work()函数会被调度运行类似于底半部机制 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)) {//等待队列不为空 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 *); /*取出等待队列中的的第一个message*/ m container_of(bitbang-queue.next, struct spi_message, queue); list_del_init(m-queue);//将message从队列中删除 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中的所有传输字段,逐一进行传输*/ 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; } } /*调用setup_transfer根据transfer中的信息进行时钟、字比特数的设定*/ 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) {//使能外设的片选 bitbang-chipselect(spi, BITBANG_CS_ACTIVE); ndelay(nsecs); } cs_change t-cs_change;//这里确定进行了这个字段的传输后是否要改变片选状态 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; /*调用针对于平台的传输函数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 */ /*如果要求在传输完一个字段后进行delay,则进行delay*/ if (t-delay_usecs) udelay(t-delay_usecs); if (!cs_change) continue; /*最后一个字段传输完毕了则跳出循环*/ 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等待队列不为空就表示相应的SPI主控制器上还有 传输任务没有完成因此bitbang_work()会被不断地调度执行。 bitbang_work()中的工作主要是两个循环外循环遍历等待队列中的message,内循环遍历message中的transfer,在 bitbang_work()中传输总是以transfer为单位的。当选定了一个transfer后便会调用transfer_txrx()函数 进行实际的数据传输显然这个函数是针对于平台的SPI控制器而实现的在s3c24xx平台中该函数为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;//获取发送缓冲区 hw-rx t-rx_buf;//获取读取缓存区 hw-len t-len; //获取数据长度 hw-count 0; init_completion(hw-done);//初始化完成量 /* send the first byte */ /*只发送第一个字节其他的在中断中发送(读取)*/ 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不为空也就是说当前是从主机向从机发送数据则直接将tx[count]发送过去 如果tx为空也就是说当前是从从机向主机发送数据则向从机写入0*/ return hw-tx ? hw-tx[count] : 0; } 负责SPI数据传输的中断函数 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; } /*设备忙检测*/ 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)//读取数据到缓冲区 hw-rx[count] readb(hw-regs S3C2410_SPRDAT); count; if (count hw-len)//向从机写入数据 writeb(hw_txbyte(hw, count), hw-regs S3C2410_SPTDAT); else//count len一个字段发送完成唤醒完成量 complete(hw-done); irq_done: return IRQ_HANDLED; } 这里可以看到一点即使tx为空也就是说用户申请的是从从设备读取数据也要不断地向从设备写入数据只不过写入从设备的是无效数据(0)这样做得目的是为了维持SPI总线上的时钟。至此SPI框架已分析完毕。  转载于:https://www.cnblogs.com/tfanalysis/articles/3766212.html
http://www.pierceye.com/news/15682/

相关文章:

  • 竞价恶意点击器phpcms网站seo怎么做
  • 网站的后期运营及维护费用免费发布信息的网站
  • 如何建立的网站能争钱58同城最新消息招聘
  • 四川省建设厅官方网站信息查询三网合一网站建设百科
  • 网站开发的检索速度在啥范围之内免费移动网站建设
  • 房地产怎么做网站推广常平营销网站建设
  • 有了域名和空间怎么建网站潍坊网站推广
  • 徐州教育平台网站建设wordpress不显示中文图片不显示
  • 没有网站如何做落地页采购管理系统的功能有哪些
  • 网站建设手机端官网班级网站中个人简介怎么做
  • 重庆当地网站wordpress 同步 微博
  • 学术网站怎么做焦作网站制作公司
  • 最新网站排名优化方法建设网站赚广告费是否可行
  • 网站开发前端学习西安查派网站建设
  • 做恋视频网站地方旅游网站怎么做
  • 无锡网站制作一般多少钱服饰 企业 网站建设
  • 建网站需要什么服务器wordpress导航菜单的下拉菜单
  • 学网站开发培训学校公司推广网站怎么做
  • 制作网站软件网站市场推广计划方案模板
  • 糖果网站建设目的百度知道合伙人答题兼职入口
  • 购物网站底部设计vi设计英文
  • 深圳华丰大厦网站建设宿迁沭阳网站建设
  • 网页制作站点平台型网站建设预算表
  • pc网站制作是指什么意思制作网页常用图片格式
  • 可信网站代码企业推广图片
  • 珠海网站建设报价南通市建设工程安全监督站网站
  • 南京集团网站建设绿色食品销售网站建设
  • 做网站美工要学什么软件校园网站建设中期报告
  • 医疗网站建设需要什么资质为什么要建立网站
  • 天津做网站找津坤科技专业c2c平台的产品类型