Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299688
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-05-25 20:16:22

first_drv 笔记  
15年4月27日19:03:23
第一步:自动分配主设备号。
驱动代码如下:
******************************************************************************

1 #include <linux/module.h>

2 #include <linux/kernel.h>

3 #include <linux/fs.h>

4 #include <linux/init.h>

5 #include <linux/delay.h>

6 #include <asm/uaccess.h>

7 #include <asm/irq.h>

8 #include <asm/io.h>

9 #include <asm/arch/regs-gpio.h>

10 #include <asm/hardware.h>

11

12 static int first_drv_open(struct inode *inode, struct file *file){

13 printk("first_drv_open!\n");

14 return 0;

15 }

16

17 static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){

18 printk("first_drv_write\n");

19 return 0;

20 }

21

22 static struct file_operations first_drv_fops = {

23 .owner = THIS_MODULE,

24 .open = first_drv_open,

25 .write = first_drv_write,

26 };

27

28 int major;

29 static int __init first_drv_init(void){

30 major = register_chrdev(0, "first_drv", &first_drv_fops);

31 return 0;

32 }

33

34 static void __exit first_drv_exit(void){

35 unregister_chrdev(major, "first_drv");

36 }

39

40 module_init(first_drv_init);

41 module_exit(first_drv_exit);

42

43 MODULE_LICENSE("GPL");


******************************************************************************
测试程序如下:

(1)驱动程序跟应用程序不同,没有main主函数,当安装设备时,就调用init函数,即29行的函数。当卸载设备的时候,调用exit函数即可。
入口函数init:注册设备,需要知道设备的主设备号,名称,对应的file_operation结构体指针。
同样,出口函数exit中就需要注销设备,知道主设备号,名称即可。

(2)设备不需要知道底层的东西,当要打开东西的时候,它只需要调用open函数即可,至于open函数需要怎么做,就是驱动的任务,咱们则只打印了一句话。

(3)还必须把file_operation这个结构体填充完,字符驱动函数基本就是围绕这个结构体来写的,并带上出入口函数。
======================================================================
第二步:当驱动程序选择自动创建主设备号的时候,主设备号可能一直发生变化,这时候应用程序就必须一直cat /proc/devices来查看主设备号,同时更改应用程序中的设备号。这样做很麻烦。
用busybox创建的根文件系统中有一个mdev机制,它可以根据系统信息自动创建设备结点。下面就是这种机制:
驱动代码:
 

1 #include <linux/module.h>

2 #include <linux/kernel.h>

3 #include <linux/fs.h>

4 #include <linux/init.h>

5 #include <linux/delay.h>

6 #include <asm/uaccess.h>

7 #include <asm/irq.h>

8 #include <asm/io.h>

9 #include <asm/arch/regs-gpio.h>

10 #include <asm/hardware.h>

  11
 12 static struct class *first_drv_class;
 13 static struct class_device *first_drv_class_dev;
 14
 15 static int first_drv_open(struct inode *inode, struct file *file){
 16     printk("first_drv_open!\n");
 17     return 0;
 18 }
 19
 20 static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){
 21     printk("first_drv_write\n");
 22     return 0;
 23 }
 24
 25 static struct file_operations first_drv_fops = {
 26     .owner  =   THIS_MODULE,
 27     .open   =   first_drv_open,
 28     .write  =   first_drv_write,
 29 };
 30
 31 int major;
 32 static int __init first_drv_init(void){
 33     major = register_chrdev(0, "first_drv", &first_drv_fops);
 34
 35     first_drv_class = class_create(THIS_MODULE, "first_drv");
 36
 37     first_drv_class_dev = class_device_create(first_drv_class, NULL, MKDEV(major,0), NULL, "xyz");
 38
 39     return 0;
 40 }
 41
 42 static void __exit first_drv_exit(void){
 43     unregister_chrdev(major, "first_drv");
 44
 45     class_device_unregister(first_drv_class_dev);
 46     class_destroy(first_drv_class);
 47 }
 50
 51 module_init(first_drv_init);
 52 module_exit(first_drv_exit);
 53
 54 MODULE_LICENSE("GPL");
*****************************************************************************
其中修改的信息用阴影标注了。

主要是在出入口函数中定义了一个类,这个类中有一个或多个成员,为这几个成员分配主次设备号,名称等东西。其中用到了MKDEV()这个函数:
    在linux内核中,dev_t类型用来保存主设备号和词设备号,
    #include
    MAJOR(dev_t, dev);                /*获得主设备号*/
    MINOR(dev_t, dev);                /*获得次设备号*/
相反,若要将主设备号和次设备号转换为dev_t类型,可以使用以下宏:
    MKDEV(int major, int minor);

测试程序中,只需把第9行“/dev/xxx”改成“/dev/xyz”即可实现功能。

==================================================================
第三步:点亮熄灭LED
 

1 #include <linux/module.h>

2 #include <linux/kernel.h>

3 #include <linux/fs.h>

4 #include <linux/init.h>

5 #include <linux/delay.h>

6 #include <asm/uaccess.h>

7 #include <asm/irq.h>

8 #include <asm/io.h>

9 #include <asm/arch/regs-gpio.h>

10 #include <asm/hardware.h>

  11
 12 static struct class *first_drv_class;
 13 static struct class_device *first_drv_class_dev;
 14
 15 volatile unsigned long *gpfcon = NULL;
 16 volatile unsigned long *gpfdat = NULL;
 17
 18 static int first_drv_open(struct inode *inode, struct file *file){
 19     //printk("first_drv_open!\n");
 20     *gpfcon &= ~((0x3 << (4*2)) | (0x3 << (5*2)) | (0x3 << (6*2)));
 21     *gpfcon |= ((0x1 << (4*2)) | (0x1 << (5*2)) | (0x1 << (6*2)));
 22     return 0;
 23 }
 24
 25 static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){
 26     //printk("first_drv_write\n");
 27
 28     int val;
 29     copy_from_user(&val, buf, count);
 30
 31     if(val == 1)
 32     {
 33         *gpfdat &= ~ ((1 << 4) | (1 << 5) | (1 << 6));
 34     }
 35     else
 36     {
 37         *gpfdat |= ((1 << 4) | (1 << 5) | (1 << 6));
 38     }
 39     return 0;
 40 }
 41
 42 static struct file_operations first_drv_fops = {
 43     .owner  =   THIS_MODULE,
 44     .open   =   first_drv_open,
 45     .write  =   first_drv_write,
 46 };
 47
 48 int major;
 49 static int __init first_drv_init(void){
 50     major = register_chrdev(0, "first_drv", &first_drv_fops);
 51
 52     first_drv_class = class_create(THIS_MODULE, "first_drv");
 53
 54     first_drv_class_dev = class_device_create(first_drv_class, NULL, MKDEV(major,0), NULL, "xyz");
 55
 56     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
 57     gpfdat = gpfcon + 1;
 58
 59     return 0;
 60 }
 61
 62 static void __exit first_drv_exit(void){
 63     unregister_chrdev(major, "first_drv");
 64
 65     class_device_unregister(first_drv_class_dev);
 66     class_destroy(first_drv_class);
 67     iounmap(gpfcon);
 68 }
 69
 70
 71
 72 module_init(first_drv_init);
 73 module_exit(first_drv_exit);
 74
 75 MODULE_LICENSE("GPL");
其中测试程序如下:

1 #include <sys/types.h>

2 #include <sys/stat.h>

3 #include <fcntl.h>

4 #include <stdio.h>

  5
  6 int main (int argc, char **argv){
  7 int fd;
  8 int val = 1;
  9 fd = open("/dev/xyz",O_RDWR);
 10 if (fd < 0)
 11     printf("cannot open!\n");
 12
 13 if (argc != 2){
 14 printf("Usage:\n");
 15 printf("%s \n", argv[0]);
 16 return 0;
 17 }
 18
 19 if (strcmp(argv[1],"on") == 0)
 20 {
 21 val = 1;
 22 }
 23 else{
 24 val = 0;
 25 }
 26
 27 write (fd, &val, 4);
 28 return 0;
 29 }
出现错误如下:
(1)指针的用法:


在映射的时候,第56 57行的是*gpfcon=(volatile unsigned long *)ioremap(0x56000050, 16);和*gpfdat = *gpfcon + 1;还是gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1;这两者有什么区别??
(2)很粗心的写了一个copy_from_usr,正确的是copy_from_user,在linux中的用户目录为usr,不

要混淆。下图为写错以后,在开发板上面的提示信息。


(3)下图为安装驱动的时候,提示的错误:仔细看这些提示,第一行提示为无法挂载内核NULL点在虚拟内存上面,第七行提示为:PC is at first_drv_init+0x7c/6xc8 [first_drv];可以分析出来是是 first_drv_init里面出错了。


下图是我去查看入口函数的时候截的图,暂时还是不太清楚到底是为什么!!

(4)当感觉没错误的时候,执行却没有现象,后来查看firstdrvtest代码的时候,发现strcmp函数的使用,如果两者相同,则函数值为0,不同的话为1。


注意:在单片机编程的过程中,直接使用物理地址就可以访问寄存器,但是在linux和驱动程序中,需要使用虚拟地址来操作寄存器,这就用到了ioremap函数,将物理地址映射成虚拟地址。

阅读(1467) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~