572至574行,分配内存,注意对象的类型是struct spidev_data,看下它在drivers/spi/spidev.c中的定义:

 struct spidev_data {
dev_t devt;
spinlock_t spi_lock;
struct spi_device *spi;
struct list_head device_entry; /* buffer is NULL unless this device is open (users > 0) */
struct mutex buf_lock;
unsigned users;
u8 *buffer;
};

76行,设备号。79行,设备链表,所有采用此驱动的设备将连成一个链表。83行,计数,也即是此设备被open的次数。

回到spidev_probe函数,577至586行,一些锁和链表的初始化。588行,从名字上就可以知道,就是找到第一个为0的位,第一个参数minors的定义:

 #define N_SPI_MINORS            32    /* ... up to 256 */

 static DECLARE_BITMAP(minors, N_SPI_MINORS);

DECLARE_BITMAP是一个宏,定义如下:

#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]

将宏展开后是这样的,unsigned long minors[1],其实就是定义一个只有一个元素的无符号长整形数组miniors。

590至593行,如果找到了非0位,就将它作为次设备号与之前注册的主设备号生成设备号。

595至598行,创建设备,并生成设备节点,设备节点在/dev目录下,名字的形式为“spidevx.x”。

603至608行,创建设备成功后,将相应的位置1,表示该次设备号已经被使用,同时将该设备加入到设备链表。

611至614行,将设备的私有数据指针指向该设备。

至此,SPI设备驱动的初始化过程也说完了。下面就以应用程序的操作顺序来说,假设是从open-->write这个过程。下面先看驱动中open函数的实现,同样在drivers/spi/spidev.c:

 static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO; mutex_lock(&device_list_lock); list_for_each_entry(spidev, &device_list, device_entry) {
if (spidev->devt == inode->i_rdev) {
status = ;
break;
}
}
if (status == ) {
if (!spidev->buffer) {
spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spidev->buffer) {
dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
status = -ENOMEM;
}
}
if (status == ) {
spidev->users++;
filp->private_data = spidev;
nonseekable_open(inode, filp);
}
} else
pr_debug("spidev: nothing for minor %d\n", iminor(inode)); mutex_unlock(&device_list_lock);
return status;
}

485至490行,遍历设备链表,每找到一个设备就将它的设备号与打开文件的设备号进行比较,相等的话表示查找成功。

491至505行,查找成功后就分配读写数据内存,使用计数加1,设置文件私有数据指针指向查找到的设备,以后在驱动的write、read函数里就可以把它取出来。

接下来是write函数的定义:

 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 = ;
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);
missing = copy_from_user(spidev->buffer, buf, count);
if (missing == ) {
status = spidev_sync_write(spidev, count);
} else
status = -EFAULT;
mutex_unlock(&spidev->buf_lock); return status;
}

199至200行,应用程序写入的数据不能大于驱动中缓冲区的大小,默认为4096个字节。

202行,指向文件的私有数据。

205行,拷贝用户空间的数据到内核空间。

207行,spidev_sync_write的定义:

 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; spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spidev_sync(spidev, &m);
}

133行,struct spi_transfer的定义在include/linux/spi/spi.h:

 struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
00000429 * for MicroWire, one buffer must be null
00000430 * buffers must work with dma_*map_single() calls, unless
00000431 * spi_message.is_dma_mapped reports a pre-existing mapping
00000432 */
const void *tx_buf;
void *rx_buf;
unsigned len; dma_addr_t tx_dma;
dma_addr_t rx_dma; unsigned cs_change:;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz; struct list_head transfer_list;
};

433至435行,发送、接收缓冲区和长度。437和438行,发送和接收的DMA地址。

440行,传输完成后是否改变片选信号。

441行,如果为0则使用驱动的默认值。

442行,传输完成后等待多长时间(毫秒)再改变片选信号。

443行,将多个传输连成一个链表。

回到spidev_sync_write函数的137行,在spi.h中定义的struct spi_message:

 struct spi_message {
struct list_head transfers; struct spi_device *spi; unsigned is_dma_mapped:; /* REVISIT: we might want a flag affecting the behavior of the
00000484 * last transfer ... allowing things like "read 16 bit length L"
00000485 * immediately followed by "read L bytes". Basically imposing
00000486 * a specific message scheduling algorithm.
00000487 *
00000488 * Some controller drivers (message-at-a-time queue processing)
00000489 * could provide that as their default scheduling algorithm. But
00000490 * others (with multi-message pipelines) could need a flag to
00000491 * tell them about such special cases.
00000492 */ /* 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
00000501 * spi_message ... between calls to spi_async and then later
00000502 * complete(), that's the spi_master controller driver.
00000503 */
struct list_head queue;
void *state;
};

477行,一个message可能包含多个transfer,因此用链表将这些transfer连起来。

479行,这次message所使用的spi设备。

481行,是否采用DMA的标志。

495行,传输完成后的回调函数指针。496行,回调函数的参数。

497行,这次message成功传输的字节数。

504和505行,当前驱动拥有的message。

回到spidev_sync_write函数,139行,spi.h中的内联函数spi_message_init:

 static inline void spi_message_init(struct spi_message *m)
{
memset(m, , sizeof *m);
INIT_LIST_HEAD(&m->transfers);
}

很简单,清0内存和初始化message的transfer链表。

140行,spi_message_add_tail也是spi.h中的内联函数:

 static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
list_add_tail(&t->transfer_list, &m->transfers);
}

将transfer加入到链表尾。

141行,spidev_sync函数是在drivers/spi/spidev.c中定义的:

 static ssize_t
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);
spin_unlock_irq(&spidev->spi_lock); if (status == ) {
wait_for_completion(&done);
status = message->status;
if (status == )
status = message->actual_length;
}
return status;
}

108行,定义并初始化一个完成量,完成量是Linux的一种同步机制。

111行,spidev_complete函数里就用来唤醒等待completion,定义如下:

 static void spidev_complete(void *arg)
{
complete(arg);
}

112行,作为spidev_complete函数的参数。

118行,调用drivers/spi/spi.c里的spi_async函数,从函数名知道,这是异步实现的。为什么是异步的?往下看就知道了。

 int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
int ret;
unsigned long flags; spin_lock_irqsave(&master->bus_lock_spinlock, flags); if (master->bus_lock_flag)
ret = -EBUSY;
else
ret = __spi_async(spi, message); spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); return ret;
}

745行,如果master所在的总线被锁住了,那么就返回忙。

748行,看__spi_async函数的定义:

 static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master; /* Half-duplex links include original MicroWire, and ones with
00000684 * only one data pin like SPI_3WIRE (switches direction) or where
00000685 * either MOSI or MISO is missing. They can also be caused by
00000686 * software limitations.
00000687 */
if ((master->flags & SPI_MASTER_HALF_DUPLEX)
|| (spi->mode & SPI_3WIRE)) {
struct spi_transfer *xfer;
unsigned flags = master->flags; list_for_each_entry(xfer, &message->transfers, transfer_list) {
if (xfer->rx_buf && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)
return -EINVAL;
if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)
return -EINVAL;
}
} message->spi = spi;
message->status = -EINPROGRESS;
return master->transfer(spi, message);
}

688至701行,如果master设置了SPI_MASTER_HALF_DUPLEX标志,或者spi设备使用的是3线模式,那么就对message里的每一个transfer的发送和接收buf做一些检查。

705行,调用的是具体的SPI控制器驱动里的函数,这里是drivers/spi/spi_s3c64xx.c里的s3c64xx_spi_transfer函数:

 static int s3c64xx_spi_transfer(struct spi_device *spi,
struct spi_message *msg)
{
struct s3c64xx_spi_driver_data *sdd;
unsigned long flags; sdd = spi_master_get_devdata(spi->master); spin_lock_irqsave(&sdd->lock, flags); if (sdd->state & SUSPND) {
spin_unlock_irqrestore(&sdd->lock, flags);
return -ESHUTDOWN;
} msg->status = -EINPROGRESS;
msg->actual_length = ; list_add_tail(&msg->queue, &sdd->queue); queue_work(sdd->workqueue, &sdd->work); spin_unlock_irqrestore(&sdd->lock, flags); return ;
}

Linux设备驱动剖析之SPI(三)的更多相关文章

  1. Linux设备驱动剖析之SPI(一)

    写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...

  2. Linux设备驱动剖析之SPI(二)

    957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...

  3. Linux设备驱动剖析之SPI(四)

    781行之前没什么好说的,直接看783行,将work投入到工作队列里,然后就返回,在这里就可以回答之前为什么是异步的问题.以后在某个合适的时间里CPU会执行这个work指定的函数,这里是s3c64xx ...

  4. linux设备驱动归纳总结(三):7.异步通知fasync【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxx ...

  5. linux设备驱动归纳总结(三):6.poll和sellct【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxx ...

  6. linux设备驱动归纳总结(三):5.阻塞型IO实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-60025.html linux设备驱动归纳总结(三):5.阻塞型IO实现 xxxxxxxxxxxxxx ...

  7. linux设备驱动归纳总结(三):4.ioctl的实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59419.html linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽 ...

  8. linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】

    本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一. ...

  9. linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59417.html linux设备驱动归纳总结(三):2.字符型设备的操作open.close.rea ...

随机推荐

  1. oc总结 --oc基础语法相关知识

    m是OC源文件扩展名,入口点也是main函数,第一个OC程序: #import <Foundation/Foundation.h> int main(int argc, const cha ...

  2. MongoDB 3.X JAVA基本操作

    对Collection的操作 import java.util.ArrayList; import java.util.Iterator; import java.util.List; import ...

  3. vimdiff的常用命令

    ★ 跳转到下一个diff点: 请使用 ]c 命令★ 跳转到前一个diff点: 请使用 [c命令如果在命令前加上数字的话,可以跳过一个或数个差异点,从而实现跳的更远.比如如果在位于第一个差异点的行输入& ...

  4. 50.EasyGank妹纸App

    50.EasyGank妹纸App 前言 基于MVP模式下 , RxJava+RxAndroid+Retrofit的组合 . RxJava RxAndroid Retrofit Okhttp Gson ...

  5. 简单理解AOP(面向切面编程)

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP与OOP是面向不同领域的两种设计思想. ...

  6. fork 至 “sys_clone" SyS_clone

    注:glibc-2.17中fork的相应系统调用是sys_clone及SyS_clone.有人说调用的是sys_fork,但是我持否定意见,如果我们向真的来发起系统调用可以使用syscall. for ...

  7. 装饰模式(Decorator Pattern)--------结构型模式

    装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为.装饰模式降低类系统的耦合度,可以动态地增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件 ...

  8. kafka学习之-server.properties详细说明

    http://blog.csdn.net/lizhitao/article/details/25667831  -- 参考文章 http://kafka.apache.org/documentatio ...

  9. greenplum日常维护手册

    1.       数据库启动:gpstart 常用可选参数: -a : 直接启动,不提示终端用户输入确认 -m:只启动master 实例,主要在故障处理时使用 2.       数据库停止:gpsto ...

  10. Python游戏《外星人入侵》来了~

    在游戏<外星人入侵>中,玩家控制着一艘最初出现在屏幕底部中央的飞船.玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击.游戏开始时,一群外星人出现在天空中,他们在屏幕中向下移动.玩家的 ...