added libjson-c. added driver, webinterface and userspace daemon for the
[openwrt/svn-archive/archive.git] / package / fonera-mp3-drv / src / mp3_drv.c
1 /*
2 * a.lp_mp3 - VS1011B driver for Fonera
3 * Copyright (c) 2007 phrozen.org - John Crispin <john@phrozen.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
18 *
19 * Feedback, Bugs... john@phrozen.org
20 *
21 */
22
23
24 #include <linux/module.h>
25 #include <linux/errno.h>
26 #include <linux/ioport.h>
27 #include <linux/init.h>
28 #include <asm/uaccess.h>
29 #include <asm/io.h>
30 #include <linux/timer.h>
31 #include <linux/init.h>
32 #include <linux/genhd.h>
33
34 // do we want debuging info ?
35 #if 0
36 #define DBG(x) x
37 #else
38 #define DBG(x)
39 #endif
40
41 #define MP3_CHUNK_SIZE 4096
42 #define MP3_BUFFERING 0
43 #define MP3_PLAYING 1
44 #define MP3_BUFFER_FINISHED 2
45 #define MP3_PLAY_FINISHED 3
46 typedef struct _MP3_DATA{
47 unsigned char mp3[MP3_CHUNK_SIZE];
48 unsigned char state;
49 } MP3_DATA;
50
51 #define IOCTL_MP3_INIT 0x01
52 #define IOCTL_MP3_RESET 0x02
53 #define IOCTL_MP3_SETVOLUME 0x03
54 #define IOCTL_MP3_GETVOLUME 0x04
55
56 typedef struct _AUDIO_DATA{
57 unsigned int bitrate;
58 unsigned int sample_rate;
59 unsigned char is_stereo;
60 }AUDIO_DATA;
61 #define IOCTL_MP3_GETAUDIODATA 0x05
62 #define IOCTL_MP3_CLEARBUFFER 0x06
63 #define IOCTL_MP3_PLAY 0x07
64
65 typedef struct _MP3_BEEP{
66 unsigned char freq;
67 unsigned int ms;
68 } MP3_BEEP;
69 #define IOCTL_MP3_BEEP 0x08
70 #define IOCTL_MP3_END_REACHED 0x09
71 #define IOCTL_MP3_BASS 0x10
72
73 #define CRYSTAL12288 0x9800
74 #define CRYSTAL24576 0x0
75
76 #define DEV_NAME "mp3"
77 #define DEV_MAJOR 196
78 #define MAX_MP3_COUNT 1
79
80 typedef struct _mp3_inf{
81 unsigned char is_open;
82 } mp3_inf;
83 static mp3_inf mp3_info[MAX_MP3_COUNT];
84
85 #define MP3_BUFFER_SIZE (128 * 1024)
86 unsigned char mp3_buffer[MP3_BUFFER_SIZE];
87
88 static unsigned long int mp3_buffer_offset_write = 0;
89 static unsigned long int mp3_buffer_offset_read = 0;
90 static unsigned char mp3_buffering_status = MP3_BUFFERING;
91 static unsigned long int mp3_data_in_buffer = 0;
92 static int mp3_thread = 0;
93 unsigned int crystal_freq;
94
95 #include "vs10xx.c"
96
97 static wait_queue_head_t wq;
98 static DECLARE_COMPLETION(mp3_exit);
99
100 static int mp3_playback_thread(void *data){
101 int j;
102 unsigned long timeout;
103 printk("started kthread\n");
104 daemonize("kmp3");
105 while(mp3_buffering_status != MP3_PLAY_FINISHED){
106 if((mp3_buffering_status == MP3_PLAYING) || (mp3_buffering_status == MP3_BUFFER_FINISHED)){
107 while(VS1011_NEEDS_DATA){
108 if(mp3_buffer_offset_read == MP3_BUFFER_SIZE){
109 mp3_buffer_offset_read = 0;
110 }
111
112 if(mp3_data_in_buffer == 0){
113 if(mp3_buffering_status == MP3_BUFFER_FINISHED){
114 printk("mp3_drv.ko : finished playing\n");
115 mp3_buffering_status = MP3_PLAY_FINISHED;
116 } else {
117 printk("mp3_drv.ko : buffer empty ?\n");
118 if(mp3_buffering_status != MP3_PLAY_FINISHED){
119 }
120 }
121 } else {
122 for(j = 0; j < 32; j++){
123 VS1011_send_SDI(mp3_buffer[mp3_buffer_offset_read + j]);
124 }
125 mp3_buffer_offset_read += 32;
126 mp3_data_in_buffer -= 32;
127 }
128 }
129 }
130 timeout = 1;
131 timeout = wait_event_interruptible_timeout(wq, (timeout==0), timeout);
132 }
133 complete_and_exit(&mp3_exit, 0);
134 }
135
136 static ssize_t module_write(struct file * file, const char * buffer, size_t count, loff_t *offset){
137 MP3_DATA mp3_data;
138
139 copy_from_user((char*) &mp3_data, buffer, sizeof(MP3_DATA));
140
141 if(mp3_data.state == MP3_BUFFER_FINISHED){
142 mp3_buffering_status = MP3_BUFFER_FINISHED;
143 DBG(printk("mp3_drv.ko : file end reached\n"));
144 return 1;
145 }
146
147 if(mp3_data.state == MP3_PLAY_FINISHED){
148 mp3_buffering_status = MP3_PLAY_FINISHED;
149 mp3_data_in_buffer = 0;
150 DBG(printk("mp3_drv.ko : stop playing\n"));
151 return 1;
152 }
153
154 if(mp3_data_in_buffer + MP3_CHUNK_SIZE >= MP3_BUFFER_SIZE){
155 DBG(printk("mp3_drv.ko : buffer is full? %ld\n", mp3_data_in_buffer);)
156 return 0;
157 }
158
159 if(mp3_buffer_offset_write == MP3_BUFFER_SIZE){
160 mp3_buffer_offset_write = 0;
161 }
162
163 memcpy(&mp3_buffer[mp3_buffer_offset_write], mp3_data.mp3, MP3_CHUNK_SIZE);
164 mp3_buffer_offset_write += MP3_CHUNK_SIZE;
165 mp3_buffering_status = mp3_data.state;
166 mp3_data_in_buffer += MP3_CHUNK_SIZE;
167 return 1;
168 }
169
170 static int module_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){
171 unsigned int retval = 0;
172 AUDIO_DATA audio_data;
173 MP3_BEEP mp3_beep;
174 DBG(printk("mp3_drv.ko : Ioctl Called (cmd=%d)\n", cmd );)
175 switch (cmd) {
176 case IOCTL_MP3_INIT:
177 crystal_freq = arg;
178 VS1011_init(crystal_freq, 1);
179 VS1011_print_registers();
180 break;
181
182 case IOCTL_MP3_RESET:
183 DBG(printk("mp3_drv.ko : doing a sw reset\n");)
184 VS1011_init(crystal_freq, 0);
185 VS1011_print_registers();
186 VS1011_send_zeros(0x20);
187 break;
188
189 case IOCTL_MP3_SETVOLUME:
190 DBG(printk("mp3_drv.ko : setting volume to : %lu\n", arg&0xffff);)
191 VS1011_set_volume(arg);
192 break;
193
194 case IOCTL_MP3_GETVOLUME:
195 retval = VS1011_get_volume();
196 DBG(printk("mp3_drv.ko : read volume : %d\n", retval);)
197 break;
198
199 case IOCTL_MP3_GETAUDIODATA:
200 DBG(printk("mp3_drv.ko : read audio data\n");)
201 VS1011_get_audio_data(&audio_data);
202 copy_to_user((char*)arg, (char*)&audio_data, sizeof(AUDIO_DATA));
203 break;
204
205 case IOCTL_MP3_CLEARBUFFER:
206 DBG(printk("mp3_drv.ko : clearing buffer\n");)
207 mp3_buffer_offset_read = 0;
208 mp3_buffer_offset_write = 0;
209 mp3_buffering_status = MP3_PLAY_FINISHED;
210 mp3_data_in_buffer = 0;
211 break;
212
213 case IOCTL_MP3_PLAY:
214 mp3_thread = kernel_thread(mp3_playback_thread, NULL, CLONE_KERNEL);
215 break;
216
217 case IOCTL_MP3_BEEP:
218 copy_from_user((char*)&mp3_beep, (char*)arg, sizeof(MP3_BEEP));
219 VS1011_sine(1,mp3_beep.freq);
220 msDelay(mp3_beep.ms);
221 VS1011_sine(0,0);
222 break;
223
224 case IOCTL_MP3_END_REACHED:
225 if(mp3_buffering_status == MP3_PLAY_FINISHED){
226 retval = 1;
227 }
228 break;
229
230 case IOCTL_MP3_BASS:
231 VS1011_set_bass(arg);
232 break;
233
234 default:
235 printk("mp3_drv.ko : unknown ioctl\n");
236 break;
237
238 }
239 return retval;
240 }
241
242 static int module_open(struct inode *inode, struct file *file){
243 unsigned int dev_minor = MINOR(inode->i_rdev);
244 if(dev_minor != 0){
245 printk("mp3_drv.ko : trying to access unknown minor device -> %d\n", dev_minor);
246 return -ENODEV;
247 }
248 if(mp3_info[dev_minor].is_open) {
249 printk("mp3_drv.ko : Device with minor ID %d already in use\n", dev_minor);
250 return -EBUSY;
251 }
252 mp3_info[dev_minor].is_open = 1;
253
254 mp3_buffering_status = MP3_PLAY_FINISHED;
255 printk("mp3_drv.ko : Minor %d has been opened\n", dev_minor);
256 return 0;
257 }
258
259 static int module_close(struct inode * inode, struct file * file){
260 unsigned int dev_minor = MINOR(inode->i_rdev);
261 mp3_info[dev_minor].is_open = 0;
262 printk("mp3_drv.ko : Minor %d has been closed\n", dev_minor);
263 mp3_buffering_status = MP3_PLAY_FINISHED;
264 return 0;
265 }
266
267 struct file_operations modulemp3_fops = {
268 write: module_write,
269 ioctl: module_ioctl,
270 open: module_open,
271 release: module_close
272 };
273
274 static int __init mod_init(void){
275 printk("mp3_drv.ko : VS1011b Driver\n");
276 printk("mp3_drv.ko : Made by John '2B|!2B' Crispin (john@phrozen.org)\n");
277 printk("mp3_drv.ko : Starting ...\n");
278
279 if(register_chrdev(DEV_MAJOR, DEV_NAME, &modulemp3_fops)) {
280 printk( "mp3_drv.ko : Error whilst opening %s (%d)\n", DEV_NAME, DEV_MAJOR);
281 return( -ENODEV );
282 }
283
284 mp3_info[0].is_open = 0;
285 printk("mp3_drv.ko : Device %s registered for major ID %d\n", DEV_NAME, DEV_MAJOR);
286 crystal_freq = CRYSTAL12288;
287 VS1011_init(crystal_freq, 1);
288 VS1011_print_registers();
289 printk("end of init\n");
290 init_waitqueue_head(&wq);
291 printk("wait queue started\n");
292 return 0;
293 }
294
295 static void __exit mod_exit(void){
296 printk( "mp3_drv.ko : Cleanup\n" );
297 unregister_chrdev(DEV_MAJOR, DEV_NAME);
298 }
299
300 module_init (mod_init);
301 module_exit (mod_exit);
302
303 MODULE_LICENSE("GPL");
304 MODULE_AUTHOR("K. John '2B|!2B' Crispin");
305 MODULE_DESCRIPTION("vs1011 Driver for Fox Board");
306
307
308