PC蜂鸣器音乐

有了《使用procfs》、《I/O映射之I/O端口》、《内核读写磁盘文件》这三篇文章的基础后,我们将其结合,实现如下功能的实例: 

1.打开传入的音乐谱文件(通过procfs接口);
2.读取音乐谱文件(以“频率”、“延时”、“频率”、“延时”、……这样的格式保存);
3.解析读取的文件;
4.将解析的数据传送去操作8254,让PC蜂鸣器弹奏文件对应的音乐。 

好了,基础的内容看开头提及的文章,接下来上代码:

#include
#include
#include
#include
#include
#include

#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("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

你可能感兴趣的:(PC蜂鸣器音乐)