『壹』 请问linux驱动怎么调用底层的驱动啊 比如说已有SPI总线驱动,现要为一个SPI设备写驱动,怎么调用底层驱动
spi总线驱动在linux中是采用了分层设计和分隔设计的思想,spi控制器的驱动和核专心层的通用属api内核已经写完了,你只要写外设驱动就好,具体你可以去看一下你的spi_s3c24xx.c这个驱动是基于platfoem写的,里面含有如何调用核心api。
『贰』 Linux下的SPI驱动怎么用谁有linux SPI使用的例子比如驱动一个小模块的程序。我想看
你所指的spidev.c是SPI的用户模式设备接口的驱动,可以通过应用程序去操作/dev/spidev*达到与硬件设专备的SPI通信属,对于操作SPI NOR FLASH,更多是注册为MTD设备,详细可参考drivers/mtd/device/m25p80.c,里面已经有相关实现。
但无论如何,前提是你的内核里已经有SPI控制器的驱动,否则如何通信呢。代码一般在drivers/spi/里。
那是应该是给spi设备驱动范本吧,可以籍此写自己的spi设备驱动,没有设备节点就自己创建个嘛。或者在驱动中添加创建设备节点的函数。
『叁』 如何在Zynq 7000平台上使用Linux spidev.c驱动
一、在前来一篇博客中,我们采用自xilinx针对Zynq 7000处理器提供的spi-cadence.c驱动实现了芯片上SPI总线驱动的注册,接下来需要修改设备树文件以时我们的外设挂接在SPI总线下。
在petalinux工程的../subsystems/linux/configs/device-tree目录下找到zynq相关的设备树文件,目录所包含的文件如下图所示。
打开其中的zynq-7000.dtsi文件,找到其中的spi0节点(具体使用spi0还是spi1根据硬件工程的配置情况),并在该节点下添加如下内容:
『肆』 Linux内核自带的SPI驱动怎么用能提供一些资料吗
下载一份内核源代码,比如说我下载的是2.6.36的,解压,里面有一个例专程,位置是linux-2.6.36.4/Documentation/spi/spidev_test.c,另外里面还有属些文档,最好也看看。
『伍』 请教linux的SPI驱动问题
内核版本来2.6.30。编进内核的SPI驱动源,通过看代码我明白了,大致过程是这样:
1、先创建一个spi_board_info结构描述spi设备信息,调用spi_register_board_info将这个结构添加到board_list中。
2、然后调用spi_register_master注册SPI控制器驱动,此时会调用scan_boardinfo扫描board_list,根据spi_board_info调用spi_new_device生成spi_device结构,用spi_add_device添加设备。
3、调用spi_register_driver注册spi_driver,通过与device匹配驱动设备。
『陆』 linux驱动程序结构框架及工作原理分别是什么
一、Linux device driver 的概念x0dx0ax0dx0a系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能:x0dx0ax0dx0a1、对设备初始化和释放;x0dx0ax0dx0a2、把数据从内核传送到硬件和从硬件读取数据;x0dx0ax0dx0a3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;x0dx0ax0dx0a4、检测和处理设备出现的错误。x0dx0ax0dx0a在Linux操作系统下有三类主要的设备文件类型,一是字符设备,二是块设备,三是网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。x0dx0ax0dx0a已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。x0dx0ax0dx0a最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。x0dx0ax0dx0a二、实例剖析x0dx0ax0dx0a我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理。把下面的C代码输入机器,你就会获得一个真正的设备驱动程序。x0dx0ax0dx0a由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close?, 注意,不是fopen, fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:x0dx0ax0dx0aSTruct file_operatiONs {x0dx0ax0dx0aint (*seek) (struct inode * ,struct file *, off_t ,int);x0dx0ax0dx0aint (*read) (struct inode * ,struct file *, char ,int);x0dx0ax0dx0aint (*write) (struct inode * ,struct file *, off_t ,int);x0dx0ax0dx0aint (*readdir) (struct inode * ,struct file *, struct dirent * ,int);x0dx0ax0dx0aint (*select) (struct inode * ,struct file *, int ,select_table *);x0dx0ax0dx0aint (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);x0dx0ax0dx0aint (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);x0dx0ax0dx0aint (*open) (struct inode * ,struct file *);x0dx0ax0dx0aint (*release) (struct inode * ,struct file *);x0dx0ax0dx0aint (*fsync) (struct inode * ,struct file *);x0dx0ax0dx0aint (*fasync) (struct inode * ,struct file *,int);x0dx0ax0dx0aint (*check_media_change) (struct inode * ,struct file *);x0dx0ax0dx0aint (*revalidate) (dev_t dev);x0dx0ax0dx0a}x0dx0ax0dx0a这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。x0dx0ax0dx0a下面就开始写子程序。x0dx0ax0dx0a#include
『柒』 linux驱动调用spi标准函数spi_sync发送速率慢的问题
spi_sync() /*会调用下面的复wait_for_completion*/
wait_for_completion() /*这里会耗费很多制时间*/
我也碰到一样的问题,暂时没解决。如果你的数据量不大的话可以spi_sync() 一次传入32bit或更多数据。如果数据量太大就没办法彻底解决了。如果你解决了也帮忙共享一下方法
『捌』 14-Linux gpio模拟spi
首先是spidev,要在/dev/下面产生设备文件,需要spidev的支持
使用的是gpio模拟spi,gpio模拟spi的时序原理是bitbang文件实现的,所以这个也需要打开,如果是在openwrt下动态加载的话就是如下两个配置
如果是直接内核的话是如下两个
跟I2C的arch层一样,主要是devices的添加和board_info的添加,如下
对于platform_add_devices,因为是使用spi_gpio,所以name是"spi_gpio"这样才可以与driver里面的spi_gpio相互匹配probe到。
因为SPI是可以一个总线上面挂多个,然后通过片选脚CS进行硬件切换,所以这变有个num_chipselect需要设置,如果有2个设置就设置2,一个设备就设置1,这边设置好之后,后面board_info也要有对应的个数,而且片选引脚需要不同。
I2C是通过每个设备有自己不同的地址,通过地址来进行软件切换。
对于board_info使用的是spidev,drivers/spi/spidev.c文件,该文件的内容是注册一个spidev驱动。该驱动是一个字符设备驱动。
如果设备与驱动匹配,那么就会执行spidev_probe()的内容。在spidev_probe()函数中会调用device_create()成功后在 /dev 目录下就会生成 spidev 相关的设备节点。
这边有几个参数要注意:
调试过程想看一些细节的debug信息可以打开内核的动态debug信息,这个在以前的print system里面有
printk的等级设置成8.
开始
定位到是 spi_gpio_request 的时候报错
后面就将zkernel/3.10.49/arch/mips/mtk/ziroom/zrmt7628.c里面GPIO的信息调整下, 因为SPI的引脚和LED的引脚号一样 ,内核不知道哪里会检测到。
修改后打印如下:
之后在/dev/下面就生成了spidev1.0的设备
有了/dev/spidev1.0设备之后,就可以在应用成操作改设备收发数据。
在drivers/spi/spidev.c里面已经封装好了ioctl的对应接口,根据这些接口就可以测试使用。
在Documentation/spi/spidev_test.c下面有个应用层的实例,打开看下就清除了。
$(cc) spidev_test.c -o spidev_test生成可执行文件spidev_test
然后拷贝到板子上,将MOSI和MISO短接就可以测试回环数据是否正常。
有逻辑分析仪的接上logic看波形就更加直观。
gpio模拟SPI:
https://blog.csdn.net/luckywang1103/article/details/70145870
在ARM Linux下使用GPIO模拟SPI时序详解:
https://blog.csdn.net/yangzheng_yz/article/details/50470577
linux SPI驱动:
https://www.cnblogs.com/xuyh/category/903809.html
『玖』 linux spi设备驱动中probe函数何时被调用
这两天被设备文件快搞疯了,也怪自己学东西一知半解吧,弄了几天总算能把设备注册理清楚一点点了。就以spi子设备的注册为例总结一下,免得自己忘记。
首先以注册一个spidev的设备为例:
static struct spi_board_info imx5_spi_printer_device[] __initdata =
{
{
.modalias = "spidev",
.max_speed_hz = 8000000,
.bus_num = 1,
.chip_select = 1,
.mode = SPI_MODE_0,
},
};
spi_register_board_info(imx5_spi_printer_device,ARRAY_SIZE(imx5_spi_printer_device));
在mx5_loco.c文件中添加上面结构体spi_board_info,modalias必须指定已有的一个驱动,至于bus_num和chip_select,如果你不知道bus_num是多少,可以在你的父驱动中打印出来,这里的bus_num一定要和父类的bus_num一致,否则是无法生成设备文件的。如果spi一直没有时钟信号,很有可能是bus_num不对。
这样系统起来之后就会在/dev目录下出现一个名为spidev1.1的设备文件,读写这个文件就可以实现spi的操作
还有下面这种情况:
static struct spi_board_info prt_spi_device[] __initdata = {
{
.modalias = "HotPRT",
.max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
.bus_num = 1,
.chip_select = 1,
// .mode = SPI_MODE_0,
.platform_data = 0,
},
};
spi_register_board_info(prt_spi_device, ARRAY_SIZE(prt_spi_device));
我自己实现了一个spi的驱动,然后需要创建一个设备文件,设备文件的创建是在probe中完成。
static struct spi_driver prt_driver = {
.driver = {
.name = "HotPRT",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = prt_probe,
.remove = __devexit_p(prt_remove),
};
spi_register_driver(&prt_driver);
但是我开始一直触发不了probe,于是找啊找,总算知道probe的调用过程了,如下:
int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}
然后调用driver_register
<pre name="code" class="cpp">int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
直接看bus_add_driver
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
mole_add_driver(drv->owner, drv);
这里只截取一部分,最后调用的是driver_attach
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
真正起作用的是__driver_attach:
static int __driver_attach(struct device * dev, void * data)
{
。。。
if (!dev->driver)
driver_probe_device(drv, dev);
。。。
}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
。。。
//1.先是判断bus是否match:
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
//2.再具体执行probe:
ret = really_probe(dev, drv);
。。。
}
really_probe才是我们要找的函数:
static int really_probe(struct device *dev, struct device_driver *drv)
{
。。。
//1.先是调用的驱动所属总线的probe函数:
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
//2.再调用你的驱动中的probe函数:
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
。。。
}
其中,drv->probe(dev),才是真正调用你的驱动实现的具体的probe函数。至此probe函数被调用。
在板文件中添加spi_board_info,并在板文件
『拾』 求SPI的SSD1306在linux下的测试程序
求SPI的SSD1306在linux下的测试程序理解SPI的驱动框架,还是从最基本的三个入口点触发,platform_device,platform_bus,platform_driver。
其中内核一提供给platform_bus,platform_driver在spi_s3c24xx_gpio.c和spi_s3c24xxc.c中,其中spi_s3c24xx_gpio.c用于IO模拟SPI (本例讨论的是IO模拟SPI),spi_s3c24xxc.c用于s3c24xx的硬件SPI。因此,我们需要动手写一个platform_device。
看看spi_s3c24xx_gpio.c做了些什么。
static int s3c2410_spigpio_probe(struct platform_device *dev)
{
... ...
/* [cgw]: 分配一个SPI主机 */
master = spi_alloc_master(&dev->dev, sizeof(struct s3c2410_spigpio));
... ...
sp = spi_master_get_devdata(master);
platform_set_drvdata(dev, sp);
/* [cgw]: 分配与spi硬件相关的配置,如指定哪些IO为MISO,MOSI,SCLK,CS,SPI工作模式,最大时钟等等 */
/* in the plkatform data */
sp->info = dev->dev.platform_data;
/* [cgw]: 提供实现SPI各种模式的时序的基本方法,和CS的激活方法 */
/* setup spi bitbang adaptor */
sp->bitbang.master = spi_master_get(master);
sp->bitbang.chipselect =