1. MISC设备驱动介绍

MISC设备也叫杂项设备,当板子上的某些外设无法分类时,就可使用MISC设备驱动。所有MISC设备驱动的主设备号都为10,不同设备使用不同的从设备号。MISC设备会自动创建cdev,不需要手动创建,因此采用MISC设备驱动可以简化字符设备驱动的编写

Linux中用miscdevice结构体表示MISC设备,include/linux/miscdevice.h文件 中定义了该结构体:

struct miscdevice {int minor;                          /* 子设备号 */const char *name;                   /* 设备名字 */const struct file_operations *fops; /* 设备操作集 */struct list_head list;struct device *parent;struct device *this_device;const struct attribute_group **groups;const char *nodename;umode_t mode;
};

定义了MISC设备后,需要设置minor、name和fops三个成员变量。minor即子设备号,主设备号固定为10,Linux系统预定义了一些MISC设备的子设备号,定义在include/linux/miscdevice.h文件中,可从中挑选,也可自已定义

#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255

设置好miscdevice后,就需要向系统中注册MISC设备,卸载设备驱动时需要注销掉MISC设备。注册和注销MISC设备使用如下函数

  • misc_register:注册MISC设备,该函数会自动创建设备
int misc_register(struct miscdevice * misc)
//misc:要注册的 MISC 设备
//返回值:负数,失败;0,成功
/*************************************************************/
/*******该函数会自动创建设备,包含了传统设备驱动中的下列步骤*******/
/*************************************************************/
alloc_chrdev_region();  /* 申请设备号 */
cdev_init();            /* 初始化 cdev */
cdev_add();             /* 添加 cdev */
class_create();         /* 创建类 */
device_create();        /* 创建设备 */
  • misc_deregister:注销MISC设备,该函数会自动删除设备
int misc_deregister(struct miscdevice * misc)
//misc:要注销的 MISC 设备
//返回值:负数,失败;0,成功
/*************************************************************/
/*******该函数会自动删除设备,包含了传统设备驱动中的下列步骤*******/
/*************************************************************/
cdev_del();                   /* 删除 cdev */
unregister_chrdev_region();   /* 注销设备号 */
device_destroy();             /* 删除设备 */
class_destroy();              /* 删除类 */

2. MISC设备驱动实验

本实验采用platform+misc 的方式编写beep驱动,采用platform来实现总线、设备和驱动,misc主要负责完成字符设备的创建

2.1 修改设备树

  • 添加pinctrl节点:在iomuxc节点的imx6ul-evk子节点下创建pinctrl_beep节点,复用SNVS_TAMPER1
pinctrl_beep: beepgrp {fsl,pins = <MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01  0x10B0>;
};
//MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 用于设置pin的复用功能
//0x10B0 用于设置pin的电气特性
  • 添加BEEP设备节点:在根节点下创建beep设备节点,设置PIN对应的pinctrl节点,指定所使用的的GPIO
beep {#address-cells = <1>;#size-cells = <1>;compatible = "andyxi-beep";pinctrl-names = "default";pinctrl-0 = <&pinctrl_beep>;led-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;status = "okay";
};
  • 检查PIN是否冲突:检查pinctrl中设置以及设备节点中指定的引脚有没有被别的外设使用

2.2 驱动程序编写

新建miscbeep.c驱动文件,并输入如下内容

  • 宏定义以及外设设备结构体定义
#define MISCBEEP_NAME  "miscbeep" /* 名字  */
#define MISCBEEP_MINOR  144   /* 子设备号 */
#define BEEPOFF    0   /* 关蜂鸣器 */
#define BEEPON     1   /* 开蜂鸣器 */
/* miscbeep设备结构体 */
struct miscbeep_dev{dev_t devid;    /* 设备号   */struct cdev cdev;   /* cdev  */struct class *class;  /* 类   */struct device *device;  /* 设备   */struct device_node *nd;  /* 设备节点 */int beep_gpio;    /* beep所使用的GPIO编号  */
};
struct miscbeep_dev miscbeep;  /* beep设备 */
  • 编写字符设备操作函数集
/* 打开设备 */
static int miscbeep_open(struct inode *inode, struct file *filp){filp->private_data = &miscbeep; /* 设置私有数据 */return 0;
}
/* 向设备写数据 */
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){int retvalue;unsigned char databuf[1];unsigned char beepstat;struct miscbeep_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}beepstat = databuf[0];  /* 获取状态值 */if(beepstat == BEEPON) { gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */} else if(beepstat == BEEPOFF) {gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */}return 0;
}
/* 设备操作函数 */
static struct file_operations miscbeep_fops = {.owner = THIS_MODULE,.open = miscbeep_open,.write = miscbeep_write,
};
  • 定义一个MISC设备,并填充minor、name和fops变量
static struct miscdevice beep_miscdev = {.minor = MISCBEEP_MINOR,.name = MISCBEEP_NAME,.fops = &miscbeep_fops,
};
  • probe函数中,初始化IO,并注册MISC设备(无需单独注册字符设备)
static int miscbeep_probe(struct platform_device *dev){int ret = 0;printk("beep driver and device was matched!\r\n");/* 设置BEEP所使用的GPIO *//* 1、获取设备节点:beep */miscbeep.nd = of_find_node_by_path("/beep");if(miscbeep.nd == NULL) {printk("beep node not find!\r\n");return -EINVAL;} /* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);if(miscbeep.beep_gpio < 0) {printk("can't get beep-gpio");return -EINVAL;}/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */ret = gpio_direction_output(miscbeep.beep_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");} /* 不需要自己注册字符设备驱动,只需要注册misc设备驱动即可 */ret = misc_register(&beep_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}
  • remove函数中,删除MISC设备
static int miscbeep_remove(struct platform_device *dev){/* 注销设备的时候关闭LED灯 */gpio_set_value(miscbeep.beep_gpio, 1);/* 注销misc设备 */misc_deregister(&beep_miscdev);return 0;
}
  • 编写设备匹配列表:注意compatible属性值要与设备节点属性值一致
/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {{ .compatible = "andyxi-beep" },{ /* Sentinel */ }
};
  • 填充platform驱动结构体
static struct platform_driver beep_driver = {.driver = {.name = "imx6ul-beep",          //驱动名字,用于和设备匹配 .of_match_table = beep_of_match,  //设备树匹配表},.probe = miscbeep_probe,.remove = miscbeep_remove,
};
  • 驱动入口和出口函数中,注册和卸载platform驱动
/* 驱动出口函数 */
static int __init miscbeep_init(void){return platform_driver_register(&beep_driver);
}
/* 驱动出口函数 */
static void __exit miscbeep_exit(void){platform_driver_unregister(&beep_driver);
}module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");

2.3 测试程序编写

新建测试文件miscbeepApp.c,并编写程序

#define BEEPOFF 0
#define BEEPON  1int main(int argc, char *argv[]){int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR); /* 打开beep驱动 */if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("BEEP Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

3. 编译测试

  • 编译驱动程序:当前目录下创建Makefile文件,并make编译
KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := miscbeep.obuild: kernel_moduleskernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
  • 编译测试程序:无需内核参与,直接编译即可
arm-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp
  • 将驱动文件和测试文件拷贝至根文件系统的目录rootfs/lib/modules/4.1.15中,加载驱动成功后,总线就会进行匹配
depmod  #第一次加载驱动时,需使用“depmod”命令
modprobe miscbeep.ko

  • 所有misc设备都属于同一类,/sys/class/misc目录下就是misc类的所有设备,每个设备对应一个子目录

  • 使用以下命令打开或者关闭beep蜂鸣器
./miscbeepAPP  /dev/miscbeep  1
./miscbeepAPP  /dev/miscbeep  0

Linux 下的 MISC 设备驱动相关推荐

  1. 什么是 Linux 下的 platform 设备驱动

    Linux下的字符设备驱动一般都比较简单,只是对IO进行简单的读写操作.但是I2C.SPI.LCD.USB等外设的驱动就比较复杂了,需要考虑到驱动的可重用性,以避免内核中存在大量重复代码,为此人们提出 ...

  2. linux下的rtc设备驱动,linux下测试RTC驱动相关的命令date和hwclock常见用法讲解

    之前对Linux下面时间相关的内容,一无所知,第一次见到hwclock,不知为何物,也没找到解释清楚的帖子.故此整理一下,简单介绍Linux下验证rtc驱动是否工作正常,相关的的命令:date和hwc ...

  3. linux下的字符设备驱动

    Linux字符设备驱动程序的一个简单示例 一.开发环境: 主  机:VMWare--Fedora 9 开发板:友善之臂mini2440--256MB Nandflash 编译器:arm-linux-g ...

  4. linux MISC设备驱动

    系列文章 I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版) I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本) linux 字符设备驱动实战 linux LED设备驱动文件 ...

  5. 【linux驱动分析】misc设备驱动

        misc设备驱动.又称混杂设备驱动. misc设备驱动共享一个设备驱动号MISC_MAJOR.它在include\linux\major.h中定义:         #define MISC_ ...

  6. 嵌入式linux MISC设备驱动

    misc 的意思是混合.杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某 些外设无法进行分类的时候就可以使用 MISC 驱动.MISC 驱动其实就是最简单的字符设备驱 动,通常嵌套在 ...

  7. Linux下的USB总线驱动 mouse

    Linux下的USB总线驱动(03)--USB鼠标驱动 usbmouse.c USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12 ...

  8. Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化

    我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...

  9. Linux SPI总线和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能: 1 ...

  10. 南京邮电大学嵌入式系统开发实验5:嵌入式Linux下LED报警灯驱动设计及编程

    实验5  嵌入式Linux下LED报警灯驱动设计及编程 一.实验目的 理解驱动本质,掌握嵌入式Linux系统下驱动开发相关知识,包括端口寄存器访问.接口函数编写.和文件系统挂接.注册及相关应用编程等知 ...

最新文章

  1. canvas转化图片并下载
  2. AI公开课:18.05.16 周明博士(MSRA副院长)—北大AI第十一讲之《语言智能的进展》课堂笔记——你了解语言智能
  3. Linux下 RPM 包和Deb包的安装(代码指令+案列)
  4. Java设计模式1:设计模式概论
  5. oracle 11g 卸载  客户端卸载
  6. linux系统下RocketMQ的集群搭建
  7. 计算机系统的组成doc,简述计算机系统的组成.doc
  8. 思科vrf配置实例_从原理到配置,最全的VLAN说明就在这了!
  9. 工业控制系统(ICS)
  10. ContextCapture(Smart3D)网盘教程 直接下载
  11. Windows 错误报告
  12. 英语句子成分后置定语语法归纳
  13. BSOJ 2927 -- 【模拟试题】保镖排队
  14. 【高自旋和低自旋配合物】
  15. 2022届互联网校招薪资开奖,拼多多最高年薪 75 万!
  16. think-queue
  17. 百度炮轰Google搜索不创新 拟全力进攻云搜索
  18. [安卓]实现苹果实现的效果之 流光溢彩背景
  19. 10万+爆文怎么写?这些软文写作技巧你掌握了吗?
  20. python(C)作业6 P110 2,4,12

热门文章

  1. tenforflow版YOLOv3下VOC数据集的准备和训练
  2. 软件测试薪资高不高?软件测试工资水平调研公布
  3. 看英语书记录的单词-5
  4. 惠普服务器硬盘指示灯不亮或显示蓝色
  5. 《跃迁:成为高手的技术》读后感
  6. 面试方法-麦可利兰的能力素质模型
  7. 四川为何紧急叫停某知名声乐演员计划在川23场次个人巡演活动?
  8. 人工神经网络—神经元的数学模型
  9. 企业微信开发实战(三、OA审批之回调通知、获取审批单号、审批详情)
  10. 电脑鼠标点一下就选很多程序