Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2463
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2023-03-22 17:22
文章分类
文章存档

2023年(5)

我的朋友
最近访客

分类: LINUX

2023-03-22 17:25:40

1:本文只是简单的通过gpio来控制亮灯的程序,我不是大神,但我在努力,请各位多多指教

首先我们要写的是一个驱动,而驱动又必须编译进内核才可以,那怎么样内核才知道你的程序是驱动而不是一般程序呢?没错,我们有现成的宏来干这个事。


点击(此处)折叠或打开

  1. module_init();
  2. module_exit();



如果你问我这些宏是怎么来的,我只能说它们来自内核,而它们的目的就是告诉内核这是个驱动程序,装载是调用module_init();所指定的函数,而卸载是调用module_exit();所指定的函数,同时它们还可以验证函数的加载方式等其它功能。

既然我们使用的是gpio引脚,自然需要它们的地址咯!


点击(此处)折叠或打开

  1. volatile unsigned long *gpfcon = NULL;
  2. volatile unsigned long *gpfdat = NULL;


Volatile:这个关键字的意思就是禁止编译器的优化,我们知道计算机在存储的时候为了存储的速度快,会把某些地址的值拷贝到寄存器中,这样以后直接读取寄存器的值速度会加快,但是有时候我们需要的值变化特别快,或者会在我们不知道的情况下改变它的值,而寄存器的值和内存地址的值可能不同步,那时我们就希望编译器不要优化,直接去读取内存地址的值,这时我们就用到了这个关键字。

上面说到module_init();会指定一个函数作为驱动的入口函数,可是内核没有提供入口函数,所以要我们自己去写

加入入口函数是:static int first_drv_init(void)

此时宏module_init();改为module_init(first_drv_init);这样驱动就知道从哪里开始执行了,

那么入口函数里面我们都做什么呢?

看看内核就会知道,内核给我们提供了一个结构体file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。我们用的是简单的gpio点灯,所以我们用的成员不多,如下只有open和write


点击(此处)折叠或打开

  1. static struct file_operations first_drv_fops = {
  2. .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  3. .open = first_drv_open,
  4. .write    =    first_drv_write,    
  5. };


既然是函数指针,那么函数 first_drv_open就得我们自己去写啦!


点击(此处)折叠或打开

  1. static int first_drv_open(struct inode *inode, struct file *file)
  2. {
  3. //printk("first_drv_open\n");
  4. /* 配置GPF4,5,6为输出 */
  5. *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
  6. *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
  7. return 0;
  8. }


在open函数中我们就是配置了gpio的配置引脚让他们具有对应的功能,具体应该参照原理图来写,这里我们用的是三星的s3c2440的芯片,

接下来就是write操作


点击(此处)折叠或打开

  1. static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  2. {
  3. int val;
  4. //printk("first_drv_write\n");
  5. copy_from_user(&val, buf, count); //    copy_to_user();
  6. if (val == 1)
  7. {
  8. // 点灯
  9. *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
  10. }
  11. else
  12. {
  13. // 灭灯
  14. *gpfdat |= (1<<4) | (1<<5) | (1<<6);
  15. }
  16. return 0;
  17. }


这里主要是对gpio引脚的数据寄存器放入1,0,来控制对应引脚是否为高点平,低电平,

上面说了内核给了我们module_init();module_exit();file_operations,前面的宏自然不用多说,内核自然回去识别,可是我们的file_operations结构体有了我们自己取的名字,内核还认识吗?这里我们就要使用另一个函数了

点击(此处)折叠或打开

  1. register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

这个函数的功能就是告诉内核,我这个驱动的file_operations结构体是first_drv_fops,驱动名字是first_drv0表示自动分配一个设备号,

当然在卸载驱动是我们也要卸载这个结构体

点击(此处)折叠或打开

  1. unregister_chrdev(major, "first_drv"); // 卸载

光这样的话还不行,我们{BANNED}最佳好自己去主动创建节点,不然每次加载驱动都要创建,太麻烦了


点击(此处)折叠或打开

  1. firstdrv_class = class_create(THIS_MODULE, "firstdrv");
  2. firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */


在卸载的时候自然需要销毁节点


点击(此处)折叠或打开

  1. class_device_unregister(firstdrv_class_dev);
  2. class_destroy(firstdrv_class);


细心的人肯定发现了,我们的gpio引脚目前还没有地址呢?来映射一下吧!


点击(此处)折叠或打开

  1. gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
  2. gpfdat = gpfcon + 1;


这样我们{BANNED}最佳简单的驱动就搞定啦,以下是完整驱动和它的测试程序

A:驱动


点击(此处)折叠或打开

  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. static struct class *firstdrv_class;
  12. static struct class_device    *firstdrv_class_dev;
  13. volatile unsigned long *gpfcon = NULL;
  14. volatile unsigned long *gpfdat = NULL;
  15. static int first_drv_open(struct inode *inode, struct file *file)
  16. {
  17. //printk("first_drv_open\n");
  18. /* 配置GPF4,5,6为输出 */
  19. *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
  20. *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
  21. return 0;
  22. }
  23. static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  24. {
  25. int val;
  26. //printk("first_drv_write\n");
  27. copy_from_user(&val, buf, count); //    copy_to_user();
  28. if (val == 1)
  29. {
  30. // 点灯
  31. *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
  32. }
  33. else
  34. {
  35. // 灭灯
  36. *gpfdat |= (1<<4) | (1<<5) | (1<<6);
  37. }
  38. return 0;
  39. }
  40. static struct file_operations first_drv_fops = {
  41. .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  42. .open = first_drv_open,
  43. .write    =    first_drv_write,    
  44. };
  45. int major;
  46. static int first_drv_init(void)
  47. {
  48. major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
  49. firstdrv_class = class_create(THIS_MODULE, "firstdrv");
  50. firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
  51. gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
  52. gpfdat = gpfcon + 1;
  53. return 0;
  54. }
  55. static void first_drv_exit(void)
  56. {
  57. unregister_chrdev(major, "first_drv"); // 卸载
  58. class_device_unregister(firstdrv_class_dev);
  59. class_destroy(firstdrv_class);
  60. iounmap(gpfcon);
  61. }
  62. module_init(first_drv_init);
  63. module_exit(first_drv_exit);
  64. MODULE_LICENSE("GPL");


B:测试


点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. /* firstdrvtest on
  6. * firstdrvtest off
  7. */
  8. int main(int argc, char **argv)
  9. {
  10. int fd;
  11. int val = 1;
  12. fd = open("/dev/xyz", O_RDWR);
  13. if (fd < 0)
  14. {
  15. printf("can't open!\n");
  16. }
  17. if (argc != 2)
  18. {
  19. printf("Usage :\n");
  20. printf("%s \n", argv[0]);
  21. return 0;
  22. }
  23. if (strcmp(argv[1], "on") == 0)
  24. {
  25. val = 1;
  26. }
  27. else
  28. {
  29. val = 0;
  30. }
  31. write(fd, &val, 4);
  32. return 0;
  33. }
阅读(187) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:_IO, _IOR, _IOW, _IOWR 宏的用法与解析

给主人留下些什么吧!~~