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

5.6.1.板载蜂鸣器驱动测试
5.6.1.1、驱动部分
(1)九鼎移植内核已经提供了蜂鸣器驱动源码
(2)make menuconfig
(3)bug排查。修改Makefile中的宏名,最终可以在系统中看到 /dev/buzzer
//寻找BSP源码中是否有蜂鸣器驱动程序:
(1)source insight中搜索文件关键字x210-buzzer;
(2)make menuconfig Alt+?搜索关键字buzzer
(3)[root@localhost linux-3.5]# grep "buzzer" * -nR
//搜不到则需要自己移植或者编写;搜到了好啊!看是否编译进内核能工作。
//九鼎的板子有蜂鸣器驱动程序,在driver/char目录下(理应在driver/misc目录下);我的tiny4412板子的内核没有哎!
5.6.1.2、应用部分
(1)应用编写:打开文件+ioctl
(2)测试实践
//根据已有的(写好的)驱动代码写应用代码。


5.6.2.misc类设备介绍
5.6.2.1、何为misc
(1)中文名:杂项设备\杂散设备
//
50 struct miscdevice {
51 int minor;
52 const char *name;
53 const struct file_operations *fops;
54 struct list_head list;
55 struct device *parent;
56 struct device *this_device;
57 const char *nodename;
58 umode_t mode;
59 };
(2)/sys/class/misc
//无法分类的一些设备(不太好归类),这是一种分类方法,没有严格分类。
(3)典型的字符设备
//主设备号是10,次设备号区分具体的实例设备
(4)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)。
(5)misc是对原始的字符设备注册接口的一个类层次的封装,很多典型字符设备都可以归类到misc类中,使用misc驱动框架来管理。
[root@localhost linux-3.5]# ls /sys/
block/ class/ devices/ fs/ kernel/ power/
bus/ dev/ firmware/ hypervisor/ module/
[root@localhost linux-3.5]# ls /sys/class/
backlight/ dmi/ input/ net/ rfkill/ spi_host/ vtconsole/
bdi/ drm/ leds/ pci_bus/ rtc/ spi_transport/ watchdog/
block/ firmware/ mdio_bus/ pcmcia_socket/ scsi_device/ thermal/
bluetooth/ graphics/ mem/ power_supply/ scsi_disk/ tpm/
bsg/ hidraw/ misc/ ppdev/ scsi_generic/ tty/
cpuid/ hwmon/ msr/ raw/ scsi_host/ usbmon/
dma/ i2c-adapter/ mtd/ regulator/ sound/ vc/
[root@localhost linux-3.5]# ls /sys/class/misc/
agpgart/ device-mapper/ microcode/ rfkill/
autofs/ fuse/ network_latency/ snapshot/
cpu_dma_latency/ hpet/ network_throughput/ uinput/
crash/ mcelog/ nvram/ vga_arbiter/

//[root@localhost linux-3.5]# vim drivers/char/misc.c //内核实现的接口,很重要!
272 static int __init misc_init(void)//
273 {
274 int err;
275 //是否使用proc文件系统
276 #ifdef CONFIG_PROC_FS
277 proc_create("misc", 0, NULL, &misc_proc_fops);
278 #endif
279 misc_class = class_create(THIS_MODULE, "misc");//
280 err = PTR_ERR(misc_class);
281 if (IS_ERR(misc_class))
282 goto fail_remove;
283
284 err = -EIO;
285 if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//
//早年写的,硬性分配;指定主设备号为10;#define MISC_MAJOR 10
286 goto fail_printk;
287 misc_class->devnode = misc_devnode;
288 return 0;
289
290 fail_printk:
291 printk("unable to get major %d for misc devices\n", MISC_MAJOR);
292 class_destroy(misc_class);
293 fail_remove:
294 remove_proc_entry("misc", NULL);
295 return err;
296 }

168 /**
169 * misc_register - register a miscellaneous device
170 * @misc: device structure
171 *
172 * Register a miscellaneous device with the kernel. If the minor
173 * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
174 * and placed in the minor field of the structure. For other cases
175 * the minor number requested is used.
176 *
177 * The structure passed is linked into the kernel and may not be
178 * destroyed until it has been unregistered.
179 *
180 * A zero is returned on success and a negative errno code for
181 * failure.
182 */
183
184 int misc_register(struct miscdevice * misc)//
185 {
186 struct miscdevice *c;
187 dev_t dev;
188 int err = 0;
189
190 INIT_LIST_HEAD(&misc->list);
191
192 mutex_lock(&misc_mtx);
193 list_for_each_entry(c, &misc_list, list) { //1、查找次设备号是否已被内核占用?
194 if (c->minor == misc->minor) {
依次判断 已有的 ==?我的设备次设备号,人家已有的话或者在忙就报错
195 mutex_unlock(&misc_mtx);
196 return -EBUSY;
197 }
198 }
199
200 if (misc->minor == MISC_DYNAMIC_MINOR) { //2、设置成255,内核自动为要注册的设备自动分配次设备号。
201 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);//bitmap 位图,属于数据结构;找到位0则可以使用
202 if (i >= DYNAMIC_MINORS) {
203 mutex_unlock(&misc_mtx);
204 return -EBUSY;
205 }
206 misc->minor = DYNAMIC_MINORS - i - 1;
207 set_bit(i, misc_minors);//已申请使用了就要置1
208 }
209
210 dev = MKDEV(MISC_MAJOR, misc->minor);//合成一个设备号
211
212 misc->this_device = device_create(misc_class, misc->parent, dev,
213 misc, "%s", misc->name); //3、核心,向内核真正注册这个设备
214 if (IS_ERR(misc->this_device)) {
215 int i = DYNAMIC_MINORS - misc->minor - 1;
216 if (i < DYNAMIC_MINORS && i >= 0)
217 clear_bit(i, misc_minors);
218 err = PTR_ERR(misc->this_device);
219 goto out;
220 }
221
222 /*
223 * Add it to the front, so that later devices can "override"
224 * earlier defaults
225 */
226 list_add(&misc->list, &misc_list);//4、注册成功则添加进内核链表
227 out:
228 mutex_unlock(&misc_mtx);
229 return err;
230 }

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)主设备号和次设备号的作用和区分
//类似于:类和类中的具体的设备

//[root@localhost linux-3.5]# vim drivers/char/misc.c //内核实现的接口,很重要!
297 subsys_initcall(misc_init);//定义了8个启动顺序,越小越前。

272 static int __init misc_init(void)//
273 {
274 int err;
275 //是否使用proc文件系统
276 #ifdef CONFIG_PROC_FS
277 proc_create("misc", 0, NULL, &misc_proc_fops);
278 #endif
[root@localhost linux-3.5]# ls /proc/ //提供了用户空间可以看见、操作内核空间的接口。(linux-2.4在用;linux-2.6废弃了它,因太乱吧!)
1 165 2008 25 2777 2933 3001 3242 44 59 75 buddyinfo keys scsi
10 17 2030 2523 2788 2935 3005 3243 45 6 76 bus key-users self
11 1798 2031 2535 2794 2936 3013 3257 46 60 77 cgroups kmsg slabinfo
[root@localhost linux-3.5]# ls /proc/misc
/proc/misc
[root@localhost linux-3.5]# cat /proc/misc //可很方便的在用户空间看见内核空间注册了哪些杂项设备;次设备号区分设备
56 autofs
229 fuse
223 uinput
184 microcode
57 rfkill
58 device-mapper
59 network_throughput
60 network_latency
61 cpu_dma_latency
62 crash
175 agpgart
144 nvram
228 hpet
231 snapshot
227 mcelog
63 vga_arbiter

56 static LIST_HEAD(misc_list);//定义了一个被初始化的内核链表;便于内核对杂项类设备管理;管理链表


5.6.4.misc驱动框架源码分析2
5.6.4.1、open函数分析
//
162 static const struct file_operations misc_fops = {
163 .owner = THIS_MODULE,
164 .open = misc_open, //
165 .llseek = noop_llseek,
166 };

112 static int misc_open(struct inode * inode, struct file * file)//inode是文件在磁盘上的路径,为了找到它而已
113 {
114 int minor = iminor(inode);
115 struct miscdevice *c;//
116 int err = -ENODEV;
117 const struct file_operations *old_fops, *new_fops = NULL;
118
119 mutex_lock(&misc_mtx);
120
121 list_for_each_entry(c, &misc_list, list) { //遍历内核的杂项设备链表,找次设备号,即该设备
122 if (c->minor == minor) {
123 new_fops = fops_get(c->fops);//找到后 读出来从链表中查到的file_operations赋值给new_fops
124 break;
125 }
126 }
128 if (!new_fops) { //如果没找到
129 mutex_unlock(&misc_mtx);
130 request_module("char-major-%d-%d", MISC_MAJOR, minor);//
131 mutex_lock(&misc_mtx);
132
133 list_for_each_entry(c, &misc_list, list) { //再找一遍
134 if (c->minor == minor) {
135 new_fops = fops_get(c->fops);
136 break;
137 }
138 }
139 if (!new_fops)
140 goto fail;//实在找不到就算了。
141 }
//如果找到了
143 err = 0;
144 old_fops = file->f_op;//将open打开的这个赋值给old_fops
145 file->f_op = new_fops;//将new_fops填充到file->f_op里面
146 if (file->f_op->open) {
147 file->private_data = c;
148 err=file->f_op->open(inode,file);//真正的open,这是关键核心!
149 if (err) { //错误了,还原
150 fops_put(file->f_op);
151 file->f_op = fops_get(old_fops);
152 }
153 }
154 fops_put(old_fops);
155 fail:
156 mutex_unlock(&misc_mtx);
157 return err;
158 }

//
50 struct miscdevice {
51 int minor;
52 const char *name;
53 const struct file_operations *fops;
//最终执行到这里的fops,这才是写驱动的人提供的,比如九鼎的x210-buzzer.c里的struct file_operations *fops;每项都被各个函数调用。
//映射到这里的过程,就是我们的分析的过程。
54 struct list_head list;
55 struct device *parent;
56 struct device *this_device;
57 const char *nodename;
58 umode_t mode;
59 };

5.6.4.2、misc在proc下的展现

//人的大脑做更多的工作应该是思考,而不是过多的记忆!照猫画虎写锁。
5.6.4.3、内核互斥锁
(1)何为互斥锁
(2)定义:DEFINE_MUTEX
(3)上锁mutex_lock和解锁mutex_unlock
//互斥锁:为了实现互斥访问,你访问使用他就不能访问使用。例如借书,借了书就相当于给其上锁。
//使用机制:
若有进程使用资源时加锁,使用完资源解锁;若有其他进程想要访问该资源时试图上锁,上锁失败时休眠在这里(互斥锁属于休眠锁、等待锁),注册自己到这个已上锁的进程等待队列中休眠等待,直到该资源被使用进程释放时解锁,排队的第一个进程才可以试图上锁使用该资源了。例如排队上厕所。
//57 static DEFINE_MUTEX(misc_mtx);
//[root@localhost linux-3.5]# vim include/linux/mutex.h
48 struct mutex {
49 /* 1: unlocked, 0: locked, negative: locked, possible waiters */
50 atomic_t count;
51 spinlock_t wait_lock;
52 struct list_head wait_list;
53 #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
54 struct task_struct *owner;
55 #endif
56 #ifdef CONFIG_DEBUG_MUTEXES
57 const char *name;
58 void *magic;
59 #endif
60 #ifdef CONFIG_DEBUG_LOCK_ALLOC
61 struct lockdep_map dep_map;
62 #endif
63 };

141 #define mutex_lock(lock) mutex_lock_nested(lock, 0) //不可打断
142 #define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0) //可打断,被信号啊啥的
143 #define mutex_lock_killable(lock) mutex_lock_killable_nested(lock, 0)

(4)内核防止竞争状态的手段:
1、原子访问 //不能被打断的情况下要一步执行完;例如int count;count++ 可能是三步汇编代码,其中可被中断掉!
2、自旋锁 //多用于多核CPU中,趋势。
3、互斥锁、信号量 //信号量被用于打开计数的,比如打开了7次就不能被打开使用了;互斥锁是特殊的信号量,就是计数值为1的信号量,只能被打开一次
//竞争状态:好多进程在争夺同一资源,内核为了仲裁解决该问题,提出了以上这些方法。
(5)原子访问主要用来做计数、自旋锁后面讲中断会详细讲、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。

 

5.6.5.蜂鸣器驱动源码分析1
5.6.5.1、dev_init
(1)信号量
//[root@localhost ~]# vim /linux-3.5/include/linux/semaphore.h
(2)miscdevice
(3)gpio_request
(4)printk
5.6.5.2、ioctl
(1)为什么需要ioctl(input output control,输入输出控制)。
//为了方便的解决read/write的复杂性,可以人性化控制。
(2)ioctl怎么用

//app_buzzer.c
#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #define DEVNAME "/dev/buzzer" #define PWM_IOCTL_SET_FREQ 1 #define PWM_IOCTL_STOP 0 int 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.6.蜂鸣器驱动源码分析2
硬件操作有关的代码
//
[root@localhost ~]#vim /linux-3.5/arch/arm/mach-vt8500/pwm.c
[root@localhost ~]# vim /linux-3.5/arch/arm/plat-samsung/pwm.c

posted @ 2017-08-10 17:56  bkycrmn  阅读(275)  评论(0)    收藏  举报