转自 https://edu.csdn.net/lecturer/505 朱老师物联网大讲堂
《5.linux驱动开发-第6部分-5.6.misc类设备与蜂鸣器驱动》

第一部分、章节目录
5.6.1.板载蜂鸣器驱动测试
5.6.2.misc类设备介绍
5.6.3.misc驱动框架源码分析1
5.6.4.misc驱动框架源码分析2
5.6.5.蜂鸣器驱动源码分析1
5.6.6.蜂鸣器驱动源码分析2

第二部分、章节介绍
5.6.1.板载蜂鸣器驱动测试
本节使用内核中提供的蜂鸣器驱动来实践测试蜂鸣器,要同时解决驱动层的问题和应用程序的编写。
5.6.2.misc类设备介绍
本节系统介绍misc类设备的概念和分类特点,并且对misc类设备的驱动框架详细讲解。
5.6.3.misc驱动框架源码分析1
本节分析misc类设备驱动框架中内核自己实现的部分,即misc.c。
5.6.4.misc驱动框架源码分析2
本节继续分析misc.c,重点是信号量及其使用的一般技巧。
5.6.5.蜂鸣器驱动源码分析1
本节分析九鼎提供的buzzer驱动源码
5.6.6.蜂鸣器驱动源码分析2
本节分析九鼎提供的buzzer驱动源码

第三部分、随堂记录
5.6.1.板载蜂鸣器驱动测试
5.6.1.1、驱动部分
(1)九鼎移植内核已经提供了蜂鸣器驱动源码
(2)make menuconfig
(3)bug排查。修改Makefile中的宏名,最终可以在系统中看到 /dev/buzzer
5.6.1.2、应用部分
(1)应用编写:打开文件+ioctl
(2)测试实践

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>#define DEVNAME     "/dev/buzzer"#define PWM_IOCTL_SET_FREQ       1
#define PWM_IOCTL_STOP          0int main(void)
{int fd = -1;fd = open(DEVNAME, O_RDWR);if (fd < 0){perror("open");return -1;}ioctl(fd, PWM_IOCTL_SET_FREQ, 10000);sleep(3);ioctl(fd, PWM_IOCTL_STOP);sleep(3);ioctl(fd, PWM_IOCTL_SET_FREQ, 3000);sleep(3);ioctl(fd, PWM_IOCTL_STOP);sleep(3);close(fd);return 0;
}

5.6.2.misc类设备介绍
5.6.2.1、何为misc
(1)中文名:杂项设备\杂散设备
(2)/sys/class/misc
(3)典型的字符设备
(4)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)。
(5)misc是对原始的字符设备注册接口的一个类层次的封装,很多典型字符设备都可以归类到misc类中,使用misc驱动框架来管理。
5.6.2.2、misc类设备驱动架构
(1)内核开发者实现部分,关键点有2个:一个是类的创建,另一个是开放给驱动开发者的接口
(2)具体设备驱动工程师实现部分
5.6.2.3、本部分学习方法
(1)蜂鸣器驱动源码已有,分析为主
(2)复习并验证前面讲的驱动框架的思维
(3)有余力的不妨开始注意一些细节

5.6.3.misc驱动框架源码分析1
5.6.3.1、misc源码框架基础
(1)misc源码框架本身也是一个模块,内核启动时自动加载
(2)源码框架的主要工作:注册misc类,使用老接口注册字符设备驱动(主设备号10),开放device注册的接口misc_register给驱动工程师
5.6.3.2、misc类设备的注册
(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。
(2)misc_list链表的作用。内核定义了一个misc_list链表用来记录所有内核中注册了的杂散类设备。当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name)
struct list_head name = LIST_HEAD_INIT(name)

原式子:static LIST_HEAD(misc_list);
展开后:static struct list_head misc_list = { &(misc_list), &(misc_list) }
(3)主设备号和次设备号的作用和区分

/** linux/drivers/char/misc.c** Generic misc open routine by Johan Myreen** Based on code from Linus** Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's*   changes incorporated into 0.97pl4*   by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)*   See busmouse.c for particulars.** Made things a lot mode modular - easy to compile in just one or two* of the misc drivers, as they are now completely independent. Linus.** Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>** Fixed a failing symbol register to free the device registration*      Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96** Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96** Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96** Handling of mouse minor numbers for kerneld:*  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,*  adapted by Bjorn Ekwall <bj0rn@blox.se>*  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>** Changes for kmod (from kerneld):* Cyrus Durgin <cider@speakeasy.org>** Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  10-Jan-1998*/#include <linux/module.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>/** Head entry for the doubly linked miscdevice list*/
static LIST_HEAD(misc_list);
static DEFINE_MUTEX(misc_mtx);/** Assigned numbers, used for dynamic minors*/
#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);#ifdef CONFIG_PROC_FS
static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
{mutex_lock(&misc_mtx);return seq_list_start(&misc_list, *pos);
}static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{return seq_list_next(v, &misc_list, pos);
}static void misc_seq_stop(struct seq_file *seq, void *v)
{mutex_unlock(&misc_mtx);
}static int misc_seq_show(struct seq_file *seq, void *v)
{const struct miscdevice *p = list_entry(v, struct miscdevice, list);seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");return 0;
}static const struct seq_operations misc_seq_ops = {.start = misc_seq_start,.next  = misc_seq_next,.stop  = misc_seq_stop,.show  = misc_seq_show,
};static int misc_seq_open(struct inode *inode, struct file *file)
{return seq_open(file, &misc_seq_ops);
}static const struct file_operations misc_proc_fops = {.owner   = THIS_MODULE,.open    = misc_seq_open,.read    = seq_read,.llseek  = seq_lseek,.release = seq_release,
};
#endifstatic int misc_open(struct inode * inode, struct file * file)
{int minor = iminor(inode);struct miscdevice *c;int err = -ENODEV;const struct file_operations *new_fops = NULL;mutex_lock(&misc_mtx);list_for_each_entry(c, &misc_list, list) {if (c->minor == minor) {new_fops = fops_get(c->fops);break;}}if (!new_fops) {mutex_unlock(&misc_mtx);request_module("char-major-%d-%d", MISC_MAJOR, minor);mutex_lock(&misc_mtx);list_for_each_entry(c, &misc_list, list) {if (c->minor == minor) {new_fops = fops_get(c->fops);break;}}if (!new_fops)goto fail;}/** Place the miscdevice in the file's* private_data so it can be used by the* file operations, including f_op->open below*/file->private_data = c;err = 0;replace_fops(file, new_fops);if (file->f_op->open)err = file->f_op->open(inode,file);
fail:mutex_unlock(&misc_mtx);return err;
}static struct class *misc_class;static const struct file_operations misc_fops = {.owner       = THIS_MODULE,.open        = misc_open,.llseek        = noop_llseek,
};/***  misc_register   -   register a miscellaneous device*    @misc: device structure**  Register a miscellaneous device with the kernel. If the minor*  number is set to %MISC_DYNAMIC_MINOR a minor number is assigned*    and placed in the minor field of the structure. For other cases*    the minor number requested is used.**   The structure passed is linked into the kernel and may not be*  destroyed until it has been unregistered. By default, an open()*    syscall to the device sets file->private_data to point to the*   structure. Drivers don't need open in fops for this.** A zero is returned on success and a negative errno code for*    failure.*/int misc_register(struct miscdevice * misc)
{dev_t dev;int err = 0;bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);INIT_LIST_HEAD(&misc->list);mutex_lock(&misc_mtx);if (is_dynamic) {int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);if (i >= DYNAMIC_MINORS) {err = -EBUSY;goto out;}misc->minor = DYNAMIC_MINORS - i - 1;set_bit(i, misc_minors);} else {struct miscdevice *c;list_for_each_entry(c, &misc_list, list) {if (c->minor == misc->minor) {err = -EBUSY;goto out;}}}dev = MKDEV(MISC_MAJOR, misc->minor);misc->this_device =device_create_with_groups(misc_class, misc->parent, dev,misc, misc->groups, "%s", misc->name);if (IS_ERR(misc->this_device)) {if (is_dynamic) {int i = DYNAMIC_MINORS - misc->minor - 1;if (i < DYNAMIC_MINORS && i >= 0)clear_bit(i, misc_minors);misc->minor = MISC_DYNAMIC_MINOR;}err = PTR_ERR(misc->this_device);goto out;}/** Add it to the front, so that later devices can "override"* earlier defaults*/list_add(&misc->list, &misc_list);out:mutex_unlock(&misc_mtx);return err;
}/***   misc_deregister - unregister a miscellaneous device*    @misc: device to unregister**  Unregister a miscellaneous device that was previously*  successfully registered with misc_register().*/void misc_deregister(struct miscdevice *misc)
{int i = DYNAMIC_MINORS - misc->minor - 1;if (WARN_ON(list_empty(&misc->list)))return;mutex_lock(&misc_mtx);list_del(&misc->list);device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));if (i < DYNAMIC_MINORS && i >= 0)clear_bit(i, misc_minors);mutex_unlock(&misc_mtx);
}EXPORT_SYMBOL(misc_register);
EXPORT_SYMBOL(misc_deregister);static char *misc_devnode(struct device *dev, umode_t *mode)
{struct miscdevice *c = dev_get_drvdata(dev);if (mode && c->mode)*mode = c->mode;if (c->nodename)return kstrdup(c->nodename, GFP_KERNEL);return NULL;
}static int __init misc_init(void)
{int err;struct proc_dir_entry *ret;ret = proc_create("misc", 0, NULL, &misc_proc_fops);misc_class = class_create(THIS_MODULE, "misc");err = PTR_ERR(misc_class);if (IS_ERR(misc_class))goto fail_remove;err = -EIO;if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))goto fail_printk;misc_class->devnode = misc_devnode;return 0;fail_printk:printk("unable to get major %d for misc devices\n", MISC_MAJOR);class_destroy(misc_class);
fail_remove:if (ret)remove_proc_entry("misc", NULL);return err;
}
subsys_initcall(misc_init);

5.6.4.misc驱动框架源码分析2
5.6.4.1、open函数分析
5.6.4.2、misc在proc下的展现
5.6.4.3、内核互斥锁
(1)何为互斥锁
(2)定义:DEFINE_MUTEX
(3)上锁mutex_lock和解锁mutex_unlock
(4)内核防止竞争状态的手段:原子访问、自旋锁、互斥锁、信号量
(5)原子访问主要用来做计数、自旋锁后面讲中断会详细讲、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。

5.6.5.蜂鸣器驱动源码分析1
5.6.5.1、dev_init
(1)信号量
(2)miscdevice
(3)gpio_request
(4)printk
5.6.5.2、ioctl
(1)为什么需要ioctl(input output control,输入输出控制)。
(2)ioctl怎么用

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>#include <linux/gpio.h>#include <plat/gpio-cfg.h>
//#include <plat/regs-clock.h>
//#include <plat/regs-gpio.h>//#include <plat/gpio-bank-e.h>
//#include <plat/gpio-bank-f.h>
//#include <plat/gpio-bank-k.h>#define DEVICE_NAME     "buzzer"#define PWM_IOCTL_SET_FREQ     1
#define PWM_IOCTL_STOP         0static struct semaphore lock;   //定义一个信号量// TCFG0在Uboot中设置,这里不再重复设置
// Timer0输入频率Finput=pclk/(prescaler1+1)/MUX1
//                     =66M/16/16
// TCFG0 = tcnt = (pclk/16/16)/freq;
// PWM0输出频率Foutput =Finput/TCFG0= freq
static void PWM_Set_Freq( unsigned long freq )
{unsigned long tcon;unsigned long tcnt;unsigned long tcfg1;struct clk *clk_p;unsigned long pclk;//unsigned tmp;//设置GPD0_2为PWM输出s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));tcon = __raw_readl(S3C2410_TCON);tcfg1 = __raw_readl(S3C2410_TCFG1);//mux = 1/16tcfg1 &= ~(0xf<<8);tcfg1 |= (0x4<<8);__raw_writel(tcfg1, S3C2410_TCFG1);clk_p = clk_get(NULL, "pclk");pclk  = clk_get_rate(clk_p);tcnt  = (pclk/16/16)/freq;__raw_writel(tcnt, S3C2410_TCNTB(2));__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%tcon &= ~(0xf<<12);tcon |= (0xb<<12);      //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0__raw_writel(tcon, S3C2410_TCON);tcon &= ~(2<<12);           //clear manual update bit__raw_writel(tcon, S3C2410_TCON);
}void PWM_Stop( void )
{//将GPD0_2设置为inputs3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));
}static int x210_pwm_open(struct inode *inode, struct file *file)
{if (!down_trylock(&lock))return 0;elsereturn -EBUSY;}static int x210_pwm_close(struct inode *inode, struct file *file)
{up(&lock);return 0;
}// PWM:GPF14->PWM0
static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case PWM_IOCTL_SET_FREQ:printk("PWM_IOCTL_SET_FREQ:\r\n");if (arg == 0)return -EINVAL;PWM_Set_Freq(arg);break;case PWM_IOCTL_STOP:default:printk("PWM_IOCTL_STOP:\r\n");PWM_Stop();break;}return 0;
}static struct file_operations dev_fops = {.owner   =   THIS_MODULE,.open    =   x210_pwm_open,.release =   x210_pwm_close, .ioctl   =   x210_pwm_ioctl,
};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,   //255 系统自动分配次设备号.name = DEVICE_NAME,   //设备名字.fops = &dev_fops,   //file_operations结构体
};static int __init dev_init(void)
{int ret;init_MUTEX(&lock); //初始化信号量ret = misc_register(&misc);    //内核开放给驱动开发的接口注册misc设备/* GPD0_2 (PWMTOUT2)对硬件初始化 */ret = gpio_request(S5PV210_GPD0(2), "GPD0");  //向内核申请S5PV210_GPD0(2)引脚if(ret)printk("buzzer-x210: request gpio GPD0(2) fail");s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);  //设置为上拉模式s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1)); //设置为输出模式gpio_set_value(S5PV210_GPD0(2), 0);    //输出值0printk ("x210 "DEVICE_NAME" initialized\n");  //"内核自动把字符串拼接为一个字符串return ret;
}static void __exit dev_exit(void)
{misc_deregister(&misc);
}module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.9tripod.com");
MODULE_DESCRIPTION("x210 PWM Driver");

5.6.6.蜂鸣器驱动源码分析2
硬件操作有关的代码

6.misc类设备与蜂鸣器驱动相关推荐

  1. misc类设备与蜂鸣器驱动

    以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵权,请告知删除. 一.板载蜂鸣器驱动测试 1.驱动部分 (1)九鼎移植内核已经提供了蜂鸣器驱动源码,但如何查找是否已经有驱动文件? 法一 ...

  2. Linux驱动框架之misc类设备驱动框架

    1.何为misc设备 (1)misc中文名就是杂项设备\杂散设备,因为现在的硬件设备多种多样,有好些设备不好对他们进行一个单独的分类,所以就将这些设备全部归属于 杂散设备,也就是misc设备,例如像a ...

  3. misc类设备驱动2——misc类设备的简介

    以下内容源于朱有鹏嵌入式课程的学习与整理,如有其侵权请告知删除. 一.misc类设备的含义 1.名字含义 misc是英文Miscellaneous的缩写,中文意思是"杂项.混杂", ...

  4. linux驱动 — switch_dev类设备的使用

    前言 在项目中碰到有人用switch_dev_register注册了switch_dev类设备,并且应用层能读到它的状态信息,感觉挺好奇,于是网上搜了些资料,自个又琢磨了一下,算是理解了,现在整理一下 ...

  5. Linux驱动(14)--字符类设备与驱动

    字符类设备 1. 静态申请字符类设备号 1.1 所需函数与头文件 1.2 源码与注释 1.3 运行结果 2. 动态申请字符类设备号 2.1 所需函数与头文件 2.2 源码与注释 2.3 运行结果 3. ...

  6. Misc杂项设备驱动框架

    Misc杂项设备驱动框架 由于设备号比较紧张,所以一些不相关的设备可以使用同一个主设备号,不同的次设备号.主设备号通常是10. 杂项设备结构体 struct miscdevice {int minor ...

  7. 4412 字符类设备的设备号

    一.静态申请字符类设备号 字符类设备函数在文件"include/linux/fs.h"中 内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev ...

  8. 十五、linux 注册字符类设备和生成节点

    一. 注册字符类设备 • 分配内存空间函数kmalloc         – 分配连续的虚拟地址,用于小内存分配.在include/linux/slab.h文件中.         – 参数1:申请的 ...

  9. Linux设备模型组件-类设备-设备类及subsystem

    Linux设备模型   一.sysfs文件系统: sysfs文件系统是Linux2.6内核引入的,它被看成是与proc.devfs和devpty等同类别的文件系统,sysfs文件系统也是一个虚拟文件系 ...

  10. ARM(IMX6U)裸机C语言蜂鸣器驱动实验(BSP+SDK)

    参考:Linux之ARM(IMX6U)裸机C语言蜂鸣器驱动实验–驱动编写,编译 作者:一只青木呀 发布时间: 2020-08-16 14:47:23 网址:https://blog.csdn.net/ ...

最新文章

  1. R语言e1071包中的支持向量机:构建nu-classification类型的支持向量机SVM并分析不同nu值惩罚下模型分类螺旋线型(sprials)线性不可分数据集的表现
  2. python格式化% 和 format
  3. DiscuzX3.2,3.3升级DiscuzX3.4的详细教程!
  4. STM32F103/302 SPI3 接口使用例程
  5. Django中models利用ORM对Mysql 进行查表的语句(多个语句)
  6. 编写统一、符合习惯的CSS的原则
  7. 光纤vs.铜缆:为什么光纤是智能、可持续建筑越来越多的选择
  8. SQL-Server使用点滴(一-数据对象篇)
  9. NetWare 5.1 Does Not Recognize CD-ROM Drive
  10. 根据模板提示,加入元素,修改CSS
  11. Oracle 数据库常用操作总结二之数据库的导入和导出
  12. 熊出没之伐木机器人_盘点熊出没中李老板的五大伐木工,光头强无人能代替
  13. 简单的Java 16方格排序游戏
  14. Bootstrap4从入门到精通视频教程
  15. 拼团返利电商系统(拼返系统)v2.6
  16. 2013年第四届C/C++ A组蓝桥杯省赛真题
  17. Gmail 中出现紫字的怪现象
  18. 赋权边覆盖问题——采用禁忌搜索算法的C++实现
  19. c语言程序设计第二版(张磊),C语言程序设计教程(第2版) 教学课件 张磊 第9章 文件程序设计.pdf...
  20. STM32 串口 FIFO

热门文章

  1. 什么是 docker?docker和虚拟机有什么差别和不同?
  2. 使用Vendor NDK实现Android Camera preview
  3. [4G/5G/6G专题基础-159]: CQI值的滤波方法
  4. android 闪屏 实现,Android游戏闪屏实现步骤详解
  5. Android开发实践:设计安卓应用以计算BMI指数
  6. 转载】强制删除域控制器
  7. 【2022】将3D目标检测看作序列预测-Point2Seq: Detecting 3D Objects as Sequences
  8. el-menu菜单下划线解决办法
  9. C#VS工程报错:CS0234 命名空间“Microsoft.VisualStudio”中不存在类型或命名空间名“VCProjectEngine(是否缺少程序集引用)
  10. 教育知识与能力-第七章 中学德育