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. MFC绘图小实验(3)

    1,使用默认的文本背景模式,在点(-200,20)处输出黄底红字“Computer Graphics Based on VC++”;在(50,20)处输出黄底红字“BoChuang Research ...

  2. 基于redis集群实现的分布式锁,可用于秒杀,定时器。

    在分布式系统中,经常会出现需要竞争同一资源的情况,使用redis可以实现分布式锁. 前提:redis集群已经整合项目,并且可以直接注入JedisCluster使用: @Autowired privat ...

  3. 腾讯游戏设计(tgideas.qq.com)回复处CSRF任意换马甲

    http://tgideas.qq.com GET /cgi-bin/comments/sendreply.cgi?iActivId=1&iSubActivId=215722&iPar ...

  4. 【ML】人脸识别

    https://github.com/colipso/face_recognition https://medium.com/@ageitgey/machine-learning-is-fun-par ...

  5. 【Postgresql】set up

    https://www.howtoforge.com/tutorial/ubuntu-postgresql-installation/ https://linux.cn/article-6770-1. ...

  6. 18 如何使用go来采集windows的基本硬件信息后发送到CMDB的服务器上

    preface 之前我使用python写了cmdb采集的脚本,打包成exe的二进制文件后放在windows上执行,也达到了预期的效果. 但是最近部门要上open-falcon监控体系,每个服务器都要安 ...

  7. 4 Flask 命令行模式

    preface 在Django中我们可以使用python manage.py shell进入 shell下面调试,在flask下面我们需要安装第三方模块来进入shell模式,安装的模块为Flask-S ...

  8. saltstack系列1之salt-api配置与使用

    salt-api salt-api是我们通过restful-api调用salt-master的接口,且调用的时候必须通过认证才能调用,认证的用户为系统用户,下面就说说如何配置salt-api. 不带S ...

  9. Android开发学习笔记-GridView的动态显示

    1.添加GridItem布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout x ...

  10. IT部门的“2/8”现状

    专家的研究和大量企业实践表明,在IT项目的生命周期中,大约80%的时间与IT项目运 营维护有关,而该阶段的投资仅占整个IT投资的20%,形成了典型的“技术高消费”.“轻服务.重技术”现象.Gartne ...