Linux设备驱动剖析之SPI(三)
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(三)的更多相关文章
- Linux设备驱动剖析之SPI(一)
写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...
- Linux设备驱动剖析之SPI(二)
957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...
- Linux设备驱动剖析之SPI(四)
781行之前没什么好说的,直接看783行,将work投入到工作队列里,然后就返回,在这里就可以回答之前为什么是异步的问题.以后在某个合适的时间里CPU会执行这个work指定的函数,这里是s3c64xx ...
- linux设备驱动归纳总结(三):7.异步通知fasync【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxx ...
- linux设备驱动归纳总结(三):6.poll和sellct【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxx ...
- linux设备驱动归纳总结(三):5.阻塞型IO实现【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-60025.html linux设备驱动归纳总结(三):5.阻塞型IO实现 xxxxxxxxxxxxxx ...
- linux设备驱动归纳总结(三):4.ioctl的实现【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59419.html linux设备驱动归纳总结(三):4.ioctl的实现 一.ioctl的简介: 虽 ...
- linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】
本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一. ...
- linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59417.html linux设备驱动归纳总结(三):2.字符型设备的操作open.close.rea ...
随机推荐
- Linux中./configure、make、make install详解
./configure && make && make install详解 2010-08-03 23:30:05 标签:休闲 ./configure &&a ...
- ASP.NET后台输出js脚本代码
利用asp.net输出js我们大多数都会直接使用Respone.Write()然后根js格式的代码,再在页面调用时我们直接这样是完全可以实现的,下面我来给大家介绍另一种方法 我是我最初的想法以下是代码 ...
- HTTP之referer
安全培训中提到可以通过referer判断安全性,hackbar中也有一个enable referer的选项,则,这个referer到底是个什么角色? (以下是搜集的一些资料整合,链接均放到底部,不再一 ...
- fopen flock fclose 文件用法
fopen函数是用来打开文件或者连接 若成功,则返回 true.若失败,则返回 false. fopen打开连接是不能直接输出的 使用: <?php $file = fopen("te ...
- GCT之数学公式(几何部分)
一.平面图形 二.空间几何体
- UNIX环境编程学习笔记(13)——文件I/O之标准I/O流
lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于 ...
- linux cfs调度器
在抽象模型中vruntime决定了进程被调度的先后顺序,在真实模型中决定被调度的先后顺序的参数是由函数entity_key决定的. static inline s64 entity_key(str ...
- VCL 中的 Windows API 函数(2): ActivateKeyboardLayout
ActivateKeyboardLayout 分别在 Controls.DBGrids.Grids 单元用到, 基本都是如下语句: ActivateKeyboardLayout(Screen.Defa ...
- WebJars are client-side web libraries (e.g. jQuery & Bootstrap) packaged into JAR (Java Archive) files
webjars网站https://www.webjars.org/,这里将很多的东西都打包成了jar包,想要用什么只需要导入相关的依赖就可以了. 比如springboot会用到jquery,webja ...
- BareTail大文件日志实时查看工具
BareTail 动态的查看日志文件,就像Linux上的tail tail -f nohup.out 功能: 实时文件查看 tail命令模式,自动滚动 支持2g以上大文件 自动滚动 彩色监控 多文件监 ...