PC蜂鸣器音乐
有了《使用procfs》、《I/O映射之I/O端口》、《内核读写磁盘文件》这三篇文章的基础后,我们将其结合,实现如下功能的实例:
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):
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