alloc_chrdev_region(alloc_chrdev_region形参含义)
如何系统的学习Linux驱动开发
在学习之前一直对驱动开发非常的陌生,感觉有点神秘。不知道驱动开发和普通的程序开发究竟有什么不同;它的基本框架又是什么样的;他的开发环境有什么特殊的地方;以及怎么写编写一个简单的字符设备驱动前编译加载,下面我就对这些问题一个一个的介绍。
一、驱动的基本框架
1.??那么究竟什么是驱动程序,它有什么用呢:
l?????驱动是硬件设备与应用程序之间的一个中间软件层
l?它使得某个特定硬件能够响应一个定义良好的内部编程接口,同时完全隐蔽了设备的工作细节
l?????用户通过一组与具体设备无关的标准化的调用来完成相应的操作
l?驱动程序的任务就是把这些标准化的系统调用映射到具体设备对于实际硬件的特定操作上
l?????驱动程序是内核的一部分,可以使用中断、DMA等操作
l?????驱动程序在用户态和内核态之间传递数据
2.??Linux驱动的基本框架
3.??Linux下设备驱动程序的一般可以分为以下三类
1)????????字符设备
a)?????????所有能够象字节流一样访问的设备都通过字符设备来实现
b)????????它们被映射为文件系统中的节点,通常在/dev/目录下面
c)????????一般要包含open read write close等系统调用的实现
2)????????块设备
d)????????通常是指诸如磁盘、内存、Flash等可以容纳文件系统的存储设备。
e)?????????块设备也是通过文件系统来访问,与字符设备的区别是:内核管理数据的方式不同
f)?????????它允许象字符设备一样以字节流的方式来访问,也可一次传递任意多的字节。
3)????????网络接口设备
g)????????通常它指的是硬件设备,但有时也可能是一个软件设备(如回环接口loopback),它们由内核中网络子系统驱动,负责发送和接收数据包。
h)????????它们的数据传送往往不是面向流的,因此很难将它们映射到一个文件系统的节点上。
二、怎么搭建一个驱动的开发环境
因为驱动是要编译进内核,在启动内核时就会驱动此硬件设备;或者编译生成一个.o文件,?当应用程序需要时再动态加载进内核空间运行。因此编译任何一个驱动程序都要链接到内核的源码树。所以搭建环境的第一步当然是建内核源码树
1.???????怎么建内核源码树
a)?首先看你的系统有没有源码树,在你的/lib/ modules目录下会有内核信息,比如我当前的系统里有两个版本:
#ls /lib/ modules
2.6.15-rc7??2.6.21-1.3194.fc7
查看其源码位置:
## ll /lib/modules/2.6.15-rc7/build
lrwxrwxrwx 1 root root 27 2008-04-28 19:19 /lib/modules/2.6.15-rc7/build - /root/xkli/linux-2.6.15-rc7
发现build是一个链接文件,其所对应的目录就是源码树的目录。但现在这里目标目录已经是无效的了。所以得自己重新下载
b)下载并编译源码树
有很多网站上可以下载,但官方网址是:
下载完后当然就是解压编译了
# tar –xzvf linux-2.6.16.54.tar.gz
#cd linux-2.6.16.54
## make menuconfig (配置内核各选项,如果没有配置就无法下一步编译,这里可以不要改任何东西)
#make
…
如果编译没有出错。那么恭喜你。你的开发环境已经搭建好了
三、了解驱动的基本知识
1.?????????设备号
1)????????什么是设备号呢?我们进系统根据现有的设备来讲解就清楚了:
#ls -l /dev/
crwxrwxrwx 1 root root?????1,???3 2009-05-11 16:36 null
crw------- 1 root root?????4,???0 2009-05-11 16:35 systty
crw-rw-rw- 1 root tty??????5,???0 2009-05-11 16:36 tty
crw-rw---- 1 root tty??????4,???0 2009-05-11 16:35 tty0
在日期前面的两个数(如第一列就是1,3)就是表示的设备号,第一个是主设备号,第二个是从设备号
2)????????设备号有什么用呢?
l?传统上,?主编号标识设备相连的驱动.?例如, /dev/null?和?/dev/zero?都由驱动?1?来管理,?而虚拟控制台和串口终端都由驱动?4?管理
l?次编号被内核用来决定引用哪个设备.?依据你的驱动是如何编写的自己区别
3)????????设备号结构类型以及申请方式
l???在内核中, dev_t?类型(在?中定义)用来持有设备编号,?对于?2.6.0?内核, dev_t?是?32?位的量, 12?位用作主编号, 20?位用作次编号.
l???能获得一个?dev_t?的主或者次编号方式:
MAJOR(dev_t dev); //主要
MINOR(dev_t dev);//次要
l???但是如果你有主次编号,?需要将其转换为一个?dev_t,?使用: MKDEV(int major, int minor);
4)????????怎么在程序中分配和释放设备号
在建立一个字符驱动时需要做的第一件事是获取一个或多个设备编号来使用.?可以达到此功能的函数有两个:
l???????一个是你自己事先知道设备号的
register_chrdev_region,?在?中声明:
int register_chrdev_region(dev_t first, unsigned int count, char *name);
first?是你要分配的起始设备编号. first?的次编号部分常常是?0,count?是你请求的连续设备编号的总数. name?是应当连接到这个编号范围的设备的名子;?它会出现在?/proc/devices?和?sysfs?中.
l???????第二个是动态动态分配设备编号
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
使用这个函数, dev?是一个只输出的参数,?它在函数成功完成时持有你的分配范围的第一个数. fisetminor?应当是请求的第一个要用的次编号;?它常常是?0. count?和?name?参数如同给?request_chrdev_region?的一样.
5)????????设备编号的释放使用
不管你是采用哪些方式分配的设备号。使用之后肯定是要释放的,其方式如下:
void unregister_chrdev_region(dev_t first, unsigned int count);
6)
2.?????????驱动程序的二个最重要数据结构
1)?????????file_operation
倒如字符设备scull的一般定义如下:
struct file_operations scull_fops = {
.owner =? THIS_MODULE,?
?.llseek =? scull_llseek,?
?.read =? scull_read,?
?.write =? scull_write,?
?.ioctl =? scull_ioctl,?
?.open =? scull_open,?
?.release =? scull_release,??
};
file_operation也称为设备驱动程序接口
定义在?,?是一个函数指针的集合.?每个打开文件(内部用一个?file?结构来代表)与它自身的函数集合相关连(?通过包含一个称为?f_op?的成员,?它指向一个?file_operations?结构).?这些操作大部分负责实现系统调用,?因此,?命名为?open, read,?等等
2)?????????File
定义位于include/fs.h
struct file结构与驱动相关的成员
l?????????mode_t f_mode??????标识文件的读写权限
l?????????loff_t f_pos???????????当前读写位置
l?????????unsigned int_f_flag?文件标志,主要进行阻塞/非阻塞型操作时检查
l?????????struct file_operation * f_op??文件操作的结构指针
l?????????void * private_data?驱动程序一般将它指向已经分配的数据
l?????????struct dentry* f_dentry??文件对应的目录项结构
3.?????????字符设备注册
1)????????内核在内部使用类型?struct cdev?的结构来代表字符设备.?在内核调用你的设备操作前,?必须编写分配并注册一个或几个这些结构.?有?2?种方法来分配和初始化一个这些结构.
l?????????????如果你想在运行时获得一个独立的?cdev?结构,可以这样使用:
struct cdev *my_cdev = cdev_alloc();
my_cdev-ops = my_fops;
l?????????????如果想将?cdev?结构嵌入一个你自己的设备特定的结构;?你应当初始化你已经分配的结构,?使用:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
2)????????一旦?cdev?结构建立,?最后的步骤是把它告诉内核,?调用:
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
说明:dev?是?cdev?结构, num?是这个设备响应的第一个设备号, count?是应当关联到设备的设备号的数目.?常常?count?是?1,?但是有多个设备号对应于一个特定的设备的情形.
3)????????为从系统去除一个字符设备,?调用:
void cdev_del(struct cdev *dev);
4.?????????open?和?release
字符设备中的几个函数分析
1.在内核中, dev_t 类型(在 linux/types.h中定义)用来持有设备编号 — 主次部分都包括.其中dev_t 是 32 位的量, 12 位用作主编号, 20 位用作次编号
1 #ifndef _LINUX_TYPES_H
2 #define _LINUX_TYPES_H
3
4 #include asm/types.h
5
6 #ifndef __ASSEMBLY__
7 #ifdef __KERNEL__
8
9 #define DECLARE_BITMAP(name,bits) /
10 unsigned long name[BITS_TO_LONGS(bits)]
11
12 #endif
13
14 #include linux/posix_types.h
15
16 #ifdef __KERNEL__
17
18 typedef __u32 __kernel_dev_t;
19
20 typedef __kernel_fd_set fd_set;
21 typedef __kernel_dev_t dev_t; //用来持有设备编号的主次部分
22 typedef __kernel_ino_t ino_t;
23 typedef __kernel_mode_t mode_t;
...
2.在 linux/kdev_t.h中的一套宏定义. 为获得一个 dev_t 的主或者次编号, 使用:
2.1设备编号的内部表示
MAJOR(dev_t dev);
MINOR(dev_t dev);
2.在有主次编号时, 需要将其转换为一个 dev_t, 可使用:
MKDEV(int major, int minor);
在linux/kdev_t.h中有下了内容
...
4 #define MINORBITS 20
5 #define MINORMASK ((1U MINORBITS) - 1)
6
7 #define MAJOR(dev) ((unsigned int) ((dev) MINORBITS))
8 #define MINOR(dev) ((unsigned int) ((dev) MINORMASK))
9 #define MKDEV(ma,mi) (((ma) MINORBITS) | (mi))//高12为表示主设备号,低20位表示次设备号
...
3.分配和释放设备编号register_chrdev_region函数
下面摘自文件fs/char_dev.c内核源代码
184 /**
185 * register_chrdev_region() - register a range of device numbers
186 * @from: the first in the desired range of device numbers; must include
187 * the major number.
188 * @count: the number of consecutive device numbers required
189 * @name: the name of the device or driver.
190 *
191 * Return value is zero on success, a negative error code on failure.
192 */
193 int register_chrdev_region(dev_t from, unsigned count, const char *name)
194 {
195 struct char_device_struct *cd;
196 dev_t to = from + count; //计算分配号范围中的最大值1280+400=1680
197 dev_t n, next;
198
199 for (n = from; n to; n = next) {/*每次申请256个设备号*/
200 next = MKDEV(MAJOR(n)+1, 0);/*主设备号加一得到的设备号,次设备号为0*/
201 if (next to)
202 next = to;
203 cd = __register_chrdev_region(MAJOR(n), MINOR(n),
204 next - n, name);
205 if (IS_ERR(cd))
206 goto fail;
207 }
208 return 0;
209 fail:/*当一次分配失败的时候,释放所有已经分配到地设备号*/
210 to = n;
211 for (n = from; n to; n = next) {
212 next = MKDEV(MAJOR(n)+1, 0);
213 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
214 }
215 return PTR_ERR(cd);
216 }
这里, from是要分配的起始设备编号. from 的次编号部分常常是 0, 但是没有要求是那个效果. count是你请求的连续设备编号的总数. 注意, 如果count 太大, 要求的范围可能溢出到下一个次编号;但是只要要求的编号范围可用, 一切都仍然会正确工作. 最后, name 是应当连接到这个编号范围的设备的名子; 它会出现在 /proc/devices 和 sysfs 中.如同大部分内核函数, 如果分配成功进行, register_chrdev_region 的返回值是 0. 出错的情况下, 返回一个负的错误码, 不能存取请求的区域.
4.下面是char_device_struct结构体的信息
fs/char_dev.c
static struct char_device_struct {
struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针
unsigned int major; // 主设备号
unsigned int baseminor; // 起始次设备号
int minorct; // 设备编号的范围大小
const char *name; // 处理该设备编号范围内的设备驱动的名称
struct file_operations *fops; // 没有使用
struct cdev *cdev; /* will die指向字符设备驱动程序描述符的指针*/
} *chrdevs[MAX_PROBE_HASH];
80 /*
81 * Register a single major with a specified minor range.
82 *
83 * If major == 0 this functions will dynamically allocate a major and return
84 * its number.
85 *
86 * If major 0 this function will attempt to reserve the passed range of
87 * minors and will return zero on success.
88 *
89 * Returns a -ve errno on failure.
90 */
/**
* 该函数主要是注册注册注册主设备号和次设备号
* major == 0此函数动态分配主设备号
* major 0 则是申请分配指定的主设备号
* 返回0表示申请成功,返 回负数说明申请失败
*/
91 static struct char_device_struct *
92 __register_chrdev_region(unsigned int major, unsigned int baseminor,
93 int minorct, const char *name)
94 {/*以下处理char_device_struct变量的初始化和注册*/
95 struct char_device_struct *cd, **cp;
96 int ret = 0;
97 int i;
98 //kzalloc()分配内存并且全部初始化为0,
99 cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
100 if (cd == NULL)
//ENOMEM定义在include/asm-generic/error-base.h中,
//15 #define ENOMEM 12 /* Out of memory */
101 return ERR_PTR(-ENOMEM);
102
103 mutex_lock(chrdevs_lock);
104
105 /* temporary */
106 if (major == 0) {//下面动态申请主设备号
107 for (i = ARRAY_SIZE(chrdevs)-1; i 0; i—) {
//ARRAY_SIZE是定义为ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
//#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
108if (chrdevs[i] == NULL)
//chrdevs是内核中已经注册了的设备好设备的一个数组
109 break;
110 }
111
112 if (i == 0) {
113 ret = -EBUSY;
114 goto out;
115 }
116 major = i;
117 ret = major;//这里得到一个位使用的设备号
118 }
119 //下面四句是对已经申请到的设备数据结构进行填充
120 cd-major = major;
121 cd-baseminor = baseminor;
122 cd-minorct = minorct;/*申请设备号的个数*/
123 strlcpy(cd-name, name, sizeof(cd-name));
124/*以下部分将char_device_struct变量注册到内核*/
125 i = major_to_index(major);
126
127 for (cp = chrdevs[i]; *cp; cp = (*cp)-next)
128 if ((*cp)-major major || //chardevs[i]设备号大于主设备号
129 ((*cp)-major == major
130 (((*cp)-baseminor = baseminor) || //chardevs[i]主设备号等于主设备号,并且此设备号大于baseminor
131 ((*cp)-baseminor + (*cp)-minorct baseminor))))
132 break;
133 //在字符设备数组中找到现在注册的设备
134 /* Check for overlapping minor ranges. */
135 if (*cp (*cp)-major == major) {
136 int old_min = (*cp)-baseminor;
137 int old_max = (*cp)-baseminor + (*cp)-minorct - 1;
138 int new_min = baseminor;
139 int new_max = baseminor + minorct - 1;
140
141 /* New driver overlaps from the left. */
142 if (new_max = old_min new_max = old_max) {
143 ret = -EBUSY;
144 goto out;
145 }
146
147 /* New driver overlaps from the right. */
148 if (new_min = old_max new_min = old_min) {
149 ret = -EBUSY;
150 goto out;
151 }
152 }
153 /*所申请的设备好号能够满足*/
154 cd-next = *cp;/*按照主设备号从小到大顺序排列*/
155 *cp = cd;
156 mutex_unlock(chrdevs_lock);
157 return cd;
158 out:
159 mutex_unlock(chrdevs_lock);
160 kfree(cd);
161 return ERR_PTR(ret);
162 }
以上程序大体上分为两个步骤:
1.char_device_struct类型变量的分配以及初始化94~123行
2.将char_device_struct变量注册到内核,12行页到162行
1.char_device_struct类型变量的分配以及初始化
(1)首先,调用 kmalloc 分配一个 char_device_struct 变量cd。
检查返回值,进行错误处理。
(2)将分配的char_device_struct变量的内存区清零memset。
(3)获取chrdevs_lock读写锁,并且关闭中断,禁止内核抢占,write_lock_irq。
(4)如果传入的主设备号major不为0,跳转到第(7)步。
(5)这时,major为0,首先需要分配一个合适的主设备号。
将 i 赋值成 ARRAY_SIZE(chrdevs)-1,其中的 chrdevs 是包含有256个char_device_struct *类型的数组,
然后递减 i 的值,直到在chrdevs数组中出现 NULL。当chrdevs数组中不存在空值的时候,
ret = -EBUSY; goto out;
(6)到达这里,就表明主设备号major已经有合法的值了,接着进行char_device_struct变量的初始化。
设置major, baseminor, minorct以及name。
2.将char_device_struct变量注册到内核
(7)将 i 赋值成 major_to_index(major)
将major对256取余数,得到可以存放char_device_struct在chrdevs中的索引
(8)进入循环,在chrdevs[i]的链表中找到一个合适位置。
退出循环的条件:
(1)chrdevs[i]为空。
(2)chrdevs[i]的主设备号大于major。
(3)chrdevs[i]的主设备号等于major,但是次设备号大于等于baseminor。
注意:cp = (*cp)-next,cp是char_device_struct **类型,(*cp)-next是一个char_device_struct *
类型,所以(*cp)-next,就得到一个char_device_struct **,并且这时候由于是指针,所以
对cp赋值,就相当于对链表中的元素的next字段进行操作。
(9)进行冲突检查,因为退出循环的情况可能造成设备号冲突(产生交集)。
如果*cp不空,并且*cp的major与要申请的major相同,此时,如果(*cp)-baseminor baseminor + minorct,
就会发生冲突,因为和已经分配了的设备号冲突了。出错就跳转到ret = -EBUSY; goto out;
(10)到这里,内核可以满足设备号的申请,将cd链接到链表中。
(11)释放chrdevs_lock读写锁,开中断,开内核抢占。
(12)返回加入链表的char_device_struct变量cd。
(13)out出错退出
a.释放chrdevs_lock读写锁,开中断,开内核抢占。
b.释放char_device_struct变量cd,kfree。
c.返回错误信息
下面程序出自fs/char_dev.c
动态申请设备号
...
218 /**
219 * alloc_chrdev_region() - register a range of char device numbers
220 * @dev: output parameter for first assigned number
221 * @baseminor: first of the requested range of minor numbers
222 * @count: the number of minor numbers required
223 * @name: the name of the associated device or driver
224 *
225 * Allocates a range of char device numbers. The major number will be
226 * chosen dynamically, and returned (along with the first minor number)
227 * in @dev. Returns zero or a negative error code.
228 */
229 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
230 const char *name)
231 {
/* dev:
仅仅作为输出参数,成功分配后将保存已分配的第一个设备编号。
baseminor:
被请求的第一个次设备号,通常是0。
count:
所要分配的设备号的个数。
name:
和所分配的设备号范围相对应的设备名称。
b.返回值:
成功返回0,失败返回负的错误编码
*/
232 struct char_device_struct *cd;
233 cd = __register_chrdev_region(0, baseminor, count, name);
234 if (IS_ERR(cd))
235 return PTR_ERR(cd);
236 *dev = MKDEV(cd-major, cd-baseminor);
237 return 0;
238 }
239
...
如何利用共享内存 实现rpc调用
关于RPC 路由器的实现主要分布在smd_rpcrouter.c、smd_rpcrouter_device.c、smd_rpcrouter_servers.c、smd_rpcrouter_clients.c等文件中。RPC路由器起着RPC服务器查询、RPC服务器和RPC客户端的注册和销毁,以及底层通信的封装功能,类似于TCP协议。
在实际的实现中,RPC路由器和RPC服务器均是作为一个虚拟的字符型设备来存在的。
下面是RPC路由器的创建过程:
代码3-11 RPC 路由器的创建过程
int msm_rpcrouter_init_devices(void)
{
int rc;
int major;
msm_rpcrouter_class=class_create(THIS_MODULE,
"oncrpc"); //创建设备节点
if (IS_ERR(msm_rpcrouter_class)) {
rc=-ENOMEM;
printk(KERN_ERR
"rpcrouter: failed to create oncrpc class\n");
goto fail;
}
rc=alloc_chrdev_region(msm_rpcrouter_devno,
0, //作为字符型设备分配资源
RPCROUTER_MAX_REMOTE_SERVERS + 1, "oncrpc");
if (rc0) {
printk(KERN_ERR
"rpcrouter: Failed to alloc chardev region (%d)\n", rc);
goto fail_destroy_class;
}
major=MAJOR(msm_rpcrouter_devno);
rpcrouter_device=device_create(msm_
rpcrouter_class, NULL, //创建设备
msm_rpcrouter_devno, NULL, "%.8x:%d",
0, 0);
if (IS_ERR(rpcrouter_device)) {
rc=-ENOMEM;
goto fail_unregister_cdev_region;
}
cdev_init(rpcrouter_cdev, rpcrouter_
router_fops); //字符型设备初始化
rpcrouter_cdev.owner=THIS_MODULE;
rc=cdev_add(rpcrouter_cdev, msm_rpcrouter_devno, 1);
if (rc0)
goto fail_destroy_device;
return 0;
fail_destroy_device:
device_destroy(msm_rpcrouter_class,
msm_rpcrouter_devno);//销毁设备
fail_unregister_cdev_region:
unregister_chrdev_region(msm_rpcrouter_devno, //去注册
RPCROUTER_MAX_REMOTE_SERVERS + 1);
fail_destroy_class:
class_destroy(msm_rpcrouter_class);
fail:
return rc;
}
android gpio 怎么分配
驱动程序初始化和退出
static int simple_major = 250;//默认的设备号码,如果为0则尝试自动分配
……
/*
* Set up the cdev structure for a device.
*/
static void simple_setup_cdev(struct cdev *dev, int minor,
struct file_operations *fops)//自编的函数,注册字符设备
{
int err, devno = MKDEV(simple_major, minor);//建立设备号
cdev_init(dev, fops);//初始化设备结构体struct cdev *dev
dev-owner = THIS_MODULE;
dev-ops = fops;//关联fops
err = cdev_add (dev, devno, 1);//注册一个字符设备
/* Fail gracefully if need be */
if (err)//注册失败处理
printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
}
/*
* Our various sub-devices.
*/
/* Device 0 uses remap_pfn_range */
static struct file_operations simple_remap_ops = { //定义设备的fops
.owner = THIS_MODULE,
.open = simple_open,
.release = simple_release,
.read = simple_read,
.write = simple_write,
.ioctl = simple_ioctl,
};
/*
* We export two simple devices. There's no need for us to maintain any
* special housekeeping info, so we just deal with raw cdevs.
*/
static struct cdev SimpleDevs;
/*
* Module housekeeping.
*/
static struct class *my_class;
static int simple_init(void)
{
int result;
dev_t dev = MKDEV(simple_major, 0);//将设备号转化为dev_t的结构
/* Figure out our device number. */
if (simple_major)
result = register_chrdev_region(dev, 1, "simple");//尝试申请主设备号
else {
result = alloc_chrdev_region(dev, 0, 1, "simple");//请求自动分配主设备号,起始值是0,总共分配1个,设备名simple
simple_major = MAJOR(dev);//将分配成功的设备号保存在simple_major变量中
}
if (result 0) {//分配主设备号失败
printk(KERN_WARNING "simple: unable to get major %d\n", simple_major);
return result;
}
if (simple_major == 0)//将返回值记录为主设备号。需要么?
simple_major = result;
/* Now set up two cdevs. */
simple_setup_cdev(SimpleDevs, 0, simple_remap_ops);//调用自编的函数注册字符设备,有Bug没有返回注册是否成功。
printk("simple device installed, with major %d\n", simple_major);//Bug:打印前应该检查注册是否成功?
my_class= class_create(THIS_MODULE, "simple");//建立一个叫simple的内核class,目的是下一步创建设备节点文件
device_create(my_class, NULL, MKDEV(simple_major, 0),
NULL, "led");//创建设备节点文件
return 0;
}
static void simple_cleanup(void)
{
cdev_del(SimpleDevs);//删除字符设备
unregister_chrdev_region(MKDEV(simple_major, 0), 1);//注销主设备号
device_destroy(my_class,MKDEV(simple_major,0));//删除设备节点
printk("simple device uninstalled\n");
}
module_init(simple_init);
module_exit(simple_cleanup);
如何利用Zynq-7000的PL和PS进行交互
在Zynq-7000上编程PL大致有3种方法:
1. 用FSBL,将bitstream集成到boot.bin中
2. 用U-BOOT命令
3. 在Linux下用xdevcfg驱动。
步骤:
1. 去掉bitstream的文件头
用FSBL烧写PL Images没有什么好说的,用Xilinx SDK的Create Boot Image工具即可完成,不再赘述。用后两种方法需要把bitstream文件的文件头用bootgen工具去掉。
一个典型的bif文件如下所示:
the_ROM_image:
{
[bootloader]fsbl_name.elf
pl_bitstream_name.bit
u-boot_name.elf
}
bif文件可以用文本编辑器写,也可以用Xilinx SDK的Create Boot Image工具生成。然后在命令行下用以下命令即可去掉bitstream文件的文件头。
bootgen -image bootimage.bif -split bin -o i BOOT.BIN
"-split”参数可以生成以下文件:
pl_bitstream_name.bit.bin
2. 在U-BOOT下烧写PL Image
命令”fpga load”和”fpga loadb”都可以。区别是前一个命令接受去掉了文件头的bitstream文件,后一个命令接受含有文件头的bitstream文件。
在OSL 2014.2上,缺省编译就可以完整支持写入PL Image的功能。但是在Petalinux 2013.10下,尽管可以在U-BOOT下看到命令”fpga”,还需要在文件
PROJ/subsystems/linux/configs/u-boot/platform-top.h 中增加以下内容后重新编译才可以支持具体的功能。
/* Enable the PL to be downloaded */
#define CONFIG_FPGA
#define CONFIG_FPGA_XILINX
#define CONFIG_FPGA_ZYNQPL
#define CONFIG_CMD_FPGA
#define CONFIG_FPGA_LOADFS
在OSL 2014.2 U-BOOT中,具体的功能是在zynqpl.c的zynq_load()中实现的。
3. 在Linux下烧写PL Image
OSL Linux 2014.2.01中已经含有xdevcfg驱动了(之前就有,不过本文是在这个版本上验证的),直接用以下命令就可以完成PL Image写入。
cat path_to_storage_media/pl_bitstream_name.bit.bin /dev/xdevcfg
Linux驱动的源代码在xilinx_devcfg.c中。因为驱动的编号是通过alloc_chrdev_region()动态分配的,所以不需要手工用mknod命令手动建立设备节点。
在Linux驱动中,每次往DevCfg中写入4096字节,直到全部写完。
4. 在用户程序中烧写PL Image
目前没有现成的源码来完成这个功能,不过可以用mmap()把DevCfg的寄存器映射到用户程序的虚地址中,然后参考一些现成的软件代码来完成这个功能:
* FSBL中的pcap.c
* U-BOOT中的zynqpl.c
* Linux中的xilinx_devcfg.c
* Xilinx SDK中的例子。例子位于以下位置,随SDK的版本会有变化。
C:\Xilinx\SDK\2014.1\data\embeddedsw\XilinxProcessorIPLib\drivers\devcfg_v3_0\examples\index.html
小结:
DevCfg外设内部有自己的DMA,只需要简单的配置PL Image的基地址和长度到DevCfg寄存器,就可以完成Zynq-7000 PL Image的加载。Xilinx已经提供了灵活的解决方案,如果开发者要把这个功能集成在自己的应用程序中,也有很多的代码可以参考,并不是很困难的任务。
编写字符驱动时出了问题: linux驱动测试程序打不开驱动设备文件 。求指导!谢谢了!
应用层默认open函数定义:int open( const char * pathname,int flags);
驱程序般open函数定义:int(*open)(struct inode *inode, struct file *filp)
LED点亮应用层主函数
[code=C/C++][/code]void main(void)
{
int testdev;
int i;
char buf[10];
testdev = open ("/dev/test",O_RDWR); //应该用应用层默认Open函数吧
if(testdev == -1)
{
printf("Cann't open file...../n");
exit(0);
}
printf("buf = 0x%x/n",buf);
read(testdev,buf,10); //功opentestdev值0能够file文件描述符呢
write(testdev,buf,1);
led_ctl.port='G';
led_ctl.bit=5;
led_ctl.value=0;
ioctl(testdev,GPIO_IO_SET_GPG,led_ctl);
printf("%s",buf);
pause();
close(testdev);
}
我疑问:
1.驱程序open函数:int(*open)(struct inode *inode, struct file *filp)
哪使用啊
2.驱open函数般用做啊
3.文代码功opentestdev值0能够file文件描述符呢
才始习Linux设备驱发借本书代码没注释面驱意思懂希望各位神指点指点万谢
面都我编写字符设备驱代码请问static int read_test()函数始static void release_test()函数参数内部代码意义
万谢
采纳前加100谢谢
#include stdio.h
#include string.h
#include stdlib.h
#include linux/types.h
#include linux/fs.h
#include linux/mm.h
#include linux/errno.h
#include asm/segment.h
#include asm/uaccess.h
#include linux/module.h
unsigned int test_major = 0;
static int read_test(struct inode *node,struct file *fle,char *buf,int count)
{
int left;
if (access_ok(VERIFY_WRITE, buf, count))
{
for (left = count; left 0; left--)
{
_put_user('a',buf);
buf ++;
}
}
return count;
}
static int write_test(struct inode *node, struct file *fle, char *buf, int count)
{
return count;
}
static int open_test(struct inode *inode,struct file *file)
{
try_module_get(THIS_MODULE);
return 0;
}
static void release_test(struct inode *inode, struct file *file)
{
module_put(THIS_MODULE);
}
struct file_operations test_fops =
{
.owner = THIS_MODULE,
.read = read_test,
.write = write_test,
.open = open_test,
.release = release_test,
};
int init_module()
{
int result;
result = register_chrdev(0,"test,test_fops");
if (result 0)
{
printk(KERN_INFO "dddddddddddddddd");
return result;
}
return 0;
}