diff options
Diffstat (limited to 'package/fonera-mp3-drv/src/mp3_drv.c')
-rw-r--r-- | package/fonera-mp3-drv/src/mp3_drv.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/package/fonera-mp3-drv/src/mp3_drv.c b/package/fonera-mp3-drv/src/mp3_drv.c new file mode 100644 index 0000000..f2c0dd1 --- /dev/null +++ b/package/fonera-mp3-drv/src/mp3_drv.c @@ -0,0 +1,308 @@ +/* +* a.lp_mp3 - VS1011B driver for Fonera +* Copyright (c) 2007 phrozen.org - John Crispin <john@phrozen.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... john@phrozen.org +* +*/ + + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <linux/timer.h> +#include <linux/init.h> +#include <linux/genhd.h> + +// do we want debuging info ? +#if 0 +#define DBG(x) x +#else +#define DBG(x) +#endif + +#define MP3_CHUNK_SIZE 4096 +#define MP3_BUFFERING 0 +#define MP3_PLAYING 1 +#define MP3_BUFFER_FINISHED 2 +#define MP3_PLAY_FINISHED 3 +typedef struct _MP3_DATA{ + unsigned char mp3[MP3_CHUNK_SIZE]; + unsigned char state; +} MP3_DATA; + +#define IOCTL_MP3_INIT 0x01 +#define IOCTL_MP3_RESET 0x02 +#define IOCTL_MP3_SETVOLUME 0x03 +#define IOCTL_MP3_GETVOLUME 0x04 + +typedef struct _AUDIO_DATA{ + unsigned int bitrate; + unsigned int sample_rate; + unsigned char is_stereo; +}AUDIO_DATA; +#define IOCTL_MP3_GETAUDIODATA 0x05 +#define IOCTL_MP3_CLEARBUFFER 0x06 +#define IOCTL_MP3_PLAY 0x07 + +typedef struct _MP3_BEEP{ + unsigned char freq; + unsigned int ms; +} MP3_BEEP; +#define IOCTL_MP3_BEEP 0x08 +#define IOCTL_MP3_END_REACHED 0x09 +#define IOCTL_MP3_BASS 0x10 + +#define CRYSTAL12288 0x9800 +#define CRYSTAL24576 0x0 + +#define DEV_NAME "mp3" +#define DEV_MAJOR 196 +#define MAX_MP3_COUNT 1 + +typedef struct _mp3_inf{ + unsigned char is_open; +} mp3_inf; +static mp3_inf mp3_info[MAX_MP3_COUNT]; + +#define MP3_BUFFER_SIZE (128 * 1024) +unsigned char mp3_buffer[MP3_BUFFER_SIZE]; + +static unsigned long int mp3_buffer_offset_write = 0; +static unsigned long int mp3_buffer_offset_read = 0; +static unsigned char mp3_buffering_status = MP3_BUFFERING; +static unsigned long int mp3_data_in_buffer = 0; +static int mp3_thread = 0; +unsigned int crystal_freq; + +#include "vs10xx.c" + +static wait_queue_head_t wq; +static DECLARE_COMPLETION(mp3_exit); + +static int mp3_playback_thread(void *data){ + int j; + unsigned long timeout; + printk("started kthread\n"); + daemonize("kmp3"); + while(mp3_buffering_status != MP3_PLAY_FINISHED){ + if((mp3_buffering_status == MP3_PLAYING) || (mp3_buffering_status == MP3_BUFFER_FINISHED)){ + while(VS1011_NEEDS_DATA){ + if(mp3_buffer_offset_read == MP3_BUFFER_SIZE){ + mp3_buffer_offset_read = 0; + } + + if(mp3_data_in_buffer == 0){ + if(mp3_buffering_status == MP3_BUFFER_FINISHED){ + printk("mp3_drv.ko : finished playing\n"); + mp3_buffering_status = MP3_PLAY_FINISHED; + } else { + printk("mp3_drv.ko : buffer empty ?\n"); + if(mp3_buffering_status != MP3_PLAY_FINISHED){ + } + } + } else { + for(j = 0; j < 32; j++){ + VS1011_send_SDI(mp3_buffer[mp3_buffer_offset_read + j]); + } + mp3_buffer_offset_read += 32; + mp3_data_in_buffer -= 32; + } + } + } + timeout = 1; + timeout = wait_event_interruptible_timeout(wq, (timeout==0), timeout); + } + complete_and_exit(&mp3_exit, 0); +} + +static ssize_t module_write(struct file * file, const char * buffer, size_t count, loff_t *offset){ + MP3_DATA mp3_data; + + copy_from_user((char*) &mp3_data, buffer, sizeof(MP3_DATA)); + + if(mp3_data.state == MP3_BUFFER_FINISHED){ + mp3_buffering_status = MP3_BUFFER_FINISHED; + DBG(printk("mp3_drv.ko : file end reached\n")); + return 1; + } + + if(mp3_data.state == MP3_PLAY_FINISHED){ + mp3_buffering_status = MP3_PLAY_FINISHED; + mp3_data_in_buffer = 0; + DBG(printk("mp3_drv.ko : stop playing\n")); + return 1; + } + + if(mp3_data_in_buffer + MP3_CHUNK_SIZE >= MP3_BUFFER_SIZE){ + DBG(printk("mp3_drv.ko : buffer is full? %ld\n", mp3_data_in_buffer);) + return 0; + } + + if(mp3_buffer_offset_write == MP3_BUFFER_SIZE){ + mp3_buffer_offset_write = 0; + } + + memcpy(&mp3_buffer[mp3_buffer_offset_write], mp3_data.mp3, MP3_CHUNK_SIZE); + mp3_buffer_offset_write += MP3_CHUNK_SIZE; + mp3_buffering_status = mp3_data.state; + mp3_data_in_buffer += MP3_CHUNK_SIZE; + return 1; +} + +static int module_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ + unsigned int retval = 0; + AUDIO_DATA audio_data; + MP3_BEEP mp3_beep; + DBG(printk("mp3_drv.ko : Ioctl Called (cmd=%d)\n", cmd );) + switch (cmd) { + case IOCTL_MP3_INIT: + crystal_freq = arg; + VS1011_init(crystal_freq, 1); + VS1011_print_registers(); + break; + + case IOCTL_MP3_RESET: + DBG(printk("mp3_drv.ko : doing a sw reset\n");) + VS1011_init(crystal_freq, 0); + VS1011_print_registers(); + VS1011_send_zeros(0x20); + break; + + case IOCTL_MP3_SETVOLUME: + DBG(printk("mp3_drv.ko : setting volume to : %lu\n", arg&0xffff);) + VS1011_set_volume(arg); + break; + + case IOCTL_MP3_GETVOLUME: + retval = VS1011_get_volume(); + DBG(printk("mp3_drv.ko : read volume : %d\n", retval);) + break; + + case IOCTL_MP3_GETAUDIODATA: + DBG(printk("mp3_drv.ko : read audio data\n");) + VS1011_get_audio_data(&audio_data); + copy_to_user((char*)arg, (char*)&audio_data, sizeof(AUDIO_DATA)); + break; + + case IOCTL_MP3_CLEARBUFFER: + DBG(printk("mp3_drv.ko : clearing buffer\n");) + mp3_buffer_offset_read = 0; + mp3_buffer_offset_write = 0; + mp3_buffering_status = MP3_PLAY_FINISHED; + mp3_data_in_buffer = 0; + break; + + case IOCTL_MP3_PLAY: + mp3_thread = kernel_thread(mp3_playback_thread, NULL, CLONE_KERNEL); + break; + + case IOCTL_MP3_BEEP: + copy_from_user((char*)&mp3_beep, (char*)arg, sizeof(MP3_BEEP)); + VS1011_sine(1,mp3_beep.freq); + msDelay(mp3_beep.ms); + VS1011_sine(0,0); + break; + + case IOCTL_MP3_END_REACHED: + if(mp3_buffering_status == MP3_PLAY_FINISHED){ + retval = 1; + } + break; + + case IOCTL_MP3_BASS: + VS1011_set_bass(arg); + break; + + default: + printk("mp3_drv.ko : unknown ioctl\n"); + break; + + } + return retval; +} + +static int module_open(struct inode *inode, struct file *file){ + unsigned int dev_minor = MINOR(inode->i_rdev); + if(dev_minor != 0){ + printk("mp3_drv.ko : trying to access unknown minor device -> %d\n", dev_minor); + return -ENODEV; + } + if(mp3_info[dev_minor].is_open) { + printk("mp3_drv.ko : Device with minor ID %d already in use\n", dev_minor); + return -EBUSY; + } + mp3_info[dev_minor].is_open = 1; + + mp3_buffering_status = MP3_PLAY_FINISHED; + printk("mp3_drv.ko : Minor %d has been opened\n", dev_minor); + return 0; +} + +static int module_close(struct inode * inode, struct file * file){ + unsigned int dev_minor = MINOR(inode->i_rdev); + mp3_info[dev_minor].is_open = 0; + printk("mp3_drv.ko : Minor %d has been closed\n", dev_minor); + mp3_buffering_status = MP3_PLAY_FINISHED; + return 0; +} + +struct file_operations modulemp3_fops = { + write: module_write, + ioctl: module_ioctl, + open: module_open, + release: module_close +}; + +static int __init mod_init(void){ + printk("mp3_drv.ko : VS1011b Driver\n"); + printk("mp3_drv.ko : Made by John '2B|!2B' Crispin (john@phrozen.org)\n"); + printk("mp3_drv.ko : Starting ...\n"); + + if(register_chrdev(DEV_MAJOR, DEV_NAME, &modulemp3_fops)) { + printk( "mp3_drv.ko : Error whilst opening %s (%d)\n", DEV_NAME, DEV_MAJOR); + return( -ENODEV ); + } + + mp3_info[0].is_open = 0; + printk("mp3_drv.ko : Device %s registered for major ID %d\n", DEV_NAME, DEV_MAJOR); + crystal_freq = CRYSTAL12288; + VS1011_init(crystal_freq, 1); + VS1011_print_registers(); + printk("end of init\n"); + init_waitqueue_head(&wq); + printk("wait queue started\n"); + return 0; +} + +static void __exit mod_exit(void){ + printk( "mp3_drv.ko : Cleanup\n" ); + unregister_chrdev(DEV_MAJOR, DEV_NAME); +} + +module_init (mod_init); +module_exit (mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("K. John '2B|!2B' Crispin"); +MODULE_DESCRIPTION("vs1011 Driver for Fox Board"); + + + |