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 ...
随机推荐
- MQ遇到的错误(2035 或 2013认证错误)
java 连接 IBM MQ时出现 2035 或 2013认证错误的解决 com.ibm.msg.client.jms.DetailedJMSSecurityException: JMSWMQ2013 ...
- 关于Struts2的文件上传
要实现Struts2框架的文件上传,需要用到2个jar包 commons-fileupload-1.2.2.jar commons-io-2.0.1.jar 由于文件解析Struts2内部已经帮我们做 ...
- npm安装包卡住不动的解决
最近诸事不顺,今天更新/安装nodejs各种包也全都卡在各个环节,用ie设了全局代理貌似也没什么改观,于是到网上找找有没有国内镜像站,倒是发现了cnpmjs.org这个网站被推荐比较多,看他们主页,他 ...
- tomcat启动时设定环境变量
在tomcat的bin目录中修改startup.bat 设置CATALINA_HOME set "CATALINA_HOME=F:\solr\apache-tomcat\apache-tom ...
- wordpress主题升级之后返回到原来版本主题的方法
wordpress后台经常可以看到主题提示升级,但是发现升级之后样式,颜色等都变了,不是以前的样子了,这时候如果想要返回到以前版本,前提,必须以前版本有备份. 在wordpress里面找到主题===添 ...
- 9 云计算系列之Cinder的安装与NFS作为cinder后端存储
preface 在前面我们知道了如何搭建Openstack的keystone,glance,nova,neutron,horizon这几个服务,然而在这几个服务中唯独缺少存储服务,那么下面我们就学习块 ...
- Spring3+mybatis3在多数据源情况下找不到数据库驱动的问题
解决问题的过程如下: 1.遇到问题和一般的解决方法和下面这个帖子的一样: http://www.oschina.net/question/188964_32305 2.我在按照1的做法配置了以后,依然 ...
- C#获取当前时区转换方法
今天给Mongodb插入数据的时候发现,日期时间对不上,原来Mongodb(麻狗)默认是0时区,所以日期当然出错. 所以用下面进行转换就可以: log.AddTime = DateTime.Speci ...
- java okhhtp下载学信网学籍信息
学信网的登录有验证码,是那种计算数字或者汉字识别的,很难识别.最近连学籍信息和学历信息也换成图片了,常规的正则 css xpath都不能使. 下载图片,需要先登录,获取登陆后的cookie和学籍信息的 ...
- Mac OS 电信3G上网设置
打开客户端后(安装客户端mobile partner需要先安装jdk),在“系统偏好设置”里选择“网络”,网络左侧添加“huaweimobile-modem”,“电话号码”填写电信卡号,“账户名称”和 ...