linux驱动基础代码
linux驱动开发(一)
linux驱动开发(⼀)1:驱动开发环境要进⾏linux驱动开发我们⾸先要有linux内核的源码树,并且这个linux内核的源码树要和开发板中的内核源码树要⼀直;⽐如说我们开发板中⽤的是linux kernel内核版本为2.6.35.7,在我们ubuntu虚拟机上必须要有同样版本的源码树,我们再编译好驱动的的时候,使⽤modinfo XXX命令会打印出⼀个版本号,这个版本号是与使⽤的源码树版本有关,如果开发板中源码树中版本与modinfo的版本信息不⼀致使⽆法安装驱动的;我们开发板必须设置好nfs挂载;这些在根⽂件系统⼀章有详细的介绍;2:开发驱动常⽤的⼏个命令lsmod :list moduel 把我们机器上所有的驱动打印出来,insmod:安装驱动rmmod:删除驱动modinfo:打印驱动信息3:写linux驱动⽂件和裸机程序有很⼤的不同,虽然都是操作硬件设备,但是由于写裸机程序的时候是我们直接写代码操作硬件设备,这只有⼀个层次;⽽我们写驱动程序⾸先要让linux内核通过⼀定的接⼝对接,并且要在linux内核注册,应⽤程序还要通过内核跟应⽤程序的接⼝相关api来对接;4:驱动的编译模式是固定的,以后编译驱动的就是就按照这个模式来套即可,下⾯我们来分下⼀下驱动的编译规则:#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build# 开发板的linux内核的源码树⽬录KERN_DIR = /root/driver/kernelobj-m += module_test.oall:make -C $(KERN_DIR) M=`pwd` modulescp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: cleanclean:make -C $(KERN_DIR) M=`pwd` modules cleanmake -C $(KERN_DIR) M=`PWD` modules这句话代码的作⽤就是到 KERN_DIR这个⽂件夹中 make modules把当前⽬录赋值给M,M作为参数传到主⽬录的Makefile中,实际上是主⽬录的makefile中有⽬标modules,下⾯有⼀定的规则来编译驱动;#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build我们在ubuntu中编译内核的时候⽤这两句代码,因为在ubuntu中为我们保留了⼀份linux内核的源码树,我们编译的时候直接调⽤那个源码树的主Makefile以及⼀些头⽂件、内核函数等;了解规则以后,我们设置好KERN_DIR、obj-m这两个变量以后直接make就可以了;经过编译会得到下⾯⼀些⽂件:下⾯我们可以使⽤lsmod命令来看⼀下我们ubuntu机器现有的⼀些驱动可以看到有很多的驱动,下⾯我们使⽤insmod XXX命令来安装驱动,在使⽤lsmod命令看⼀下实验现象可以看到我们刚才安装的驱动放在了第⼀个位置;使⽤modinfo来打印⼀下驱动信息modinfo xxx.ko这⾥注意vermagic 这个的1.8.0-41是你⽤的linux内核源码树的版本号,只有这个编译的版本号与运⾏的linux内核版本⼀致的时候,驱动程序才会被安装注意license:GPL linux内核开元项⽬的许可证⼀般都是GPL这⾥尽量设置为GPL,否则有些情况下会出现错误;下⾯使⽤rmmod xxx删除驱动;-------------------------------------------------------------------------------------5:下⾯我们分析⼀下驱动。
linux显卡驱动安装
linux显卡驱动安装Linux是一个自由和开放源代码的操作系统,它的目标是提供一个可靠、稳定、安全的操作平台。
在Linux系统中,显卡驱动的安装非常重要,因为显卡驱动决定了系统图形性能的好坏。
在Linux系统中,显卡驱动的安装一般有两种方式:通过软件包管理器安装和手动安装。
通过软件包管理器安装显卡驱动是最常见、最简单的方式。
不同的Linux发行版使用不同的软件包管理器,如Ubuntu使用apt,Fedora使用dnf等。
用户只需要打开终端,输入相应的命令就可以自动下载和安装显卡驱动。
例如,在Ubuntu中,用户可以使用以下命令安装NVIDIA显卡驱动:sudo apt-get install nvidia-驱动版本号手动安装显卡驱动则需要用户自己去官网下载驱动程序,并按照安装说明进行安装。
手动安装驱动相对较为复杂,需要用户有一定的技术基础。
首先,用户需要确定自己所使用的显卡型号,并到显卡厂商官方网站上找到对应的Linux驱动程序。
然后,用户需要下载驱动程序,并将其解压到一个合适的目录中。
接下来,用户需要打开终端,进入到驱动程序所在的目录,然后运行安装脚本。
安装脚本会自动检测系统配置,并安装相应的驱动程序。
最后,用户需要重新启动电脑,使驱动程序生效。
无论是通过软件包管理器安装还是手动安装,安装完显卡驱动后,用户可以通过系统设置中的“显示”来查看和配置显卡驱动。
在这里,用户可以调整分辨率、刷新率、亮度等参数,以获得更好的图形性能和显示效果。
总的来说,Linux系统中的显卡驱动安装相对于Windows系统来说稍微复杂一些,但只要按照正确的方法和步骤进行操作,就能轻松完成安装,并获得良好的图形性能。
所以,对于使用Linux系统的用户来说,学会如何安装显卡驱动是非常有必要的。
linux设备驱动之8250串口驱动
linux设备驱动之8250串口驱动一:前言前一段时间自己实践了一下8250芯片串口驱动的编写。
今天就在此基础上分析一下linux kernel自带的串口驱动。
毕竟只有对比专业的驱动代码才能更好的进步,同以往一样,基于linix kernel2.6.25.相应驱动代码位于:linux-2.6.25/drivers/serial/8250.c。
二:8250串口驱动初始化相应的初始化函数为serial8250_init().代码如下:static int __init serial8250_init(void){int ret, i;if (nr_uarts > UART_NR)nr_uarts = UART_NR;printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ ""%d ports, IRQ sharing %sabled\n", nr_uarts,share_irqs ? "en" : "dis");for (i = 0; i < NR_IRQS; i++)spin_lock_init(&irq_lists[i].lock);ret = uart_register_driver(&serial8250_reg);if (ret)goto out;serial8250_isa_devs = platform_device_alloc("serial8250",PLA T8250_DEV_LEGACY);if (!serial8250_isa_devs) {ret = -ENOMEM;goto unreg_uart_drv;}ret = platform_device_add(serial8250_isa_devs);if (ret)goto put_dev;serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);ret = platform_driver_register(&serial8250_isa_driver);if (ret == 0)goto out;platform_device_del(serial8250_isa_devs);put_dev:platform_device_put(serial8250_isa_devs);unreg_uart_drv:uart_unregister_driver(&serial8250_reg);out:return ret;}这段代码涉及到的知识要求,如platform ,uart等我们在之前都已经做过详细的分析。
Linux下的ds18b20驱动
for(j=90;j>0;j--);
}
void msdelay(unsigned int i) //延时i ms
{
for(i=i;i>0;i--)
usdelay(1000);
}
void SetL(void)
{
s3c2410_gpio_cfgpin(DS_PIN,OUT);
int i;
for(i=0;i>=1;
}
}
unsignedint ds_read(void) //从18b20读一个字节的数据
{
unsigned int uidata=0;unsigned int i;
for(i=0;i>=1;
SetL();
udelay(1);
s3c2410_gpio_setpin(DS_PIN,1);
mdelay(50);
ds_start();
ds_send(Skip_ROM); //跳过读序号列号的操作
ds_send(Read); //准备读温度
tl=ds_read();
th=ds_read();
th0)
{
printk(copy data failed\n);
return -1;
}
//else
// printk(copy data succese\n);
flag=0;
SetH();//初始化成功后赋为高电平准备从外界读入温度
udelay(400);
return flag;
}
void ds_send(unsigned int uidata) //向18b20写入一个字节的数据
Linux视频设备驱动编程(v4l2编程)
Linux视频设备驱动编程(v4l2编程)一.什么是video4linuxVideo4linux2(简称V4L2),是linux中关于视频设备的内核驱动。
在Linux 中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
二、一般操作流程(视频设备):1. 打开设备文件。
int fd=open(”/dev/video0″,O_RDWR);2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。
VIDIOC_QUERYCAP,struct v4l2_capability3. 选择视频输入,一个视频设备可以有多个视频输入。
VIDIOC_S_INPUT,struct v4l2_input4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format5. 向驱动申请帧缓冲,一般不超过5个。
struct v4l2_requestbuffers6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。
mmap7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer8. 开始视频的采集。
VIDIOC_STREAMON9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。
VIDIOC_DQBUF10. 将缓冲重新入队列尾,这样可以循环采集。
VIDIOC_QBUF11. 停止视频的采集。
VIDIOC_STREAMOFF12. 关闭视频设备。
close(fd);三、常用的结构体(参见/usr/include/linux/videodev2.h):struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数struct v4l2_capability cap;//这个设备的功能,比如是否是视频输入设备struct v4l2_input input; //视频输入struct v4l2_standard std;//视频的制式,比如PAL,NTSCstruct v4l2_format fmt;//帧的格式,比如宽度,高度等struct v4l2_buffer buf;//代表驱动中的一帧v4l2_std_id stdid;//视频制式,例如:V4L2_STD_PAL_Bstruct v4l2_queryctrl query;//查询的控制struct v4l2_control control;//具体控制的值下面具体说明开发流程(网上找的啦,也在学习么)打开视频设备在V4L2中,视频设备被看做一个文件。
LinuxI2C驱动--用户态驱动简单示例
LinuxI2C驱动--⽤户态驱动简单⽰例1. Linux内核⽀持I2C通⽤设备驱动(⽤户态驱动:由应⽤层实现对硬件的控制可以称之为⽤户态驱动),实现⽂件位于drivers/i2c/i2c-dev.c,设备⽂件为/dev/i2c-02. I2C通⽤设备驱动以字符设备注册进内核的static const struct file_operations i2cdev_fops = {.owner = THIS_MODULE,.llseek = no_llseek,.read = i2cdev_read,.write = i2cdev_write,.unlocked_ioctl = i2cdev_ioctl,.open = i2cdev_open,.release = i2cdev_release,};res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);3. 对设备⽂件进⾏读写时,可以调⽤read、write或者ioctl等⽅法,他们都是通过调⽤函数i2c_transfer来实现对I2C设备的操作的int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num){int ret;/* REVISIT the fault reporting model here is weak:** - When we get an error after receiving N bytes from a slave,* there is no way to report "N".** - When we get a NAK after transmitting N bytes to a slave,* there is no way to report "N" ... or to let the master* continue executing the rest of this combined message, if* that's the appropriate response.** - When for example "num" is two and we successfully complete* the first message but get an error part way through the* second, it's unclear whether that should be reported as* one (discarding status on the second message) or errno* (discarding status on the first one).*/if (adap->algo->master_xfer) {#ifdef DEBUGfor (ret = 0; ret < num; ret++) {dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, ""len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)'R' : 'W', msgs[ret].addr, msgs[ret].len,(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");}#endifif (in_atomic() || irqs_disabled()) {ret = mutex_trylock(&adap->bus_lock);if (!ret)/* I2C activity is ongoing. */return -EAGAIN;} else {mutex_lock_nested(&adap->bus_lock, adap->level);}ret = adap->algo->master_xfer(adap,msgs,num);mutex_unlock(&adap->bus_lock);return ret;} else {dev_dbg(&adap->dev, "I2C level transfers not supported\n");return -EOPNOTSUPP;}}4. i2c_transfer通过代码可以看出,i2c_transfer 通过调⽤相应的 adapter 的 master_xfer ⽅法实现的,⽽ master_xfer 主要是根据 struct i2c_msg 类型的msgs来进⾏处理的。
Linux0.01内核源代码及注释
Bootsect.s(1-9)!! SYS_SIZE is the number of clicks (16 bytes) to be loaded.! 0x3000 is 0x30000 bytes = 196kB, more than enough for current! versions of linux ! SYS_SIZE 是要加载的节数(16 字节为1 节)。
0x3000 共为1 2 3 4 5 60x7c000x00000x900000x100000xA0000system 模块代码执行位置线路0x90200! 0x30000 字节=192 kB(上面Linus 估算错了),对于当前的版本空间已足够了。
!SYSSIZE = 0x3000 ! 指编译连接后system 模块的大小。
参见列表1.2 中第92 的说明。
! 这里给出了一个最大默认值。
!! bootsect.s (C) 1991 Linus Torvalds!! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves! iself out of the way to address 0x90000, and jumps there.!! It then loads 'setup' directly after itself (0x90200), and the system! at 0x10000, using BIOS interrupts.!! NOTE! currently system is at most 8*65536 bytes long. This should be no! problem, even in the future. I want to keep it simple. This 512 kB! kernel size should be enough, especially as this doesn't contain the! buffer cache as in minix!! The loader has been made as simple as possible, and continuos! read errors will result in a unbreakable loop. Reboot by hand. It! loads pretty fast by getting whole sectors at a time whenever possible.!! 以下是前面这些文字的翻译:! bootsect.s (C) 1991 Linus Torvalds 版权所有!! bootsect.s 被bios-启动子程序加载至0x7c00 (31k)处,并将自己! 移到了地址0x90000 (576k)处,并跳转至那里。
linux 蓝牙驱动代码分析
net/hci_core.cHCI 在主机端的驱动主要是为上层提供一个统一的接口,让上层协议不依赖于具体硬件的实现。
HCI在硬件中的固件与HCI在主机端的驱动通信方式有多种,比如像 UART、USB和PC Card等等。
hci_core.c相当于一个框架,用于把各种具体通信方式胶合起来,并提供一些公共函数的实现。
hci_cmd_task是负责发送CMD的任务,它从hdev->cmd_q队列中取CMD,然后调用hci_send_frame把CMD发送出去,hci_send_frame又会调用实际的HCI驱动的send函数发送数据。
hci_rx_task是负责接收数据的任务,它从hdev->rx_q队列中取数据,然后根据数据的类型调用上层函数处理。
数据包有三种类型:1. HCI_EVENT_PKT:用于处理一些通信事件,比如连接建立,连接断开,认证和加密等事件,这些事件控制协议状态的改变。
2. HCI_ACLDATA_PKT:异步非连接的数据包,通过hci_acldata_packet提交给上层的L2CAP协议处理(hci_proto[HCI_PROTO_L2CAP])。
3. HCI_SCODATA_PKT:同步面向连接的数据包,通过hci_scodata_packet提供给上层的SCO协议处理(hci_proto[HCI_PROTO_SCO])。
hci_tx_task是负责发送数据的任务,发送所有connection中的ACL和SCO数据,以及hdev->raw_q中的数据包。
HCI为上层提供的接口主要有:1. hci_send_sco:发送SCO数据包,把要发送的数据包放入connection的发送队列中,然后调度发送任务去发送。
2. hci_send_acl:发送ACL数据包,把要发送的数据包放入connection的发送队列中,然后调度发送任务去发送。
3. hci_send_cmd:发送命令数据,把要发送的数据包放入hdev->cmd_q队列中,然后调度命令发送任务去发送。
Linux设备驱动开发入门-Read
Linux设备驱动开发入门本文以快捷而简单的方式讲解如何像一个内核开发者那样开发linux设备驱动源作者: Xavier Calbet版权:GNU Free Documentation License 翻译: 顾宏军()中文版权:创作共用.署名-非商业用途-保持一致知识准备要开发Linux 设备驱动,需要掌握以下知识:•C 编程 需要掌握深入一些的C 语言知识,比如,指针的使用,位处理函数,等。
•微处理器编程 需要理解微机的内部工作原理:存贮器地址,中断,等。
这些内容对一个汇编程序员应该比较熟悉。
Linux 下有好几种不同的设备。
为简单起见,本文只涉及以模块形式加载的字符设备。
使用2.6.x 的内核。
(特别是Debian Sarge 使用的2.6.8内核。
)用户空间和内核空间当你开发设备驱动时,需要理解“用户空间”和内核空间之间的区别。
1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20:21:22:23:24:25:•内核空间 :Linux 操作系统,特别是它的内核,用一种简单而有效的方法管理机器的硬件,给用户提供一个简捷而统一的编程接口。
同样的,内核,特别是它的设备驱动程序,是连接最终用户/程序员和硬件的一坐桥或者说是接口。
任何子程序或者函数只要是内核的一部分(例如:模块,和设备驱动),那它也就是内核空间的一部分。
•用户空间. 最终用户的应用程序,像UNIX 的shell 或者其它的GUI 的程序(例如,gedit),都是用户空间的一部分。
很显然,这些应用程序需要和系统的硬件进行交互。
但是,他们不是直接进行,而是通过内核支持的函数进行。
它们的关系可以通过下图表示:图1: 应用程序驻留在用户空间, 模块和设备驱动驻留在内核空间26:27:28:29:30:31:32:33:34:35:36:37:38:39:40:用户空间和内核空间之间的接口函数内核在用户空间提供了很多子程序或者函数,它们允许用户应用程序员和硬件进行交互。
Linux I2C设备驱动编写
Linux I2C设备驱动编写(一)在Linux驱动中I2C系统中主要包含以下几个成员:如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。
如果适配器支持SMBUS 协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。
master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。
functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。
在内核的drivers/i2c/i2c-stub.c中实现了一个i2c adapter的例子,其中实现的是更为复杂的SMBUS。
SMBus 与I2C的区别通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。
时钟速度对比:在电气特性上他们也有所不同,SMBus要求的电压范围更低。
I2C driver具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C 协议与主机进行数据传输、控制。
结构体如下:如同普通设备的驱动能够驱动多个设备一样,一个I2C driver也可以对应多个I2C client。
以重力传感器AXLL34X为例,其实现的I2C驱动为:这里要说明一下module_i2c_driver宏定义(i2c.h):module_driver():理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:这一句宏就解决了模块module安装卸载的复杂代码。
这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。
I2C client即I2C设备。
I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义:下面还是以adxl34x为例:这样ADXL34X的i2c设备就被注册到了系统中,当名字与i2c_driver中的id_table中的成员匹配时就能够出发probe匹配函数了。
Linux ——Driver
第一章驱动程序基本框架星期二, 06/08/2010 - 00:21— william前言不管是Windows还是Linux,驱动程序都扮演着重要的角色。
应用程序只能通过驱动程序才能同硬件设备或系统内核通讯。
Linux内核对不同的系统定义了标准的接口(API),应用程序就是通过这些标准的接口来操作内核和硬件。
驱动可以被编译的内核中(build-in),也可以做为内核模块(Module)存在于内核的外面,需要的时候动态插入到内核中运行。
就像你学习操作系统概念时所了解的那样,Linux内核也分为几个大的部分:进程管理、内存管理、文件系统、设备控制、网络系统等,参考图1-1。
图1-1 Linux系统(来源:O‟Reilly Media, LDD3)这里就不对Linux系统内核的各个部分做过多的介绍了,在后面的学习中你就会逐渐地对这些概念有个更深入的了解。
其实Linux内核的精髓远不止这些,对于一个Linux内核的爱好者或开发者来说,最好详细的浏览内核源代码,订阅Linux内核相关的邮件列表,或是登陆Linux开发社区。
更多的信息,请登陆Linux内核官方网站:一个简单的驱动下面我们来编写第一个驱动程序,它很简单,在运行时会输出…Hello World‟消息。
// hello.c#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>static int __init hello_init(void){printk(KERN_ALERT "Hello World!\n");return 0;}static void __exit hello_exit(void){printk(KERN_ALERT "Goodbye World!\n");}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");这就是一个简单的驱动程序,它什么也没做,仅仅是输出一些信息,不过对于我们来说这已经足够了。
2-Linux驱动和内核模块编程
设备驱动的Hello World模块 设备驱动的 模块
模块卸载函数
static void __exit cleanup_function(void) { /* 释放资源 */ } module_exit(cleanup_function);
在模块被移除前注销接口并 释放所有所占用的系统资源
标识这个代码是只用于模块卸载( 标识这个代码是只用于模块卸载 通过使编译器把它放在 特殊的 ELF 段) 原型: 原型:#define __exit __attribute__ ((__section__(“.exit.text”)))
查看已加载模块
lsmod cat /proc/modules.
卸载驱动模块 卸载模块
从内核中卸载模块可以用rmmod工具.
注意,如果内核认为该模块任然在使用状态, 注意,如果内核认为该模块任然在使用状态,或 者内核被禁止移除该模块,则无法移除该模块。 者内核被禁止移除该模块,则无法移除该模块。
内核打印函数
隐藏硬件细节,提高应用软件的可移植性 提供安全性 开发模式 内核态驱动 用户态驱动
提供机制,而不是提供策略
机制:驱动程序能实现什么功能 策略:用户如何使用这些功能
设备的分类和特点Biblioteka 设备分类字符设备(char device) 字符设备 块设备(block device) 块设备 网络设备(network device) 网络设备
MODULE_LICENSE()---模块许可证声明 模块许可证声明
模块许可证(LICENSE)声明描述内核模块的许可权限 如果不声明LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告
动手写一个内核模块
linux驱动编程初级+makefile
驱动编程1 模块的概述 (2)2 source insight 加载内核源码方法 (2)3 模块makefile的编写 (3)4 模块makefile编写方法 (4)5 在X86上运行模块: (5)6 编写模块 (5)7 模块的加载进内核命令 (5)8 最简单的上层调用+ 调用驱动方法 (6)9 复杂框架上层应用+驱动调用方法 (7)10 复杂框架字符设备创建并注册过程 (7)11 file_operations常用函数 (9)12 同步互斥操作 (10)13 同步互斥函数总结 (10)14 阻塞IO编程流程 (11)15 轮询操作上层select 下层poll (12)16 信号处理 (12)17 中断 (13)18 中断新模型--上半部中断和下半部中断的实现 (14)19 内核定时器编程 (15)20 内核延时函数 (15)21 内核源代码中头文件分配方式 (15)22 linux内核管理和内核的内存管理 (16)23 设备io端口和io内存访问–如何控制led的亮灭 (16)24 * 驱动-设备分离思想编程————内核进阶 (18)25 驱动-设备分离-核心最小架构 (18)26 驱动设备分离思想- 上层架构(基于封装) (20)27 头文件总结 (24)28 设置系统自启动命令u-boot (24)第一天需要理清的东西1)模块的概念,模块与应用的区别2)模块主要的组成头文件、module_init() modoule_exit() module_lisence()3)模块的如何编辑,如何编译,如何加载到内核中运行使用makefile4)模块驱动编写,必须通过上层应用程序调用。
1模块的概述模块是内核的一部分,为了防止内核太大,把它放在文件系统里面。
也可以在编译内核的直接编译进内核。
1,存储位置可以在开始时编译进内核,也可以编译进模块,最后加载2、运行时环境在哪个内核树下编译,就对应这个运行环境3、模块的编译问题:前提条件是需要对应的内核源码树,或者必须有对应的内核版本匹配4、模块编译使用makefile 注意makefile的编写2source insight 加载内核源码方法在windows下创建工程,使用source insight查看内核代码:2.1 先将内核源码拷到对应的文件夹2.2 在source insight 里添加工程,筛选需要添加的文件注意选择按照树来添加,然后按照remove来踢出不需要的文件夹2.3 最后同步3模块makefile的编写模块的编译:1)、模块编译的核心语句:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules-C :进入内核源码树M= : 返回到当前目录,再次执行该目录下的makefileeg: /tnt/abc.c -----> abc.ko1、在/tnt目录下敲make,只有/tnt目录下的Makefile被调用2、目的是要进入到内核源码树中,一进一回,-C来进,M=带着内核源码树中的makefile的功能回来了-------内核源码树中Makefile的目标:obj-y:汇集了所有编译进内核的目标文件obj-m:汇集了所有编译成模块的目标文件3、回来过后,我们只有确定obj-m变量的集合4、make modules告诉内核的makefile,只做编译模块的功能4模块makefile编写方法ifeq ($(KERNELRELEASE),)KERNELDIR := /work/linux-2.6.35-farsightPWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesinstall:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean:rm -rf .tmp_versions *.ko *.o .*.cmd *.mod.c *.order *.symvers.PHONY: modules cleanelseobj-m := ex1.oendif以上是makefile的内容,●注意原来的内核目录树不要进行make clean 或者make distclean●KERNELDIR 表示模块加载在哪个内核的文件夹(又叫内核源码树),●$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 表示进入该内核文件夹,将顶层makefile 中的内容带回,再重新执行一次该makefile 将obj-m := ex1.o 编译,并执行make modules (并只编译ex1.c ,不编译其它模块)●$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 表示执行顶层makefile的modules install 标签下的命令●安装的位置原来默认在/lib 下面,所以需要修改其到我们制作的根文件系统下/work/rootfs/在顶层Makefile位置搜索:MODLIB修改为:●obj-m := ex1.o 你需要编译的.c的文件名****************************此时简单的编译环境已经搭建完毕******************* ****************************执行make ***********************************************执行make install *******************在/work/rootfs/lib/modules/2.6.35/extra即可找到该模块.ko*****************************************************************************搭建好环境,保证虚拟机与板子与计算机网络连通,并设置板子u-boot 从nfs挂载,启动内核,并成功通过nfs 加载rootfs,此时环境完毕,进入/work/rootfs/lib/modules/2.6.35/extra ,找到模块,加载卸装模块操纵5在X86上运行模块:修改Makefile中的内核源码树的目录X86下的内核源码树:/lib/modules/2.6.35-22-generic/build如果没有在控制台上交互,默认是看不到信息的,需要dmesg这个命令去查看6编写模块模块最小组成如下:●注意:module_init module_exit 必须放在末尾●注意:函数的原型返回值●头文件7模块的加载进内核命令insmodrmmodlsmod8最简单的上层调用+ 调用驱动方法8.1 首先在module_init(abc) abc函数中注册设备register_chrdev(注册设备号,上层可见的设备名,操作封装)该函数完成设备注册,在板子上用cat /proc/devices 便可以看见该设备8.2 完成fops 操作的封装●注意格式●必须在函数后面声明该结构体●头文件#include <linux/fs.h>8.3 查看到该字符设备后,创建设备节点,则上层通过设备字符名与该设备号绑定mknod /dev/hf_char c 245 0ls /dev/ 可以查看注册的所有设备节点8.4 此时上层应用的open(”hf_char”,O_RDWR),即可完成该设备的打开,即可以完成上层应用于下层驱动相关fops 的操作。
Linux驱动之LCD驱动编写
Linux驱动之LCD驱动编写在这篇博客中已经分析了编写LCD驱动的步骤,接下来就按照这个步骤来字尝试字节编写LCD驱动。
⽤的LCD屏幕为tft 屏,每个像素点为16bit。
对应与红绿蓝分别为565。
1、分配⼀个fb_info结构2、设置fb_info结构3、硬件相关的操作,配置LCD时钟、配置IO端⼝、配置LCD寄存器。
4、最终注册fbinfo结构到registered_fb数组要理解LCD的⼯作原理,需要了解LCD的时钟,在TFT的LCD中有如下的时钟。
这个⼏个时钟数据在配置LCD寄存器时都说需要设置的。
1、VCLK:两个像素之间的时钟,即两个像素隔多长时间才能显⽰下⼀个像素2、HSYNC:⽔平同步时钟,即第⼀⾏像素点显⽰完成之后隔多长时间才能开始下⼀⾏的显⽰3、VSYNC:垂直⽅向的同步时钟,也叫帧同步信号,即⼀帧数据显⽰完成之后(⼀帧数据表⽰⼀个屏幕显⽰完成,即⼀个显存的数据全部取完),过多长下⼀帧数据才开始显⽰本节需要⽤到的函数:void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配DMA缓存区给显存//返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表⽰分配失败,则需要使⽤dma_free_writecombine()释放内存,避免内存泄漏//参数如下://*dev:指针,这⾥填0,表⽰这个申请的缓冲区⾥没有内容//size:分配的地址⼤⼩(字节单位)//*handle:申请到的物理起始地址//gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常⽤标志如下://GFP_ATOMIC ⽤来从中断处理和进程上下⽂之外的其他代码中分配内存. 从不睡眠.//GFP_KERNEL 内核内存的正常分配. 可能睡眠.//GFP_USER ⽤来为⽤户空间页来分配内存; 它可能睡眠.分配⼀段DMA缓存区,分配出来的内存会禁⽌cache缓存(因为DMA传输不需要CPU)它和 dma_alloc_coherent ()函数相似,不过 dma_alloc_coherent ()函数是分配出来的内存会禁⽌cache缓存以及禁⽌写⼊缓冲区dma_free_writecombine(dev,size,cpu_addr,handle); //释放缓存//cpu_addr:虚拟地址,//handle:物理地址释放DMA缓冲区, dev和size参数和上⾯的⼀样struct fb_info *framebuffer_alloc(size_t size, struct device *dev); //申请⼀个fb_info结构体,//size:额外的内存,//*dev:指针, 这⾥填0,表⽰这个申请的结构体⾥没有内容int register_framebuffer(struct fb_info *fb_info);//向内核中注册fb_info结构体,若内存不够,注册失败会返回负数int unregister_framebuffer(struct fb_info *fb_info) ;//注销内核中fb_info结构体本节需要⽤到的结构体:fb_info结构体如下:struct fb_info {... ...struct fb_var_screeninfo var; //可变的参数struct fb_fix_screeninfo fix; //固定的参数... ...struct fb_ops *fbops; //操作函数... ...char __iomem *screen_base; //显存虚拟起始地址unsigned long screen_size; //显存虚拟地址长度void *pseudo_palette;//假的16⾊调⾊板,⾥⾯存放了16⾊的数据,可以通过8bpp数据来找到调⾊板⾥⾯的16⾊颜⾊索引值,模拟出16⾊颜⾊来,节省内存,不需要的话就指向⼀个不⽤的数组即可 ... ...};其中操作函数fb_info-> fbops 结构体写法如下:static struct fb_ops s3c_lcdfb_ops = {.owner = THIS_MODULE,.fb_setcolreg = my_lcdfb_setcolreg,//设置调⾊板fb_info-> pseudo_palette,⾃⼰构造该函数.fb_fillrect = cfb_fillrect, //填充矩形,⽤/drivers/video/ cfbfillrect.c⾥的函数即可.fb_copyarea = cfb_copyarea, //复制数据, ⽤/drivers/video/cfbcopyarea.c⾥的函数即可.fb_imageblit = cfb_imageblit, //绘画图形, ⽤/drivers/video/imageblit.c⾥的函数即可};固定的参数fb_info-> fix 结构体如下:struct fb_fix_screeninfo {char id[16]; //id名字unsigned long smem_start; //framebuffer物理起始地址__u32 smem_len; //framebuffer长度,字节为单位__u32 type; //lcd类型,默认值0即可__u32 type_aux; //附加类型,为0__u32 visual; //画⾯设置,常⽤参数如下// FB_VISUAL_MONO01 0 单⾊,0:⽩⾊,1:⿊⾊// FB_VISUAL_MONO10 1 单⾊,1:⽩⾊,0:⿊⾊// FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩)// FB_VISUAL_PSEUDOCOLOR 3 伪彩// FB_VISUAL_DIRECTCOLOR 4 直彩 __u16 xpanstep; /*如果没有硬件panning就赋值为0 */ __u16 ypanstep; /*如果没有硬件panning就赋值为0 */ __u16 ywrapstep; /*如果没有硬件ywrap就赋值为0 */ __u32 line_length; /*⼀⾏的字节数 ,例:(RGB565)240*320,那么这⾥就等于240*16/8 */ /*以下成员都可以不需要*/ unsigned long mmio_start; /*内存映射IO的起始地址,⽤于应⽤层直接访问寄存器,可以不需要*/__u32 mmio_len; /* 内存映射IO的长度,可以不需要*/__u32 accel;__u16 reserved[3];};可变的参数fb_info-> var 结构体如下:structfb_var_screeninfo{ __u32xres; /*可见屏幕⼀⾏有多少个像素点*/__u32 yres; /*可见屏幕⼀列有多少个像素点*/__u32 xres_virtual; /*虚拟屏幕⼀⾏有多少个像素点 */__u32 yres_virtual; /*虚拟屏幕⼀列有多少个像素点*/__u32 xoffset; /*虚拟到可见屏幕之间的⾏偏移,若可见和虚拟的分辨率⼀样,就直接设为0*/ __u32 yoffset; /*虚拟到可见屏幕之间的列偏移*/__u32 bits_per_pixel; /*每个像素的位数即BPP,⽐如:RGB565则填⼊16*/__u32 grayscale; /*⾮0时,指的是灰度,真彩直接填0即可*/struct fb_bitfield red; //fb缓存的R位域, fb_bitfield结构体成员如下://__u32 offset; 区域偏移值,⽐如RGB565中的R,就在第11位//__u32 length; 区域长度,⽐如RGB565的R,共有5位//__u32 msb_right; msb_right ==0,表⽰数据左边最⼤, msb_right!=0,表⽰数据右边最⼤struct fb_bitfield green; /*fb缓存的G位域*/struct fb_bitfield blue; /*fb缓存的B位域*/ /*以下参数都可以不填,默认为0*/struct fb_bitfield transp; /*透明度,不需要填0即可*/__u32nonstd; /* != 0表⽰⾮标准像素格式*/__u32 activate; /*设为0即可*/__u32height; /*外设⾼度(单位mm),⼀般不需要填*/__u32width; /*外设宽度(单位mm),⼀般不需要填*/__u32 accel_flags; /*过时的参数,不需要填*//* 除了pixclock本⾝外,其他的都以像素时钟为单位*/__u32pixclock; /*像素时钟(⽪秒)*/__u32 left_margin; /*⾏切换,从同步到绘图之间的延迟*/__u32right_margin; /*⾏切换,从绘图到同步之间的延迟*/__u32upper_margin; /*帧切换,从同步到绘图之间的延迟*/__u32lower_margin; /*帧切换,从绘图到同步之间的延迟*/__u32hsync_len; /*⽔平同步的长度*/__u32 vsync_len; /*垂直同步的长度*/__u32 sync;__u32 vmode;__u32 rotate;__u32reserved[5]; /*保留*/}1.写驱动程序:(驱动设置:参考⾃带的LCD平台驱动drivers/video/s3c2410fb.c )1.1 步骤如下:在驱动init⼊⼝函数中:1)分配⼀个fb_info结构体2)设置fb_info 2.1)设置固定的参数fb_info-> fix 2.2) 设置可变的参数fb_info-> var 2.3) 设置操作函数fb_info-> fbops 2.4) 设置fb_info 其它的成员3)设置硬件相关的操作 3.1)配置LCD引脚 3.2)根据LCD⼿册设置LCD控制器 3.3)分配显存(framebuffer),把地址告诉LCD控制器和fb_info4)开启LCD,并注册fb_info: register_framebuffer() 4.1) 直接在init函数中开启LCD(后⾯讲到电源管理,再来优化) 控制LCDCON5允许PWREN信号, 然后控制LCDCON1输出PWREN信号, 输出GPB0⾼电平来开背光, 4.2) 注册fb_info在驱动exit出⼝函数中:1)卸载内核中的fb_info2) 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址3)释放DMA缓存地址dma_free_writecombine()4)释放注册的fb_info1.2 具体代码如下:#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/io.h> //含有iomap函数iounmap函数#include <asm/uaccess.h>//含有copy_from_user函数#include <linux/device.h>//含有类相关的处理函数#include <linux/fb.h> //含有fb_info结构体定义//#include <asm/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/platform_device.h>//含有平台设备总线模型相关变量#include <linux/mm.h>#include <linux/slab.h>//#include <linux/module.h>//#include <linux/kernel.h>//#include <linux/errno.h>//#include <linux/string.h>//#include <linux/mm.h>//#include <linux/slab.h>//#include <linux/delay.h>//#include <linux/fb.h>//#include <linux/init.h>//#include <linux/dma-mapping.h>//#include <linux/interrupt.h>//#include <linux/workqueue.h>//#include <linux/wait.h>//#include <linux/platform_device.h>//#include <linux/clk.h>//#include <asm/io.h>//#include <asm/uaccess.h>//#include <asm/div64.h>//#include <asm/mach/map.h>//#include <asm/arch/regs-lcd.h>//#include <asm/arch/regs-gpio.h>//#include <asm/arch/fb.h>/*lcd控制寄存器放在⼀个结构体⾥⾯*/struct lcd_regs {unsigned long lcdcon1;unsigned long lcdcon2;unsigned long lcdcon3;unsigned long lcdcon4;unsigned long lcdcon5;unsigned long lcdsaddr1;unsigned long lcdsaddr2;unsigned long lcdsaddr3;unsigned long redlut;unsigned long greenlut;unsigned long bluelut;unsigned long reserved[9];unsigned long dithmode;unsigned long tpal;unsigned long lcdintpnd;unsigned long lcdsrcpnd;unsigned long lcdintmsk;unsigned long lpcsel;};static struct fb_info *s3c_mylcdfb_info;//fb_info结构体static volatile unsigned long *gpbcon;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpbdat;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpccon;static volatile unsigned long *gpdcon;static volatile unsigned long *gpgcon;//GPG4⽤于lcd电源static volatile struct lcd_regs* lcd_regs;//lcd寄存器static u32 pseudo_palette[16]; //调⾊板内存/* from pxafb.c */static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){chan &= 0xffff; //取出16bit的数据chan >>= 16 - bf->length; //return chan << bf->offset;}static int s3c_mylcdfb_setcolreg(unsigned int regno, unsigned int red,unsigned int green, unsigned int blue,unsigned int transp, struct fb_info *info){unsigned int val;if (regno > 16)return1;/* ⽤red,green,blue三原⾊构造出val */val = chan_to_field(red, &info->var.red);val |= chan_to_field(green, &info->var.green);val |= chan_to_field(blue, &info->var.blue);//((u32 *)(info->pseudo_palette))[regno] = val;pseudo_palette[regno] = val;return0;}static struct fb_ops s3c_mylcdfb_ops = { //操作函数结构体.owner = THIS_MODULE,.fb_setcolreg = s3c_mylcdfb_setcolreg,//待会设置,这个是调⾊板,如果使⽤⼩于16bit的像素需要⽤到 .fb_fillrect = cfb_fillrect,.fb_copyarea = cfb_copyarea,.fb_imageblit = cfb_imageblit,};static int lcd_drv_init(void){/*1、分配⼀个fb_info*/s3c_mylcdfb_info = framebuffer_alloc(0,NULL);//size为额外分配的⼤⼩,这⾥不需要,所以设为0if(s3c_mylcdfb_info==NULL){printk("unframebuffer_alloc\n");return1;}/*2、设置*//*2.1 设置固定的参数*/strcpy(s3c_mylcdfb_info->fix.id, "mylcd");//名字//s3c_mylcdfb_info->fix.smem_start = ;//显存的物理起始地址,后⾯设置s3c_mylcdfb_info->fix.smem_len = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩ s3c_mylcdfb_info->fix.type = FB_TYPE_PACKED_PIXELS;//LCD类型,填充像素的类型 tft//s3c_mylcdfb_info->fix.type_aux= //附加的LCD类型,不需要设置s3c_mylcdfb_info->fix.visual = FB_VISUAL_TRUECOLOR;//视觉类型,选择真彩⾊s3c_mylcdfb_info->fix.line_length = 480*16/8; //⼀⾏的长度,单位为字节// s3c_mylcdfb_info->fix.mmio_start = //控制lcd的寄存器的物理地址// s3c_mylcdfb_info->fix.mmio_len = //控制lcd的寄存器的⼤⼩/*2.2 设置可变的参数*/s3c_mylcdfb_info->var.xres = 480;//x⽅向的分辨率s3c_mylcdfb_info->var.yres = 272;//y⽅向的分辨率s3c_mylcdfb_info->var.xres_virtual = 480;//x⽅向的虚拟分辨率s3c_mylcdfb_info->var.yres_virtual = 272;//y⽅向的虚拟分辨率s3c_mylcdfb_info->var.bits_per_pixel = 16;//每个像素的⼤⼩,单位为bits3c_mylcdfb_info->var.grayscale = 0;//灰度值s3c_mylcdfb_info->var.red.length = 5;//红⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.green.length = 6;//绿⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.blue.length = 5;//蓝⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.red.offset= 11;//红⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.green.offset= 6;//绿⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.blue.offset=0;//蓝⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.red.msb_right= 0;//低位在前还是⾼位在前,⼀般⾼位在前,也就是⼩端模式s3c_mylcdfb_info->var.green.msb_right= 0;s3c_mylcdfb_info->var.blue.msb_right=0;s3c_mylcdfb_info->var.activate = FB_ACTIVATE_NOW;//使⽤默认参数,显存⽴刻⽣效/*2.3 设置操作函数*/s3c_mylcdfb_info->fbops = &s3c_mylcdfb_ops;/*2.4 其它的⼀些设置 */s3c_mylcdfb_info->pseudo_palette = pseudo_palette;//调⾊板的地址//s3c_mylcdfb_info->screen_base = ;//显存的虚拟基地址s3c_mylcdfb_info->screen_size = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩/*3、硬件相关的操作 *//*3.1、配置GPIO⽤于LCD*/gpbcon = ioremap(0x56000010, 8);//将实际的寄存器地址转换为虚拟地址gpccon = ioremap(0x56000020 , 4);gpdcon = ioremap(0x56000030 , 4);gpgcon = ioremap(0x56000060 , 4);gpbdat = gpbcon + 1;*gpccon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */*gpdcon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[23:8] */*gpbcon &= ~(3); /* GPB0设置为输出引脚 */*gpbcon |= 1;*gpbdat &= ~1; /* 输出低电平关闭LCD背光 */*gpgcon |= (3<<8); /* GPG4⽤作LCD_PWREN 电源*//*3.2、根据LCD⼿册设置LCD控制器,⽐如VCLK的频率等 */lcd_regs = ioremap(0X4D000000 , sizeof(struct lcd_regs));/** bit[17:8] : VCLK = HCLK / [(CLKVAL+1) x 2]* 10M = 100M/[(CLKVAL+1) x 2]* CLKVAL = 4** bit[6:5] :PNRMODE = 11显⽰模式,选择TFT模式** bit[4:1] :BPPMODE = 1100;像素=16bit 565** bit[0] :ENVID = 0;先关闭LCD控制器*/lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);///** [31:24] : VBPD = 帧同步信号发出后,过多长时间开始显⽰数据,单位为⾏,理解为1⾏的时间* 看LCD⼿册tvb = VBPD + 1 = 2;所以VBPD = 1** [23:14]:LINEVAL + 1= 272;,所以LINEVAL = 271;垂直⽅向尺⼨,多少⾏** [13:6]:VFPD = ⼀帧的数据传输完成之后,过多长时间开始下⼀帧数据的帧同步信号,单位为⾏,理解为1⾏的时间 * 看LCD⼿册tvf = VFPD + 1 = 2;所以VFPD = 1** [5:0]:VSPW = 帧同步信号的脉冲宽度,单位为⾏* 看LCD⼿册tvp = VSPW + 1 =10;所以VSPW = 9*/lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9<<0);/** [25:19]:HBPD = ⾏同步信号发出后,经过多少个VCLK,才发送像素的数据,单位为VCLK* 看LCD⼿册thb = HBPD + 1 = 2;所以HBPD=1** [18:8]:HOZVAL + 1 = 480,所以 HOZVAL = 479;⽔平⽅向尺⼨,多少列**[7:0]:HFPD = ⼀⾏的像素数据传输完成之后,经过多长时间,才能发送下⼀个⾏同步信号,单位为VCLK*看LCD⼿册thf = HFPD + 1 = 2;所以HFPD = 1;*/lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);/** [7:0]:HSPW = ⾏同步信号的脉冲宽度,单位为VCLK* 看LCD⼿册thp = HSPW + 1 = 41;所以HSPW = 40**/lcd_regs->lcdcon4 = (40<<0);/** [11] :FRM565 = 1;16位模式的格式 R:G:B = 5:6:5* [10] :INVVCLK = 0;VCLK在哪个边沿取数据 = 0表⽰下降沿取数据* [9] :INVVLINE = 1;⾏同步信号是否需要反转= 1需要反转* [8] :INVVFRAME = 1;帧同步信号是否需要反转= 1需要反转* [7] :INVVD = 0; 数据是否需要反转* [6] :INVVDEN = 0; 数据使能信号是否需要反转* [5] :INVPWREN = 0;电源使能信号是否需要反转* [4] :INVLEND = 0;⾏结束信号是否需要反转* [3] :PWREN = 0;电源使能信号,先不使能* [2] :ENLEND = 1;//⾏结束信号先使能* [1:0] :BSWP 、HWSWP = 0 1;字节内部不需要交换,字节间需要交换*/lcd_regs->lcdcon5= (1<<11) | (3<<8) | (1<<2) | (1<<0);/*3.3、显存和调⾊板设置 *//**利⽤dma_alloc_writecombine分配⼀块连续的显存*/s3c_mylcdfb_info->screen_base = dma_alloc_writecombine(NULL,s3c_mylcdfb_info->screen_size,(&(s3c_mylcdfb_info->fix.smem_start)),GFP_KERNEL);//返回虚拟地址if(s3c_mylcdfb_info->screen_base==NULL) //如果显存分配失败,直接返回{printk("undma_alloc_writecombine\n");return1;}/**将显存的地址告诉LCD控制器(物理地址)*/lcd_regs->lcdsaddr1 = (s3c_mylcdfb_info->fix.smem_start >> 1) & (~(3<<30));//起始地址lcd_regs->lcdsaddr2 = ((s3c_mylcdfb_info->fix.smem_start + s3c_mylcdfb_info->screen_size) >> 1) & 0x1fffff;//结束地址lcd_regs->lcdsaddr3 = (480*16/16); /* ⼀⾏的长度(单位: 2字节) *///s3c_lcd->fix.smem_start = xxx; /* 显存的物理地址 *//* 启动LCD */lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本⾝电源 */*gpbdat |= 1; /* 输出⾼电平, 使能背光 *//*4、注册LCD*/register_framebuffer(s3c_mylcdfb_info);printk("register_framebuffer\n");return0;}static void lcd_drv_exit(void){unregister_framebuffer(s3c_mylcdfb_info);lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本⾝ */*gpbdat &= ~1; /* 关闭背光 */dma_free_writecombine(NULL, s3c_mylcdfb_info->fix.smem_len, s3c_mylcdfb_info->screen_base, s3c_mylcdfb_info->fix.smem_start);iounmap(lcd_regs);iounmap(gpbcon);iounmap(gpccon);iounmap(gpdcon);iounmap(gpgcon);framebuffer_release(s3c_mylcdfb_info);}module_init(lcd_drv_init);module_exit(lcd_drv_exit);MODULE_LICENSE("GPL");2.重新编译内核,去掉默认的LCDmake menuconfig ,进⼊menu菜单重新设置内核参数:进⼊Device Drivers-> Graphics support:<M> S3C2410 LCD framebuffer support //将⾃带的LCD驱动设为模块, 不编进内核中然后make uImage 编译内核make modules 编译模块为什么要编译模块?因为LCD驱动相关的⽂件也没有编进内核,⽽fb_ops⾥的成员fb_fillrect(), fb_copyarea(), fb_imageblit()⽤的都是drivers/video下⾯的3个⽂件,所以需要这3个的.ko模块,如下图所⽰:3.挂载驱动将编译好的LCD驱动模块和drivers/video⾥的3个.ko模块放⼊nfs⽂件系统⽬录中然后烧写内核, 先装载3个/drivers/video下编译好的模块,再来装载LCD驱动模块挂载LCD驱动后, 如下图,可以通过 ls -l /dev/fb* 命令查看已挂载的LCD设备节点:4.测试运⾏测试有两种:echo hello> /dev/tty1 // LCD上便显⽰hello字段cat Makefile>/dev/tty1 // LCD上便显⽰Makeflie⽂件的内容4.1使⽤上节的键盘驱动在LCD终端打印命令⾏vi /etc/inittab //修改inittab, inittab:配置⽂件,⽤于启动init进程时,读取inittab添加->tty1::askfirst:-/bin/sh //将sh进程(命令⾏)输出到tty1⾥,也就是使LCD输出信息然后重启,insmod装载3个/drivers/video下编译好的模块,再来insmod装载LCD驱动模块,tty1设备便有了,就能看到提⽰信息:如下图,我们insmod上⼀节的键盘驱动后,按下enter键,便能在LCD终端上操作linux了从上图可以看到按下enter键,它就启动了⼀个进程号772的-sh进程,如下图发现这个-sh的描述符都指向了tty1:以上内容转载⾃。
ch341a在linux系统中的驱动编写
在Linux系统中编写ch341a驱动,首先需要了解ch341a的硬件接口和通信协议。
然后,可以使用Linux内核提供的设备驱动框架进行开发。
以下是一个简单的示例:1. 首先,创建一个名为`ch341a.c`的文件,用于编写驱动代码:```c#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/init.h>#include <linux/platform_device.h>#include <linux/gpio.h>#include <linux/interrupt.h>#include <linux/delay.h>#define CH341A_GPIO_PIN 4 // 根据实际情况修改引脚号static int ch341a_probe(struct platform_device *pdev){int ret;ret = gpio_request(CH341A_GPIO_PIN, "ch341a");if (ret) {dev_err(&pdev->dev, "Failed to request GPIO pin %d", CH341A_GPIO_PIN);return ret;}// 初始化ch341a设备,例如设置波特率、数据位等// ...return 0;}static int ch341a_remove(struct platform_device *pdev){gpio_free(CH341A_GPIO_PIN);// 释放ch341a设备资源,例如关闭串口等// ...return 0;}static const struct of_device_id ch341a_of_match[] = { { .compatible = "ch341a", },{ /* sentinel */ }};MODULE_DEVICE_TABLE(of, ch341a_of_match);static struct platform_driver ch341a_driver = {.probe = ch341a_probe,.remove = ch341a_remove,.driver = {.name = "ch341a",.of_match_table = ch341a_of_match,},};module_platform_driver(ch341a_driver);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Driver for ch341a USB-Serial adapter"); MODULE_AUTHOR("Your Name");```2. 编译驱动模块:```bashmake -C /lib/modules/$(uname -r)/build M=$(pwd) modules```3. 加载驱动模块:```bashsudo insmod ch341a.ko```4. 卸载驱动模块:```bashsudo rmmod ch341a```。
Linux驱动--platform总线
Linux设备模型之platform总线-一:前言Platform总线是kernel中最近加入的一种虚拟总线.在近版的2.6kernel中,很多驱动都用platform改写了.只有在分析完platform总线之后,才能继续深入下去分析.在分析完sysfs和设备驱动模型之后,这部份应该很简单了.闲言少叙.步入正题.GO.GO!以下的源代码分析是基于2.6.25的.二:platform概貌在分析源代码之前,先在内核代码中找一个platform架构的驱动程序.下面以i8042芯片的驱动为例进行分析.在linux-2.6.25/drivers/input/serio/i8042.c的intel 8042的初始化入口中,有以下代码分段: static int __init i8042_init(void){……err = platform_driver_register(&i8042_driver);if (err)goto err_platform_exit;i8042_platform_device = platform_device_alloc("i8042", -1);if (!i8042_platform_device) {err = -ENOMEM;goto err_unregister_driver;}err = platform_device_add(i8042_platform_device);if (err)goto err_free_device;……}我们在上面的程序片段中看到,驱动程序先注册了一个platform device.然后又添加了一个platform device.这里就涉及到了platform的两个最主要的操作,一个设备驱动注册,一个设备注册.要了解platform总线的来龙去脉.得从它的初始化开始.三:platform初始化Platform总线的初始化是在linux-2.6.25/drivers/base/platform.c中的platform_bus_init()完成的,代码如下:int __init platform_bus_init(void){int error;error = device_register(&platform_bus);if (error)return error;error = bus_register(&platform_bus_type);if (error)device_unregister(&platform_bus);return error;}上面代码中调用的子函数在<<linux设备模型深探>>中已经分析过了.这段初始化代码创建了一个名为“platform”的设备.后续platform的设备都会以此为parent.在sysfs中表示为:所有platform类型的设备都会添加在platform_bus所代码的目录下.即/sys/devices/platform 下面.接着,这段初始化代码又创建了一个名为“platform”的总线.platform_bus_type的定义如下:struct bus_type platform_bus_type = {.name = "platform",.dev_attrs = platform_dev_attrs,.match = platform_match,.uevent = platform_uevent,.suspend = platform_suspend,.suspend_late = platform_suspend_late,.resume_early = platform_resume_early,.resume = platform_resume,};我们知道,在bus_type中包含了诸如设备与驱动匹配(mach)、hotplug(uevent)事件等很多重要的操作.这些操作在分析platform设备注册与platform驱动注册的时候依次分析.四:platform device注册在intel 8042的驱动代码中,我们看到注册一个platform device分为了两部分,一部份是创建一个platform device结构,另一部份是将其注册到总线中.先来看第一个接口.struct platform_device *platform_device_alloc(const char *name, int id){struct platform_object *pa;pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);if (pa) {strcpy(pa->name, name);pa-> = pa->name;pa->pdev.id = id;device_initialize(&pa->pdev.dev);pa->pdev.dev.release = platform_device_release;}return pa ? &pa->pdev : NULL;}这段代码主要初始化了封装在struct platform_object中的struct device.Struct platform_object结构定义如下:struct platform_object {struct platform_device pdev;char name[1];};在定义中,定义name为一个长度.所以,才有了platform_device_alloc()中分配struct platform_object的时候多加了名称的长度:pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);struct device结构如下:struct platform_device {const char * name;int id;struct device dev;u32 num_resources;struct resource * resource;};在这个结构里封装了struct device.struct resource. struct resource这个结构在此之前没有接触过,这个结构表示设备所拥有的资源.即I/O端口或者是I/O映射内存.platform_device_add()代码分段分析如下:int platform_device_add(struct platform_device *pdev){int i, ret = 0;if (!pdev)return -EINVAL;if (!pdev->dev.parent)pdev->dev.parent = &platform_bus;pdev->dev.bus = &platform_bus_type;初始化设备的parent为platform_bus.初始化驱备的总线为platform_bus_type.回忆platform初始化的时候.这两个东东这里终于派上用场了.if (pdev->id != -1)snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,pdev->id);elsestrlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);设置设备struct device 的bus_id成员.留心这个地方,在以后还需要用到这个的.for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i];if (r->name == NULL)r->name = pdev->dev.bus_id;p = r->parent;if (!p) {if (r->flags & IORESOURCE_MEM)p = &iomem_resource;else if (r->flags & IORESOURCE_IO)p = &ioport_resource;}if (p && insert_resource(p, r)) {printk(KERN_ERR"%s: failed to claim resource %d\n",pdev->dev.bus_id, i);ret = -EBUSY;goto failed;}}如果设备指定了它所拥有的资源,将这些资源分配给它.如果分配失败,则失败退出.从代码中可以看出.如果struct resource的flags域被指定为IORESOURCE_MEM.则所表示的资源为I/O映射内存.如果指定为IORESOURCE_IO.则所表示的资源为I/O端口.pr_debug("Registering platform device '%s'. Parent at %s\n",pdev->dev.bus_id, pdev->dev.parent->bus_id);ret = device_add(&pdev->dev);if (ret == 0)return ret;failed:while (--i >= 0)if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))release_resource(&pdev->resource[i]);return ret;}device_add()已经很熟悉了吧.没错,它就是将设备注册到指定的bus_type.在分析linux设备模型的时候,曾说过.调用device_add()会产生一个hotplug事件.platform device的hotplug与一般的device事件相比.它还要它所属bus_type的uevent().对这个流程不熟悉的可以参照<< linux设备模型深探>>.platform device所属的bus_type为platform_bus_type.它的uevent()接口代码如下:static int platform_uevent(struct device *dev, struct kobj_uevent_env *env){struct platform_device *pdev = to_platform_device(dev);add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);return 0;}上面的代码很简单,它就是在环境变量中添加一项MODALIAS.五:platform driver的注册在intel 8024的驱动代码中看到.platform driver注册的接口为:int platform_driver_register(struct platform_driver *drv)drv->driver.bus = &platform_bus_type;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;if (drv->suspend)drv->driver.suspend = platform_drv_suspend;if (drv->resume)drv->driver.resume = platform_drv_resume;return driver_register(&drv->driver);}struct platform_driver主要封装了struct device_driver结构.如下:struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*suspend_late)(struct platform_device *, pm_message_t state);int (*resume_early)(struct platform_device *);int (*resume)(struct platform_device *);struct device_driver driver;};在这个接口里,它指定platform driver的所属总线.如果在struct platform_driver指定了各项接口的操作,就会为struct device_driver中的相应接口赋值.不要被上面的platform_drv_XXX吓倒了,它们其实很简单,就是将struct device转换为struct platform_device和struct platform_driver.然后调用platform_driver中的相应接口函数最后, platform_driver_register()将驱动注册到总线上.这样,总线上有设备,又有驱动,就会进行设备与匹配的过程,调用的相应接口为:bus ->match --- > bus->probe/driver->probe (如果总线的probe操作不存在,就会调用设备的probe接口).这样,我们又回到platform_bus_type中的各项操作了,调用总线的match函数,检查驱动和设备是否匹配。
xilinx PCIE的Linux驱动程序源代码
//--------------------------------------------------------------------------------//-- Filename: xbmd.h//--//-- Description: Main header file for kernel driver//--//-- XBMD is an example Red Hat device driver which exercises XBMD design //-- Device driver has been tested on Red Hat Fedora FC9 2.6.15.//--------------------------------------------------------------------------------// Define Result values#define SUCCESS 0#define CRIT_ERR -1// Debug - define will output more info#define Verbose 1// Max DMA Buffer Size#define BUF_SIZE (4096 * 1024)enum {INITCARD,INITRST,DISPREGS,RDDCSR,RDDDMACR,RDWDMATLPA,RDWDMATLPS,RDWDMATLPC,RDWDMATLPP,RDRDMATLPP,RDRDMATLPA,RDRDMATLPS,RDRDMATLPC,RDWDMAPERF,RDRDMAPERF,RDRDMASTAT,RDNRDCOMP,RDRCOMPDSIZE,RDDLWSTAT,RDDLTRSSTAT,RDDMISCCONT,RDDMISCONT,DFCCTL,DFCPINFO,DFCNPINFO,DFCINFO,RDCFGREG,WRCFGREG,RDBMDREG,WRBMDREG,WRDDMACR,WRWDMATLPS,WRWDMATLPC,WRWDMATLPP,WRRDMATLPS,WRRDMATLPC,WRRDMATLPP,WRDMISCCONT,WRDDLNKC,NUMCOMMANDS};//--------------------------------------------------------------------------------//-- Filename: xbmd.c//--//-- Description: XBMD device driver.//--//-- XBMD is an example Red Hat device driver which exercises XBMD design //-- Device driver has been tested on Red Hat Fedora FC9 2.6.15.//--------------------------------------------------------------------------------#include <linux/init.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/fs.h>//#include <linux/pci-aspm.h>//#include <linux/pci_regs.h>#include <asm/uaccess.h> /* copy_to_user */#include "xbmd.h"// semaphoresenum {SEM_READ,SEM_WRITE,SEM_WRITEREG,SEM_READREG,SEM_W AITFOR,SEM_DMA,NUM_SEMS};//semaphoresstruct semaphore gSem[NUM_SEMS];MODULE_LICENSE("Dual BSD/GPL");// Defines the Vendor ID. Must be changed if core generated did not set the Vendor ID to the same value #define PCI_VENDOR_ID_XILINX 0x10ee// Defines the Device ID. Must be changed if core generated did not set the Device ID to the same value #define PCI_DEVICE_ID_XILINX_PCIE 0x0007// Defining#define XBMD_REGISTER_SIZE (4*8) // There are eight registers, and each is 4 bytes wide. #define HA VE_REGION 0x01 // I/O Memory region#define HA VE_IRQ 0x02 // Interupt//Status Flags:// 1 = Resouce successfully acquired// 0 = Resource not acquired.#define HA VE_REGION 0x01 // I/O Memory region#define HA VE_KREG 0x04 // Kernel registrationint gDrvrMajor = 241; // Major number not dynamic.unsigned int gStatFlags = 0x00; // Status flags used for cleanup.unsigned long gBaseHdwr; // Base register address (Hardware address) unsigned long gBaseLen; // Base register address Lengthvoid *gBaseVirt = NULL; // Base register address (Virtual address, for I/O). char gDrvrName[]= "xbmd"; // Name of driver in proc.struct pci_dev *gDev = NULL; // PCI device structure.int gIrq; // IRQ assigned by PCI system.char *gBufferUnaligned = NULL; // Pointer to Unaligned DMA buffer.char *gReadBuffer = NULL; // Pointer to dword aligned DMA buffer.char *gWriteBuffer = NULL; // Pointer to dword aligned DMA buffer.dma_addr_t gReadHW Addr;dma_addr_t gWriteHWAddr;unsigned long SA_SHIRQ = 0;unsigned long SA_SAMPLE_RANDOM = 0;int pos;// Struct Used for Writing CFG Register. Holds value and register to be writtentypedef struct cfgwrite {int reg;int value;} cfgwr;// Struct Used for Writing BMD Register. Holds value and register to be writtentypedef struct bmdwrite {int reg;int value;} bmdwr;//-----------------------------------------------------------------------------// Prototypes//-----------------------------------------------------------------------------void XPCIe_IRQHandler (int irq, void *dev_id, struct pt_regs *regs);u32 XPCIe_ReadReg (u32 dw_offset);void XPCIe_WriteReg (u32 dw_offset, u32 val);void XPCIe_InitCard (void);void XPCIe_InitiatorReset (void);u32 XPCIe_ReadCfgReg (u32 byte);u32 XPCIe_WriteCfgReg (u32 byte, u32 value);//---------------------------------------------------------------------------// Name: XPCIe_Open//// Description: Book keeping routine invoked each time the device is opened. //// Arguments: inode :// filp ://// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Open(struct inode *inode, struct file *filp){printk(KERN_INFO"%s: Open: module opened\n",gDrvrName); return SUCCESS;}//---------------------------------------------------------------------------// Name: XPCIe_Release//// Description: Book keeping routine invoked each time the device is closed. //// Arguments: inode :// filp ://// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Release(struct inode *inode, struct file *filp){printk(KERN_INFO"%s: Release: module released\n",gDrvrName); return(SUCCESS);}//---------------------------------------------------------------------------// Name: XPCIe_Write// Description: This routine is invoked from user space to write data to// the PCIe device.//// Arguments: filp : file pointer to opened device.// buf : pointer to location in users space, where data is to// be acquired.// count : Amount of data in bytes user wishes to send.//// Returns: SUCCESS = Success// CRIT_ERR = Critical failure//// Modification log:// Date Who Description////---------------------------------------------------------------------------ssize_t XPCIe_Write(struct file *filp, const char *buf, size_t count,loff_t *f_pos){int ret = SUCCESS;memcpy((char *)gWriteBuffer, buf, count);printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count); memcpy((char *)gReadBuffer, buf, count);printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count); return (ret);}//---------------------------------------------------------------------------// Name: XPCIe_Read//// Description: This routine is invoked from user space to read data from// the PCIe device. ***NOTE: This routine returns the entire// buffer, (BUF_SIZE), count is ignored!. The user App must// do any needed processing on the buffer.//// Arguments: filp : file pointer to opened device.// buf : pointer to location in users space, where data is to// be placed.// count : Amount of data in bytes user wishes to read.//// Returns: SUCCESS = Success// CRIT_ERR = Critical failure//// Date Who Description//----------------------------------------------------------------------------ssize_t XPCIe_Read(struct file *filp, char *buf, size_t count, loff_t *f_pos){memcpy(buf, (char *)gWriteBuffer, count);printk(KERN_INFO"%s: XPCIe_Read: %d bytes have been read...\n", gDrvrName, count); return (0);}//---------------------------------------------------------------------------// Name: XPCIe_Ioctl//// Description: This routine is invoked from user space to configure the// running driver.//// Arguments: inode :// filp : File pointer to opened device.// cmd : Ioctl command to execute.// arg : Argument to Ioctl command.//// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){u32 regx;int ret = SUCCESS;switch (cmd) {case INITCARD: // Initailizes XBMD applicationXPCIe_InitCard();break;case INITRST: // Resets XBMD applicationsXPCIe_InitiatorReset();case DISPREGS:break;case RDDCSR: // Read: Device Control Status Registerregx = XPCIe_ReadReg(0);*((u32 *)arg) = regx;break;case RDDDMACR: // Read: DMA Control Status Register regx = XPCIe_ReadReg(1);*((u32 *)arg) = regx;break;case RDWDMATLPA: // Read: Write DMA TLP Address Register regx = XPCIe_ReadReg(2);*((u32 *)arg) = regx;break;case RDWDMATLPS: // Read: Write DMA TLP Size Register regx = XPCIe_ReadReg(3);*((u32 *)arg) = regx;break;case RDWDMATLPC: // Read: Write DMA TLP Count Register regx = XPCIe_ReadReg(4);*((u32 *)arg) = regx;break;case RDWDMATLPP: // Read: Write DMA TLP Pattern Register regx = XPCIe_ReadReg(5);*((u32 *)arg) = regx;break;case RDRDMATLPP: // Read: Read DMA TLP Pattern Register regx = XPCIe_ReadReg(6);*((u32 *)arg) = regx;break;case RDRDMATLPA: // Read: Read DMA TLP Address Register regx = XPCIe_ReadReg(7);*((u32 *)arg) = regx;break;case RDRDMATLPS: // Read: Read DMA TLP Size Register regx = XPCIe_ReadReg(8);*((u32 *)arg) = regx;break;case RDRDMATLPC: // Read: Read DMA TLP Count Register regx = XPCIe_ReadReg(9);*((u32 *)arg) = regx;break;regx = XPCIe_ReadReg(10);*((u32 *)arg) = regx;break;case RDRDMAPERF: // Read: Read DMA Performance Registerregx = XPCIe_ReadReg(11);*((u32 *)arg) = regx;break;case RDRDMASTAT: // Read: Read DMA Status Registerregx = XPCIe_ReadReg(12);*((u32 *)arg) = regx;break;case RDNRDCOMP: // Read: Number of Read Completion w/ Data Register regx = XPCIe_ReadReg(13);*((u32 *)arg) = regx;break;case RDRCOMPDSIZE: // Read: Read Completion Size Registerregx = XPCIe_ReadReg(14);*((u32 *)arg) = regx;break;case RDDLWSTAT: // Read: Device Link Width Status Registerregx = XPCIe_ReadReg(15);*((u32 *)arg) = regx;break;case RDDLTRSSTAT: // Read: Device Link Transaction Size Status Register regx = XPCIe_ReadReg(16);*((u32 *)arg) = regx;break;case RDDMISCCONT: // Read: Device Miscellaneous Control Registerregx = XPCIe_ReadReg(17);*((u32 *)arg) = regx;break;case RDDMISCONT: // Read: Device MSI Controlregx = XPCIe_ReadReg(18);*((u32 *)arg) = regx;break;case RDDLNKC: // Read: Device Directed Link Change Registerregx = XPCIe_ReadReg(19);*((u32 *)arg) = regx;break;case DFCCTL: // Read: Device FC Control Registerregx = XPCIe_ReadReg(20);*((u32 *)arg) = regx;case DFCPINFO: // Read: Device FC Posted Informationregx = XPCIe_ReadReg(21);*((u32 *)arg) = regx;break;case DFCNPINFO: // Read: Device FC Non Posted Informationregx = XPCIe_ReadReg(22);*((u32 *)arg) = regx;break;case DFCINFO: // Read: Device FC Completion Informationregx = XPCIe_ReadReg(23);*((u32 *)arg) = regx;break;case WRDDMACR: // Write: DMA Control Status RegisterXPCIe_WriteReg(1, arg);break;case WRWDMATLPS: // Write: Write DMA TLP Size RegisterXPCIe_WriteReg(3, arg);break;case WRWDMATLPC: // Write: Write DMA TLP Count RegisterXPCIe_WriteReg(4, arg);break;case WRWDMATLPP: // Write: Write DMA TLP Pattern RegisterXPCIe_WriteReg(5, arg);break;case WRRDMATLPS: // Write: Read DMA TLP Size RegisterXPCIe_WriteReg(8, arg);break;case WRRDMATLPC: // Write: Read DMA TLP Count RegisterXPCIe_WriteReg(9, arg);break;case WRRDMATLPP: // Write: Read DMA TLP Pattern RegisterXPCIe_WriteReg(6, arg);break;case WRDMISCCONT: // Write: Device Miscellaneous Control RegisterXPCIe_WriteReg(18, arg);break;case WRDDLNKC: // Write: Device Directed Link Change RegisterXPCIe_WriteReg(19, arg);break;case RDBMDREG: // Read: Any XBMD Reg. Added generic functionality so all register can be read regx = XPCIe_ReadReg(*(u32 *)arg);*((u32 *)arg) = regx;case RDCFGREG: // Read: Any CFG Reg. Added generic functionality so all register can be read regx = XPCIe_ReadCfgReg(*(u32 *)arg);*((u32 *)arg) = regx;break;case WRBMDREG: // Write: Any BMD Reg. Added generic functionality so all register can be read XPCIe_WriteReg((*(bmdwr *)arg).reg,(*(bmdwr *)arg).value);printk(KERN_WARNING"%d: Write Register.\n", (*(bmdwr *)arg).reg);printk(KERN_WARNING"%d: Write Value\n", (*(bmdwr *)arg).value);break;case WRCFGREG: // Write: Any CFG Reg. Added generic functionality so all register can be read regx = XPCIe_WriteCfgReg((*(cfgwr *)arg).reg,(*(cfgwr *)arg).value);printk(KERN_WARNING"%d: Write Register.\n", (*(cfgwr *)arg).reg);printk(KERN_WARNING"%d: Write Value\n", (*(cfgwr *)arg).value);break;default:break;}return ret;}// Aliasing write, read, ioctl, etc...struct file_operations XPCIe_Intf = {read: XPCIe_Read,write: XPCIe_Write,ioctl: XPCIe_Ioctl,open: XPCIe_Open,release: XPCIe_Release,};static int XPCIe_init(void){// Find the Xilinx EP device. The device is found by matching device and vendor ID's which is defined// at the top of this file. Be default, the driver will look for 10EE & 0007. If the core is generated// with other settings, the defines at the top must be changed or the driver will not loadgDev = pci_find_device (PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_PCIE, gDev);if (NULL == gDev) {// If a matching device or vendor ID is not found, return failure and update kernel log.// NOTE: In fedora systems, the kernel log is located at: /var/log/messagesreturn (CRIT_ERR);}// Get Base Address of registers from pci structure. Should come from pci_dev// structure, but that element seems to be missing on the development system.gBaseHdwr = pci_resource_start (gDev, 0);if (0 > gBaseHdwr) {printk(KERN_WARNING"%s: Init: Base Address not set.\n", gDrvrName);return (CRIT_ERR);}// Print Base Address to kernel logprintk(KERN_INFO"%s: Init: Base hw val %X\n", gDrvrName, (unsigned int)gBaseHdwr);// Get the Base Address LengthgBaseLen = pci_resource_len (gDev, 0);// Print the Base Address Length to Kernel Logprintk(KERN_INFO"%s: Init: Base hw len %d\n", gDrvrName, (unsigned int)gBaseLen);// Remap the I/O register block so that it can be safely accessed.// I/O register block starts at gBaseHdwr and is 32 bytes long.// It is cast to char because that is the way Linus does it.// Reference "/usr/src/Linux-2.4/Documentation/IO-mapping.txt".gBaseVirt = ioremap(gBaseHdwr, gBaseLen);if (!gBaseVirt) {printk(KERN_WARNING"%s: Init: Could not remap memory.\n", gDrvrName);return (CRIT_ERR);}// Print out the aquired virtual base addresssprintk(KERN_INFO"%s: Init: Virt HW address %X\n", gDrvrName, (unsigned int)gBaseVirt); // Get IRQ from pci_dev structure. It may have been remapped by the kernel,// and this value will be the correct one.gIrq = gDev->irq;printk(KERN_INFO"%s: Init: Device IRQ: %X\n",gDrvrName, gIrq);//---START: Initialize Hardwareif (0 > check_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE)) {printk(KERN_WARNING"%s: Init: Memory in use.\n", gDrvrName);return (CRIT_ERR);}// Try to gain exclusive control of memory for demo hardware.request_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE, "3GIO_Demo_Drv");// Update flagsgStatFlags = gStatFlags | HA VE_REGION;printk(KERN_INFO"%s: Init: Initialize Hardware Done..\n",gDrvrName);// Request IRQ from OS.// In past architectures, the SHARED and SAMPLE_RANDOM flags were called: SA_SHIRQ and SA_SAMPLE_RANDOM// respectively. In older Fedora core installations, the request arguments may need to be reverted back.// SA_SHIRQ | SA_SAMPLE_RANDOMprintk(KERN_INFO"%s: ISR Setup..\n", gDrvrName);if (0 > request_irq(gIrq, &XPCIe_IRQHandler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, gDrvrName, gDev)) {printk(KERN_WARNING"%s: Init: Unable to allocate IRQ",gDrvrName);return (CRIT_ERR);}// Update flags stating IRQ was successfully obtainedgStatFlags = gStatFlags | HA VE_IRQ;// Bus Master Enableif (0 > pci_enable_device(gDev)) {printk(KERN_WARNING"%s: Init: Device not enabled.\n", gDrvrName);return (CRIT_ERR);}//--- END: Initialize Hardware//--- START: Allocate Buffers// Allocate the read buffer with size BUF_SIZE and return the starting addressgReadBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gReadHWAddr);if (NULL == gReadBuffer) {printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);return (CRIT_ERR);}// Print Read buffer size and address to kernel logprintk(KERN_INFO"%s: Read Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gReadBuffer, (unsigned int)gReadHW Addr);// Allocate the write buffer with size BUF_SIZE and return the starting addressgWriteBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gWriteHWAddr);if (NULL == gWriteBuffer) {printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);return (CRIT_ERR);}// Print Write buffer size and address to kernel logprintk(KERN_INFO"%s: Write Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gWriteBuffer, (unsigned int)gWriteHW Addr); //--- END: Allocate Buffers//--- START: Register Driver// Register with the kernel as a character device.if (0 > register_chrdev(gDrvrMajor, gDrvrName, &XPCIe_Intf)) {printk(KERN_WARNING"%s: Init: will not register\n", gDrvrName);return (CRIT_ERR);}printk(KERN_INFO"%s: Init: module registered\n", gDrvrName);gStatFlags = gStatFlags | HA VE_KREG;//--- END: Register Driver// The driver is now successfully loaded. All HW is initialized, IRQ's assigned, and buffers allocatedprintk("%s driver is loaded\n", gDrvrName);// Initializing card registersXPCIe_InitCard();return 0;}//--- XPCIe_InitiatorReset(): Resets the XBMD reference design//--- Arguments: None//--- Return Value: None//--- Detailed Description: Writes a 1 to the DCSR register which resets the XBMD designvoid XPCIe_InitiatorReset(){XPCIe_WriteReg(0, 1); // Write: DCSR (offset 0) with value of 1 (Reset Device)XPCIe_WriteReg(0, 0); // Write: DCSR (offset 0) with value of 0 (Make Active)}//--- XPCIe_InitCard(): Initializes XBMD descriptor registers to default values//--- Arguments: None//--- Return Value: None//--- Detailed Description: 1) Resets device//--- 2) Writes specific values into the XBMD registers inside the EPvoid XPCIe_InitCard(){XPCIe_WriteReg(0, 1); // Write: DCSR (offset 0) with value of 1 (Reset Device)XPCIe_WriteReg(0, 0); // Write: DCSR (offset 0) with value of 0 (Make Active)XPCIe_WriteReg(2, gWriteHW Addr); // Write: Write DMA TLP Address register with starting address XPCIe_WriteReg(3, 0x20); // Write: Write DMA TLP Size register with default value (32dwords) XPCIe_WriteReg(4, 0x2000); // Write: Write DMA TLP Count register with default value (2000) XPCIe_WriteReg(5, 0x00000000); // Write: Write DMA TLP Pattern register with default value (0x0) XPCIe_WriteReg(6, 0xfeedbeef); // Write: Read DMA Expected Data Pattern with default value (feedbeef) XPCIe_WriteReg(7, gReadHW Addr); // Write: Read DMA TLP Address register with starting address. XPCIe_WriteReg(8, 0x20); // Write: Read DMA TLP Size register with default value (32dwords) XPCIe_WriteReg(9, 0x2000); // Write: Read DMA TLP Count register with default value (2000)}//--- XPCIe_exit(): Performs any cleanup required before releasing the device//--- Arguments: None//--- Return Value: None//--- Detailed Description: Performs all cleanup functions required before releasing devicestatic void XPCIe_exit(void){// Check if we have a memory region and free itif (gStatFlags & HA VE_REGION) {(void) release_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE);}// Check if we have an IRQ and free itif (gStatFlags & HA VE_IRQ) {(void) free_irq(gIrq, gDev);}// Free Write and Read buffers allocated to useif (NULL != gReadBuffer)(void) kfree(gReadBuffer);(void) kfree(gWriteBuffer);// Free memory allocated to our Endpointpci_free_consistent(gDev, BUF_SIZE, gReadBuffer, gReadHW Addr);pci_free_consistent(gDev, BUF_SIZE, gWriteBuffer, gWriteHW Addr);gReadBuffer = NULL;gWriteBuffer = NULL;// Free up memory pointed to by virtual addressif (gBaseVirt != NULL) {iounmap(gBaseVirt);}gBaseVirt = NULL;// Unregister Device Driverif (gStatFlags & HA VE_KREG) {unregister_chrdev(gDrvrMajor, gDrvrName);}gStatFlags = 0;// Update Kernel log stating driver is unloadedprintk(KERN_ALERT"%s driver is unloaded\n", gDrvrName);}// Driver Entry Pointmodule_init(XPCIe_init);// Driver Exit Pointmodule_exit(XPCIe_exit);void XPCIe_IRQHandler(int irq, void *dev_id, struct pt_regs *regs){u32 i, regx;printk(KERN_WARNING"%s: Interrupt Handler Start ..",gDrvrName); for (i = 0; i < 32; i++) {regx = XPCIe_ReadReg(i);printk(KERN_W ARNING"%s Interrupt Handler End ..\n", gDrvrName);}u32 XPCIe_ReadReg (u32 dw_offset){u32 ret = 0;//u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));//ret = readl(reg_addr);ret = readl(gBaseVirt + (4 * dw_offset));return ret;}void XPCIe_WriteReg (u32 dw_offset, u32 val){//u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));writel(val, (gBaseVirt + (4 * dw_offset)));}ssize_t* XPCIe_ReadMem(char *buf, size_t count){int ret = 0;dma_addr_t dma_addr;//make sure passed in buffer is large enoughif ( count < BUF_SIZE ) {printk("%s: XPCIe_Read: passed in buffer too small.\n", gDrvrName);ret = -1;goto exit;}down(&gSem[SEM_DMA]);// pci_map_single return the physical address corresponding to// the virtual address passed to it as the 2nd parameterdma_addr = pci_map_single(gDev, gReadBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);if ( 0 == dma_addr ) {printk("%s: XPCIe_Read: Map error.\n",gDrvrName);ret = -1;// Now pass the physical address to the device hardware. This is now// the destination physical address for the DMA and hence the to be// put on Memory Transactions// Do DMA transfer here....printk("%s: XPCIe_Read: ReadBuf Virt Addr = %x Phy Addr = %x.\n",gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);// Unmap the DMA buffer so it is safe for normal access again.pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);up(&gSem[SEM_DMA]);// Now it is safe to copy the data to user space.if ( copy_to_user(buf, gReadBuffer, BUF_SIZE) ) {ret = -1;printk("%s: XPCIe_Read: Failed copy to user.\n",gDrvrName);goto exit;}exit:return ret;}ssize_t XPCIe_WriteMem(const char *buf, size_t count) {int ret = 0;dma_addr_t dma_addr;if ( (count % 4) != 0 ) {printk("%s: XPCIe_Write: Buffer length not dword aligned.\n",gDrvrName);ret = -1;goto exit;}// Now it is safe to copy the data from user space.if ( copy_from_user(gWriteBuffer, buf, count) ) {ret = -1;printk("%s: XPCIe_Write: Failed copy to user.\n",gDrvrName);goto exit;}//set DMA semaphore if in loopback// pci_map_single return the physical address corresponding to// the virtual address passed to it as the 2nd parameterdma_addr = pci_map_single(gDev, gWriteBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);if ( 0 == dma_addr ) {printk("%s: XPCIe_Write: Map error.\n",gDrvrName);ret = -1;goto exit;}// Now pass the physical address to the device hardware. This is now// the source physical address for the DMA and hence the to be// put on Memory Transactions// Do DMA transfer here....printk("%s: XPCIe_Write: WriteBuf Virt Addr = %x Phy Addr = %x.\n",gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);// Unmap the DMA buffer so it is safe for normal access again.pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);up(&gSem[SEM_DMA]);exit:return (ret);}u32 XPCIe_ReadCfgReg (u32 byte) {u32 pciReg;if (pci_read_config_dword(gDev, byte, &pciReg) < 0) {printk("%s: XPCIe_ReadCfgReg: Reading PCI interface failed.",gDrvrName);return (-1);}return (pciReg);}u32 XPCIe_WriteCfgReg (u32 byte, u32 val) {if (pci_write_config_dword(gDev, byte, val) < 0) {printk("%s: XPCIe_Read Device Control: Reading PCI interface failed.",gDrvrName);return (-1);}return 1;}。
linux中编译驱动的方法
linux中编译驱动的方法
在Linux中编译驱动的方法通常涉及以下步骤:
1. 编写驱动代码:首先,您需要编写适用于Linux内核的驱动代码。
这通常是在内核源代码树之外编写的。
驱动代码通常以C语言编写,并遵循内核编程约定。
2. 获取内核源代码:为了编译驱动,您需要获得Linux内核的源代码。
您可以从Linux官方网站或镜像站点下载内核源代码。
3. 配置内核:在编译驱动之前,您需要配置内核以包含您的驱动。
这可以通过运行`make menuconfig`命令来完成。
在配置菜单中,您可以选择要编译的驱动以及相关的内核选项。
4. 编译驱动:一旦您配置了内核并选择了要编译的驱动,您可以使用`make`命令来编译驱动。
这将在内核源代码目录下生成可执行文件或模块文件。
5. 加载和测试驱动:一旦驱动被编译,您可以将其加载到Linux 内核中以进行测试。
您可以使用`insmod`命令将模块加载到内核,然后使用`dmesg`命令检查内核日志以查看驱动是否正确加载。
这些是基本的步骤,但具体的步骤可能会因您的环境和需求而有所不同。
在编译和加载驱动时,请确保您具有适当的权限和知识,因为这可能需要管理员权限,并且错误的操作可能会导致系统不稳定或损坏。
linux下8025T iic(I2C)驱动源代码
}
if (NULL == reg_addr && NULL != tmp ){
kfree( tmp );
printk( KERN_INFO "----kmalloc()--error--\r\n" );
return -ENOMEM;
ret,reg[0],reg[1],reg[2],reg[3],reg[4],reg[5],reg[6],reg[7] );
}
return ret;
}
static int _8025t_detach(struct i2c_adapter *adap)
{
return 0;
printk( KERN_INFO "----i2c_attach_client()--error--\r\n" );
}
printk( KERN_INFO "----_8025t_attach()--end--\r\n" );
return 0;
}
static int _8025t_probe( struct i2c_adapter *adapter )
}
memset( tmp, 0x00, count );
memset( reg_addr, 0x00, count );
reg_addr[0] = buf[0];
tmp[0] = buf[0];
msg[0].addr = p_bห้องสมุดไป่ตู้nk->client.addr;
}
char reg[]={0x00,0x30,0x30,0x10,0x30,0x20,0x08,0x10};
xilinx PCIE的Linux驱动程序源代码
//--------------------------------------------------------------------------------//-- Filename: xbmd.h//--//-- Description: Main header file for kernel driver//--//-- XBMD is an example Red Hat device driver which exercises XBMD design //-- Device driver has been tested on Red Hat Fedora FC9 2.6.15.//--------------------------------------------------------------------------------// Define Result values#define SUCCESS 0#define CRIT_ERR -1// Debug - define will output more info#define Verbose 1// Max DMA Buffer Size#define BUF_SIZE (4096 * 1024)enum {INITCARD,INITRST,DISPREGS,RDDCSR,RDDDMACR,RDWDMATLPA,RDWDMATLPS,RDWDMATLPC,RDWDMATLPP,RDRDMATLPP,RDRDMATLPA,RDRDMATLPS,RDRDMATLPC,RDWDMAPERF,RDRDMAPERF,RDRDMASTAT,RDNRDCOMP,RDRCOMPDSIZE,RDDLWSTAT,RDDLTRSSTAT,RDDMISCCONT,RDDMISCONT,DFCCTL,DFCPINFO,DFCNPINFO,DFCINFO,RDCFGREG,WRCFGREG,RDBMDREG,WRBMDREG,WRDDMACR,WRWDMATLPS,WRWDMATLPC,WRWDMATLPP,WRRDMATLPS,WRRDMATLPC,WRRDMATLPP,WRDMISCCONT,WRDDLNKC,NUMCOMMANDS};//--------------------------------------------------------------------------------//-- Filename: xbmd.c//--//-- Description: XBMD device driver.//--//-- XBMD is an example Red Hat device driver which exercises XBMD design //-- Device driver has been tested on Red Hat Fedora FC9 2.6.15.//--------------------------------------------------------------------------------#include <linux/init.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/fs.h>//#include <linux/pci-aspm.h>//#include <linux/pci_regs.h>#include <asm/uaccess.h> /* copy_to_user */#include "xbmd.h"// semaphoresenum {SEM_READ,SEM_WRITE,SEM_WRITEREG,SEM_READREG,SEM_W AITFOR,SEM_DMA,NUM_SEMS};//semaphoresstruct semaphore gSem[NUM_SEMS];MODULE_LICENSE("Dual BSD/GPL");// Defines the Vendor ID. Must be changed if core generated did not set the Vendor ID to the same value #define PCI_VENDOR_ID_XILINX 0x10ee// Defines the Device ID. Must be changed if core generated did not set the Device ID to the same value #define PCI_DEVICE_ID_XILINX_PCIE 0x0007// Defining#define XBMD_REGISTER_SIZE (4*8) // There are eight registers, and each is 4 bytes wide. #define HA VE_REGION 0x01 // I/O Memory region#define HA VE_IRQ 0x02 // Interupt//Status Flags:// 1 = Resouce successfully acquired// 0 = Resource not acquired.#define HA VE_REGION 0x01 // I/O Memory region#define HA VE_KREG 0x04 // Kernel registrationint gDrvrMajor = 241; // Major number not dynamic.unsigned int gStatFlags = 0x00; // Status flags used for cleanup.unsigned long gBaseHdwr; // Base register address (Hardware address) unsigned long gBaseLen; // Base register address Lengthvoid *gBaseVirt = NULL; // Base register address (Virtual address, for I/O). char gDrvrName[]= "xbmd"; // Name of driver in proc.struct pci_dev *gDev = NULL; // PCI device structure.int gIrq; // IRQ assigned by PCI system.char *gBufferUnaligned = NULL; // Pointer to Unaligned DMA buffer.char *gReadBuffer = NULL; // Pointer to dword aligned DMA buffer.char *gWriteBuffer = NULL; // Pointer to dword aligned DMA buffer.dma_addr_t gReadHW Addr;dma_addr_t gWriteHWAddr;unsigned long SA_SHIRQ = 0;unsigned long SA_SAMPLE_RANDOM = 0;int pos;// Struct Used for Writing CFG Register. Holds value and register to be writtentypedef struct cfgwrite {int reg;int value;} cfgwr;// Struct Used for Writing BMD Register. Holds value and register to be writtentypedef struct bmdwrite {int reg;int value;} bmdwr;//-----------------------------------------------------------------------------// Prototypes//-----------------------------------------------------------------------------void XPCIe_IRQHandler (int irq, void *dev_id, struct pt_regs *regs);u32 XPCIe_ReadReg (u32 dw_offset);void XPCIe_WriteReg (u32 dw_offset, u32 val);void XPCIe_InitCard (void);void XPCIe_InitiatorReset (void);u32 XPCIe_ReadCfgReg (u32 byte);u32 XPCIe_WriteCfgReg (u32 byte, u32 value);//---------------------------------------------------------------------------// Name: XPCIe_Open//// Description: Book keeping routine invoked each time the device is opened. //// Arguments: inode :// filp ://// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Open(struct inode *inode, struct file *filp){printk(KERN_INFO"%s: Open: module opened\n",gDrvrName); return SUCCESS;}//---------------------------------------------------------------------------// Name: XPCIe_Release//// Description: Book keeping routine invoked each time the device is closed. //// Arguments: inode :// filp ://// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Release(struct inode *inode, struct file *filp){printk(KERN_INFO"%s: Release: module released\n",gDrvrName); return(SUCCESS);}//---------------------------------------------------------------------------// Name: XPCIe_Write// Description: This routine is invoked from user space to write data to// the PCIe device.//// Arguments: filp : file pointer to opened device.// buf : pointer to location in users space, where data is to// be acquired.// count : Amount of data in bytes user wishes to send.//// Returns: SUCCESS = Success// CRIT_ERR = Critical failure//// Modification log:// Date Who Description////---------------------------------------------------------------------------ssize_t XPCIe_Write(struct file *filp, const char *buf, size_t count,loff_t *f_pos){int ret = SUCCESS;memcpy((char *)gWriteBuffer, buf, count);printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count); memcpy((char *)gReadBuffer, buf, count);printk(KERN_INFO"%s: XPCIe_Write: %d bytes have been written...\n", gDrvrName, count); return (ret);}//---------------------------------------------------------------------------// Name: XPCIe_Read//// Description: This routine is invoked from user space to read data from// the PCIe device. ***NOTE: This routine returns the entire// buffer, (BUF_SIZE), count is ignored!. The user App must// do any needed processing on the buffer.//// Arguments: filp : file pointer to opened device.// buf : pointer to location in users space, where data is to// be placed.// count : Amount of data in bytes user wishes to read.//// Returns: SUCCESS = Success// CRIT_ERR = Critical failure//// Date Who Description//----------------------------------------------------------------------------ssize_t XPCIe_Read(struct file *filp, char *buf, size_t count, loff_t *f_pos){memcpy(buf, (char *)gWriteBuffer, count);printk(KERN_INFO"%s: XPCIe_Read: %d bytes have been read...\n", gDrvrName, count); return (0);}//---------------------------------------------------------------------------// Name: XPCIe_Ioctl//// Description: This routine is invoked from user space to configure the// running driver.//// Arguments: inode :// filp : File pointer to opened device.// cmd : Ioctl command to execute.// arg : Argument to Ioctl command.//// Returns: 0 on success, error code on failure.//// Modification log:// Date Who Description////---------------------------------------------------------------------------int XPCIe_Ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){u32 regx;int ret = SUCCESS;switch (cmd) {case INITCARD: // Initailizes XBMD applicationXPCIe_InitCard();break;case INITRST: // Resets XBMD applicationsXPCIe_InitiatorReset();case DISPREGS:break;case RDDCSR: // Read: Device Control Status Registerregx = XPCIe_ReadReg(0);*((u32 *)arg) = regx;break;case RDDDMACR: // Read: DMA Control Status Register regx = XPCIe_ReadReg(1);*((u32 *)arg) = regx;break;case RDWDMATLPA: // Read: Write DMA TLP Address Register regx = XPCIe_ReadReg(2);*((u32 *)arg) = regx;break;case RDWDMATLPS: // Read: Write DMA TLP Size Register regx = XPCIe_ReadReg(3);*((u32 *)arg) = regx;break;case RDWDMATLPC: // Read: Write DMA TLP Count Register regx = XPCIe_ReadReg(4);*((u32 *)arg) = regx;break;case RDWDMATLPP: // Read: Write DMA TLP Pattern Register regx = XPCIe_ReadReg(5);*((u32 *)arg) = regx;break;case RDRDMATLPP: // Read: Read DMA TLP Pattern Register regx = XPCIe_ReadReg(6);*((u32 *)arg) = regx;break;case RDRDMATLPA: // Read: Read DMA TLP Address Register regx = XPCIe_ReadReg(7);*((u32 *)arg) = regx;break;case RDRDMATLPS: // Read: Read DMA TLP Size Register regx = XPCIe_ReadReg(8);*((u32 *)arg) = regx;break;case RDRDMATLPC: // Read: Read DMA TLP Count Register regx = XPCIe_ReadReg(9);*((u32 *)arg) = regx;break;regx = XPCIe_ReadReg(10);*((u32 *)arg) = regx;break;case RDRDMAPERF: // Read: Read DMA Performance Registerregx = XPCIe_ReadReg(11);*((u32 *)arg) = regx;break;case RDRDMASTAT: // Read: Read DMA Status Registerregx = XPCIe_ReadReg(12);*((u32 *)arg) = regx;break;case RDNRDCOMP: // Read: Number of Read Completion w/ Data Register regx = XPCIe_ReadReg(13);*((u32 *)arg) = regx;break;case RDRCOMPDSIZE: // Read: Read Completion Size Registerregx = XPCIe_ReadReg(14);*((u32 *)arg) = regx;break;case RDDLWSTAT: // Read: Device Link Width Status Registerregx = XPCIe_ReadReg(15);*((u32 *)arg) = regx;break;case RDDLTRSSTAT: // Read: Device Link Transaction Size Status Register regx = XPCIe_ReadReg(16);*((u32 *)arg) = regx;break;case RDDMISCCONT: // Read: Device Miscellaneous Control Registerregx = XPCIe_ReadReg(17);*((u32 *)arg) = regx;break;case RDDMISCONT: // Read: Device MSI Controlregx = XPCIe_ReadReg(18);*((u32 *)arg) = regx;break;case RDDLNKC: // Read: Device Directed Link Change Registerregx = XPCIe_ReadReg(19);*((u32 *)arg) = regx;break;case DFCCTL: // Read: Device FC Control Registerregx = XPCIe_ReadReg(20);*((u32 *)arg) = regx;case DFCPINFO: // Read: Device FC Posted Informationregx = XPCIe_ReadReg(21);*((u32 *)arg) = regx;break;case DFCNPINFO: // Read: Device FC Non Posted Informationregx = XPCIe_ReadReg(22);*((u32 *)arg) = regx;break;case DFCINFO: // Read: Device FC Completion Informationregx = XPCIe_ReadReg(23);*((u32 *)arg) = regx;break;case WRDDMACR: // Write: DMA Control Status RegisterXPCIe_WriteReg(1, arg);break;case WRWDMATLPS: // Write: Write DMA TLP Size RegisterXPCIe_WriteReg(3, arg);break;case WRWDMATLPC: // Write: Write DMA TLP Count RegisterXPCIe_WriteReg(4, arg);break;case WRWDMATLPP: // Write: Write DMA TLP Pattern RegisterXPCIe_WriteReg(5, arg);break;case WRRDMATLPS: // Write: Read DMA TLP Size RegisterXPCIe_WriteReg(8, arg);break;case WRRDMATLPC: // Write: Read DMA TLP Count RegisterXPCIe_WriteReg(9, arg);break;case WRRDMATLPP: // Write: Read DMA TLP Pattern RegisterXPCIe_WriteReg(6, arg);break;case WRDMISCCONT: // Write: Device Miscellaneous Control RegisterXPCIe_WriteReg(18, arg);break;case WRDDLNKC: // Write: Device Directed Link Change RegisterXPCIe_WriteReg(19, arg);break;case RDBMDREG: // Read: Any XBMD Reg. Added generic functionality so all register can be read regx = XPCIe_ReadReg(*(u32 *)arg);*((u32 *)arg) = regx;case RDCFGREG: // Read: Any CFG Reg. Added generic functionality so all register can be read regx = XPCIe_ReadCfgReg(*(u32 *)arg);*((u32 *)arg) = regx;break;case WRBMDREG: // Write: Any BMD Reg. Added generic functionality so all register can be read XPCIe_WriteReg((*(bmdwr *)arg).reg,(*(bmdwr *)arg).value);printk(KERN_WARNING"%d: Write Register.\n", (*(bmdwr *)arg).reg);printk(KERN_WARNING"%d: Write Value\n", (*(bmdwr *)arg).value);break;case WRCFGREG: // Write: Any CFG Reg. Added generic functionality so all register can be read regx = XPCIe_WriteCfgReg((*(cfgwr *)arg).reg,(*(cfgwr *)arg).value);printk(KERN_WARNING"%d: Write Register.\n", (*(cfgwr *)arg).reg);printk(KERN_WARNING"%d: Write Value\n", (*(cfgwr *)arg).value);break;default:break;}return ret;}// Aliasing write, read, ioctl, etc...struct file_operations XPCIe_Intf = {read: XPCIe_Read,write: XPCIe_Write,ioctl: XPCIe_Ioctl,open: XPCIe_Open,release: XPCIe_Release,};static int XPCIe_init(void){// Find the Xilinx EP device. The device is found by matching device and vendor ID's which is defined// at the top of this file. Be default, the driver will look for 10EE & 0007. If the core is generated// with other settings, the defines at the top must be changed or the driver will not loadgDev = pci_find_device (PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_PCIE, gDev);if (NULL == gDev) {// If a matching device or vendor ID is not found, return failure and update kernel log.// NOTE: In fedora systems, the kernel log is located at: /var/log/messagesreturn (CRIT_ERR);}// Get Base Address of registers from pci structure. Should come from pci_dev// structure, but that element seems to be missing on the development system.gBaseHdwr = pci_resource_start (gDev, 0);if (0 > gBaseHdwr) {printk(KERN_WARNING"%s: Init: Base Address not set.\n", gDrvrName);return (CRIT_ERR);}// Print Base Address to kernel logprintk(KERN_INFO"%s: Init: Base hw val %X\n", gDrvrName, (unsigned int)gBaseHdwr);// Get the Base Address LengthgBaseLen = pci_resource_len (gDev, 0);// Print the Base Address Length to Kernel Logprintk(KERN_INFO"%s: Init: Base hw len %d\n", gDrvrName, (unsigned int)gBaseLen);// Remap the I/O register block so that it can be safely accessed.// I/O register block starts at gBaseHdwr and is 32 bytes long.// It is cast to char because that is the way Linus does it.// Reference "/usr/src/Linux-2.4/Documentation/IO-mapping.txt".gBaseVirt = ioremap(gBaseHdwr, gBaseLen);if (!gBaseVirt) {printk(KERN_WARNING"%s: Init: Could not remap memory.\n", gDrvrName);return (CRIT_ERR);}// Print out the aquired virtual base addresssprintk(KERN_INFO"%s: Init: Virt HW address %X\n", gDrvrName, (unsigned int)gBaseVirt); // Get IRQ from pci_dev structure. It may have been remapped by the kernel,// and this value will be the correct one.gIrq = gDev->irq;printk(KERN_INFO"%s: Init: Device IRQ: %X\n",gDrvrName, gIrq);//---START: Initialize Hardwareif (0 > check_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE)) {printk(KERN_WARNING"%s: Init: Memory in use.\n", gDrvrName);return (CRIT_ERR);}// Try to gain exclusive control of memory for demo hardware.request_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE, "3GIO_Demo_Drv");// Update flagsgStatFlags = gStatFlags | HA VE_REGION;printk(KERN_INFO"%s: Init: Initialize Hardware Done..\n",gDrvrName);// Request IRQ from OS.// In past architectures, the SHARED and SAMPLE_RANDOM flags were called: SA_SHIRQ and SA_SAMPLE_RANDOM// respectively. In older Fedora core installations, the request arguments may need to be reverted back.// SA_SHIRQ | SA_SAMPLE_RANDOMprintk(KERN_INFO"%s: ISR Setup..\n", gDrvrName);if (0 > request_irq(gIrq, &XPCIe_IRQHandler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, gDrvrName, gDev)) {printk(KERN_WARNING"%s: Init: Unable to allocate IRQ",gDrvrName);return (CRIT_ERR);}// Update flags stating IRQ was successfully obtainedgStatFlags = gStatFlags | HA VE_IRQ;// Bus Master Enableif (0 > pci_enable_device(gDev)) {printk(KERN_WARNING"%s: Init: Device not enabled.\n", gDrvrName);return (CRIT_ERR);}//--- END: Initialize Hardware//--- START: Allocate Buffers// Allocate the read buffer with size BUF_SIZE and return the starting addressgReadBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gReadHWAddr);if (NULL == gReadBuffer) {printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);return (CRIT_ERR);}// Print Read buffer size and address to kernel logprintk(KERN_INFO"%s: Read Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gReadBuffer, (unsigned int)gReadHW Addr);// Allocate the write buffer with size BUF_SIZE and return the starting addressgWriteBuffer = pci_alloc_consistent(gDev, BUF_SIZE, &gWriteHWAddr);if (NULL == gWriteBuffer) {printk(KERN_CRIT"%s: Init: Unable to allocate gBuffer.\n",gDrvrName);return (CRIT_ERR);}// Print Write buffer size and address to kernel logprintk(KERN_INFO"%s: Write Buffer Allocation: %X->%X\n", gDrvrName, (unsigned int)gWriteBuffer, (unsigned int)gWriteHW Addr); //--- END: Allocate Buffers//--- START: Register Driver// Register with the kernel as a character device.if (0 > register_chrdev(gDrvrMajor, gDrvrName, &XPCIe_Intf)) {printk(KERN_WARNING"%s: Init: will not register\n", gDrvrName);return (CRIT_ERR);}printk(KERN_INFO"%s: Init: module registered\n", gDrvrName);gStatFlags = gStatFlags | HA VE_KREG;//--- END: Register Driver// The driver is now successfully loaded. All HW is initialized, IRQ's assigned, and buffers allocatedprintk("%s driver is loaded\n", gDrvrName);// Initializing card registersXPCIe_InitCard();return 0;}//--- XPCIe_InitiatorReset(): Resets the XBMD reference design//--- Arguments: None//--- Return Value: None//--- Detailed Description: Writes a 1 to the DCSR register which resets the XBMD designvoid XPCIe_InitiatorReset(){XPCIe_WriteReg(0, 1); // Write: DCSR (offset 0) with value of 1 (Reset Device)XPCIe_WriteReg(0, 0); // Write: DCSR (offset 0) with value of 0 (Make Active)}//--- XPCIe_InitCard(): Initializes XBMD descriptor registers to default values//--- Arguments: None//--- Return Value: None//--- Detailed Description: 1) Resets device//--- 2) Writes specific values into the XBMD registers inside the EPvoid XPCIe_InitCard(){XPCIe_WriteReg(0, 1); // Write: DCSR (offset 0) with value of 1 (Reset Device)XPCIe_WriteReg(0, 0); // Write: DCSR (offset 0) with value of 0 (Make Active)XPCIe_WriteReg(2, gWriteHW Addr); // Write: Write DMA TLP Address register with starting address XPCIe_WriteReg(3, 0x20); // Write: Write DMA TLP Size register with default value (32dwords) XPCIe_WriteReg(4, 0x2000); // Write: Write DMA TLP Count register with default value (2000) XPCIe_WriteReg(5, 0x00000000); // Write: Write DMA TLP Pattern register with default value (0x0) XPCIe_WriteReg(6, 0xfeedbeef); // Write: Read DMA Expected Data Pattern with default value (feedbeef) XPCIe_WriteReg(7, gReadHW Addr); // Write: Read DMA TLP Address register with starting address. XPCIe_WriteReg(8, 0x20); // Write: Read DMA TLP Size register with default value (32dwords) XPCIe_WriteReg(9, 0x2000); // Write: Read DMA TLP Count register with default value (2000)}//--- XPCIe_exit(): Performs any cleanup required before releasing the device//--- Arguments: None//--- Return Value: None//--- Detailed Description: Performs all cleanup functions required before releasing devicestatic void XPCIe_exit(void){// Check if we have a memory region and free itif (gStatFlags & HA VE_REGION) {(void) release_mem_region(gBaseHdwr, XBMD_REGISTER_SIZE);}// Check if we have an IRQ and free itif (gStatFlags & HA VE_IRQ) {(void) free_irq(gIrq, gDev);}// Free Write and Read buffers allocated to useif (NULL != gReadBuffer)(void) kfree(gReadBuffer);(void) kfree(gWriteBuffer);// Free memory allocated to our Endpointpci_free_consistent(gDev, BUF_SIZE, gReadBuffer, gReadHW Addr);pci_free_consistent(gDev, BUF_SIZE, gWriteBuffer, gWriteHW Addr);gReadBuffer = NULL;gWriteBuffer = NULL;// Free up memory pointed to by virtual addressif (gBaseVirt != NULL) {iounmap(gBaseVirt);}gBaseVirt = NULL;// Unregister Device Driverif (gStatFlags & HA VE_KREG) {unregister_chrdev(gDrvrMajor, gDrvrName);}gStatFlags = 0;// Update Kernel log stating driver is unloadedprintk(KERN_ALERT"%s driver is unloaded\n", gDrvrName);}// Driver Entry Pointmodule_init(XPCIe_init);// Driver Exit Pointmodule_exit(XPCIe_exit);void XPCIe_IRQHandler(int irq, void *dev_id, struct pt_regs *regs){u32 i, regx;printk(KERN_WARNING"%s: Interrupt Handler Start ..",gDrvrName); for (i = 0; i < 32; i++) {regx = XPCIe_ReadReg(i);printk(KERN_W ARNING"%s Interrupt Handler End ..\n", gDrvrName);}u32 XPCIe_ReadReg (u32 dw_offset){u32 ret = 0;//u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));//ret = readl(reg_addr);ret = readl(gBaseVirt + (4 * dw_offset));return ret;}void XPCIe_WriteReg (u32 dw_offset, u32 val){//u32 reg_addr = (u32)(gBaseVirt + (4 * dw_offset));writel(val, (gBaseVirt + (4 * dw_offset)));}ssize_t* XPCIe_ReadMem(char *buf, size_t count){int ret = 0;dma_addr_t dma_addr;//make sure passed in buffer is large enoughif ( count < BUF_SIZE ) {printk("%s: XPCIe_Read: passed in buffer too small.\n", gDrvrName);ret = -1;goto exit;}down(&gSem[SEM_DMA]);// pci_map_single return the physical address corresponding to// the virtual address passed to it as the 2nd parameterdma_addr = pci_map_single(gDev, gReadBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);if ( 0 == dma_addr ) {printk("%s: XPCIe_Read: Map error.\n",gDrvrName);ret = -1;// Now pass the physical address to the device hardware. This is now// the destination physical address for the DMA and hence the to be// put on Memory Transactions// Do DMA transfer here....printk("%s: XPCIe_Read: ReadBuf Virt Addr = %x Phy Addr = %x.\n",gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);// Unmap the DMA buffer so it is safe for normal access again.pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);up(&gSem[SEM_DMA]);// Now it is safe to copy the data to user space.if ( copy_to_user(buf, gReadBuffer, BUF_SIZE) ) {ret = -1;printk("%s: XPCIe_Read: Failed copy to user.\n",gDrvrName);goto exit;}exit:return ret;}ssize_t XPCIe_WriteMem(const char *buf, size_t count) {int ret = 0;dma_addr_t dma_addr;if ( (count % 4) != 0 ) {printk("%s: XPCIe_Write: Buffer length not dword aligned.\n",gDrvrName);ret = -1;goto exit;}// Now it is safe to copy the data from user space.if ( copy_from_user(gWriteBuffer, buf, count) ) {ret = -1;printk("%s: XPCIe_Write: Failed copy to user.\n",gDrvrName);goto exit;}//set DMA semaphore if in loopback// pci_map_single return the physical address corresponding to// the virtual address passed to it as the 2nd parameterdma_addr = pci_map_single(gDev, gWriteBuffer, BUF_SIZE, PCI_DMA_FROMDEVICE);if ( 0 == dma_addr ) {printk("%s: XPCIe_Write: Map error.\n",gDrvrName);ret = -1;goto exit;}// Now pass the physical address to the device hardware. This is now// the source physical address for the DMA and hence the to be// put on Memory Transactions// Do DMA transfer here....printk("%s: XPCIe_Write: WriteBuf Virt Addr = %x Phy Addr = %x.\n",gDrvrName, (unsigned int)gReadBuffer, (unsigned int)dma_addr);// Unmap the DMA buffer so it is safe for normal access again.pci_unmap_single(gDev, dma_addr, BUF_SIZE, PCI_DMA_FROMDEVICE);up(&gSem[SEM_DMA]);exit:return (ret);}u32 XPCIe_ReadCfgReg (u32 byte) {u32 pciReg;if (pci_read_config_dword(gDev, byte, &pciReg) < 0) {printk("%s: XPCIe_ReadCfgReg: Reading PCI interface failed.",gDrvrName);return (-1);}return (pciReg);}u32 XPCIe_WriteCfgReg (u32 byte, u32 val) {if (pci_write_config_dword(gDev, byte, val) < 0) {printk("%s: XPCIe_Read Device Control: Reading PCI interface failed.",gDrvrName);return (-1);}return 1;}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
else
{
memcpy(fifo_dev->mem,fifo_dev->mem+count,fifo_dev->current_len-count);//数据提前
fifo_dev->current_len-=count;
wake_up_interruptible(&fifo_dev->w_wait);//唤醒写等待队列
struct FIFO_DEV *fifo_dev=filp->private_data;
DECLARE_WAITQUEUE(wait,current);//定义等待队列
down(&fifo_dev->sem);//获取信息量
add_wait_queue(&fifo_dev->r_wait,&wait);//将等待队列加入写等待队列头
ret=count;
}
out:up(&fifo_dev->sem);
out2:remove_wait_queue(&fifo_dev->r_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
unsigned int current_len;//当前缓冲区中字节数
unsigned char mem[SIZE];//内存缓冲区
struct semaphore sem;//信号量
wait_queue_head_t r_wait;//读等待队列头
wait_queue_head_t w_wait;//写等待队列头
wake_up_interruptible(&fifo_dev->r_wait);
//释放异步读通知道信号
if(fifo_dev->async_queue)
{
kill_fasync(&fifo_dev->async_queue,SIGIO,POLL_IN);
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/delay.h>
}
static const struct file_operations fifo_ops=
{
.owner=THIS_MODULE,
.open=fifo_open,
.release=fifo_release,
.read=fifo_read,
.write=fifo_write,
up(&fifo_dev->sem);
schedule();
if(signal_pending(current))
{
ret=-ERESTARTSYS;
goto out2;
{
filp->private_data=fifo_dev;
return 0;
}
static int fifo_release(struct inode *inode,struct file *filp)
{
struct FIFO_DEV *fifo_dev=filp->private_data;
}
else
{
ret=alloc_chrdev_region(&devno,0,1,DEVNAME);
major=MAJOR(devno);
down(&fifo_dev->sem);
poll_wait(filp,&fifo_dev->r_wait,wait);
poll_wait(filp,&fifo_dev->w_wait,wait);
if(fifo_dev->current_len!=0)
{
if(filp->f_flags &O_NONBLOCK)
{
ret=-EAGAIN;
goto out;
}
else
{
__set_current_state(TASK_INTERRUPTIBLE);
}
static ssize_t fifo_write(struct file *filp,char __user *buf,size_t count,loff_t *ppos )
{
int ret;
struct FIFO_DEV *fifo_dev=filp->private_data;
}
if(copy_from_user(fifo_dev->mem+fifo_dev->current_len,buf,count))
{
ret= -EFAULT;
goto out;
}
else
{
fifo_dev->current_len+=count;
}
ret=count;
}
out:up(&fifo_dev->sem);
out2:remove_wait_queue(&fifo_dev->w_wait,&wait);
set_current_state(TASK_RUNNING);
fifo_fasync(-1, filp, 0);//将文件从异步操作中删除
return 0;
}
static ssize_t fifo_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos )
{
int ret=0;
{
ret=-ERESTARTSYS;
goto out2;
}
down(&fifo_dev->sem);
}
}
if(count>fifo_dev->current_len)
{
count=fifo_dev->current_len;
}
if(copy_to_user(buf,fifo_dev->mem,count))//读取数据到用户缓冲区
{
ret= -EFAULT;
goto out;
struct fasync_struct *async_queue;//异步结构体指针
};
struct FIFO_DEV *fifo_dev;
//异步通知函数
static int fifo_fasync(int fd,struct file *filp,int mode)
{
}
down(&fifo_dev-if(count>SIZE-fifo_dev->current_len)
{
count=SIZE-fifo_dev->current_len;
.poll=fifo_poll,
.fasync=fifo_fasync,
};
int __init fifo_init(void)
{
int ret;
dev_t devno=MKDEV(major,0);
if(major)
{
ret=register_chrdev_region(devno,1,DEVNAME);
return ret;
}
static unsigned int fifo_poll(struct file * filp,struct poll_table *wait)
{
int mask=0;
struct FIFO_DEV *fifo_dev=filp->private_data;
DECLARE_WAITQUEUE(wait,current);
down(&fifo_dev->sem);
add_wait_queue(&fifo_dev->w_wait,&wait);
if(fifo_dev->current_len==SIZE)
{
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
{
__set_current_state(TASK_INTERRUPTIBLE);//阻塞当前进程
up(&fifo_dev->sem);//释放信息量
schedule();//调度其它进程
if(signal_pending(current))
#include <asm/uaccess.h>
#define DEVNAME "GLOBAL_FIFO_DEV"//驱动名称