PC蜂鸣器音乐
有了“使用procfs”、“I/O映射之I/O端口”、“内核读写磁盘文件”这三篇文章的基础后,我们将其结合,实现如下功能的实例:
1.打开传入的音乐谱文件(通过procfs接口);
2.读取音乐谱文件(以“频率”、“延时”、“频率”、“延时”、……这样的格式保存);
3.解析读取的文件;
4.将解析的数据传送去操作8254,让PC蜂鸣器弹奏文件对应的音乐。
好了,基础的内容看开头提及的文章,接下来上代码:
1.打开传入的音乐谱文件(通过procfs接口);
2.读取音乐谱文件(以“频率”、“延时”、“频率”、“延时”、……这样的格式保存);
3.解析读取的文件;
4.将解析的数据传送去操作8254,让PC蜂鸣器弹奏文件对应的音乐。
好了,基础的内容看开头提及的文章,接下来上代码:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include <linux/timex.h> #ifndef MAX_PATH #define MAX_PATH 256 #endif #define PACKAGE_SIZE 512 extern void msleep(unsigned int msecs); struct proc_dir_entry * slam_dir = NULL; struct proc_dir_entry * slam_entry = NULL; static char * dir_name = "slam_song"; static char * entry_name = "path"; static void beep(unsigned int freq, unsigned int delay) { unsigned int count; if (freq) count = PIT_TICK_RATE / freq; else count = 20; outb_p(0xB6, 0x43); outb_p(count & 0xff, 0x42); outb((count >> 8) & 0xff, 0x42); outb_p(inb_p(0x61) | 0x03, 0x61); msleep(delay); outb(inb_p(0x61) & 0xFC, 0x61); } static int play(char * filename) { struct file * fp; mm_segment_t cur_mm_seg; loff_t fpos = 0; char buf; int data[PACKAGE_SIZE] = {0}; int i = 0, j = 0; //i:data count,j:just temp value fp = filp_open(filename, O_RDONLY, 0644); if (IS_ERR(fp)) { printk("<xinu>filp_open error\n"); return -1; } cur_mm_seg = get_fs(); set_fs(KERNEL_DS); while(vfs_read(fp, &buf, sizeof(buf), &fpos)>0) { if(buf == ','){ i++; continue; } if(buf == '\n' || buf == '\r') continue; data[i] = data[i] * 10 + (buf - '0'); } set_fs(cur_mm_seg); filp_close(fp, NULL); for (j = 0; j < i; j += 2) { beep(data[j], data[j+1]); } return 0; } static ssize_t song_path_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char filename[MAX_PATH]; if(count > MAX_PATH){ printk("The filename is too long,the length must < %d\n", MAX_PATH); return -EFAULT; } if(copy_from_user(filename, buf, count)){ printk("copy_from_user error!\n"); return -EFAULT; } filename[count-1] = '\0'; play(filename); return count; } static const struct file_operations slam_fops = { .owner = THIS_MODULE, .write = song_path_write, }; static int beep_songs_init(void) { #ifdef CONFIG_PROC_FS slam_dir = proc_mkdir(dir_name, NULL); if (!slam_dir){ printk("Create directory \"%s\" failed.\n", dir_name); return -1; } slam_entry = proc_create(entry_name, 0666, slam_dir, &slam_fops); if (!slam_entry){ printk("Create file \"%s\" failed.\n", entry_name); return -1; } #else printk("This module requests PROCFS support in linux kernel,need set CONFIG_PROC_FS configure Y\n"); #endif return 0; } static void beep_songs_exit(void) { #ifdef CONFIG_PROC_FS proc_remove(slam_entry); proc_remove(slam_dir); #endif printk("Bye %s!\n", __func__); } module_init(beep_songs_init); module_exit(beep_songs_exit); MODULE_LICENSE("GPL");
相应的Makefile文件内容如下:
obj-m += beep_songs.o CUR_PATH:=$(shell pwd) LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6 all: make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules clean: make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean
对应的源码文件目录树如下:
/home/xinu/xinu/linux_kernel_driver_l1/beep_songs/
├── beep_songs.c
└── Makefile
下面有两个实现好的音乐文件:
1.中文版“生日快乐歌”(happy_birthday_chinese.txt):
/home/xinu/xinu/linux_kernel_driver_l1/beep_songs/
├── beep_songs.c
└── Makefile
下面有两个实现好的音乐文件:
1.中文版“生日快乐歌”(happy_birthday_chinese.txt):
392,375,392,125,440,500,392,500,523,500,494,1000,392,375,392,125,440,500,392,500,587,500,523,1000,392,375,392,125,784,500,659,500,523,500,494,500,440,1000,698,375,698,125,659,500,523,500,587,500,523,1000
2.周华健“朋友”(pengyou.txt):
0,900,587,450,659,450,523,900,587,450,659,450,587,900,659,450,784,450,880,900,784,450,659,450,659,900,587,450,659,450,523,900,587,450,659,450,392,900,587,450,659,450,587,900,440,450,523,4950,587,450,659,450,523,900,587,450,459,450,523,900,587,450,659,450,784,900,784,450,880,450,523,900,440,450,523,450,587,900,587,450,523,450,440,900,440,450,523,450,587,450,659,450,587,450,523,225,587,225,587,900,659,450,587,225,523,225,523,900,587,450,587,450,659,900,523,450,587,450,659,900,784,450,784,225,784,225,880,900,523,450,440,450,523,900,587,450,587,225,523,225,440,900,440,450,392,450,440,1800,523,900,0,450,659,225,659,225,784,450,784,450,784,450,784,225,880,1125,784,450,880,450,988,450,1046,450,880,450,880,225,784,1125,659,450,659,225,587,1125,523,450,523,225,880,1125,784,450,659,225,587,1125,523,225,523,225,440,1350,587,450,659,225,659,225,784,450,784,450,784,450,784,225,880,11,784,25,880,450,988,450,1046,450,880,450,880,450,784,225,659,1125,659,450,587,225,523,1125,523,450,880,225,784,1125,659,450,587,225,523,1125,440,450,440,225,523,675,523,1350
当我们将上面的模块编译加载后,会在/proc目录下有/proc/slam_song/path文件生成,然后我们将上面的音乐文件路径写入该文件后即可开始演奏,如下:
echo “/home/xinu/happy_birthday_chinese.txt” > /proc/slam_song/path
echo “/home/xinu/pengyou.txt” > /proc/slam_song/path
本例解决了之前相关例子里beep函数对freq为0时未做除数0的检查,还有本例有如下优化建议:
1.在写入文件路径时需等待演奏结束才返回,此时可采用多线程方式解决;
2.采用多线程方式处理后,还需要使用同步机制对演奏过程中再次写入路径情况进行处理。
关于这两条建议,我们将在后面的实例中再展开应用,你可先在本例基础上做些尝试。
参考资料:
http://blog.csdn.net/sky_j123/article/details/19574955
echo “/home/xinu/happy_birthday_chinese.txt” > /proc/slam_song/path
echo “/home/xinu/pengyou.txt” > /proc/slam_song/path
本例解决了之前相关例子里beep函数对freq为0时未做除数0的检查,还有本例有如下优化建议:
1.在写入文件路径时需等待演奏结束才返回,此时可采用多线程方式解决;
2.采用多线程方式处理后,还需要使用同步机制对演奏过程中再次写入路径情况进行处理。
关于这两条建议,我们将在后面的实例中再展开应用,你可先在本例基础上做些尝试。
参考资料:
http://blog.csdn.net/sky_j123/article/details/19574955
评论
发表评论