--- linux-2.4.22/drivers/sound/cmpci.c 2003-08-25 12:44:42.000000000 +0100 +++ linux-2.4.22-fixcmpci/drivers/sound/cmpci.c 2005-07-20 16:49:55.000000000 +0100 @@ -108,6 +108,7 @@ #include #include #include +#include #include "dm.h" @@ -211,7 +212,7 @@ static const unsigned sample_shift[] = { #define SND_DEV_DSP16 5 -#define NR_DEVICE 3 /* maximum number of devices */ +#define NR_DEVICE 10 /* maximum number of devices */ /*********************************************/ @@ -233,6 +234,18 @@ struct cm_state { unsigned short vol[13]; } mix; + // soundscape timing fields: input/output timestamps + struct timeval in_timestamp; + struct timeval in_timeproc; + struct timeval out_timeproc; + + // soundscape proc entries + struct proc_dir_entry *in_proc_timing; + struct proc_dir_entry *out_proc_timing; + struct proc_dir_entry *avail_proc_timing; + struct proc_dir_entry *speed_proc; + struct proc_dir_entry *proc_root; + unsigned int rateadc, ratedac; /* wave stuff */ unsigned char fmt, enable; @@ -315,6 +328,18 @@ struct cm_state { static struct cm_state *devs; static unsigned long wavetable_mem; +// soundscape +#define SOUNDSCAPE_PROC_DIR "soundscape" +#define PROC_DIR_PREFIX "interface" +#define IN_TIMING_FILE "mic_timing" +#define OUT_TIMING_FILE "spk_timing" +#define AVAIL_TIMING_FILE "avail" +#define DATE_FILE "time" +#define SPEED_FILE "speed" +struct proc_dir_entry *soundscape_proc; +struct proc_dir_entry *soundscape_procdate; +int record_time = 1; + /* --------------------------------------------------------------------- */ static inline unsigned ld2(unsigned int x) @@ -1090,6 +1115,12 @@ static void cm_update_ptr(struct cm_stat } else { hwptr = get_dmaadc(s) % s->dma_adc.dmasize; diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize; + + // soundscape: get timestamp for current sample + if(diff > 0) { + do_gettimeofday(&(s->in_timestamp)); + } + s->dma_adc.hwptr = hwptr; s->dma_adc.total_bytes += diff; s->dma_adc.count += diff; @@ -1107,6 +1138,13 @@ static void cm_update_ptr(struct cm_stat if (s->dma_dac.ready) { hwptr = get_dmadac(s) % s->dma_dac.dmasize; diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; + + // soundscape: set the output timestamp for this sample + if(record_time){ + do_gettimeofday(&(s->out_timeproc)); + record_time = 0; + } + s->dma_dac.hwptr = hwptr; s->dma_dac.total_bytes += diff; if (s->dma_dac.mapped) { @@ -1125,6 +1163,10 @@ static void cm_update_ptr(struct cm_stat if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) wake_up(&s->dma_dac.wait); } + + // soundscape: mark the end of the current sample + if(s->dma_dac.count <= 0) + record_time = 1; } } @@ -1180,6 +1222,7 @@ static void cm_interrupt(int irq, void * outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2); cm_update_ptr(s); + //printk(KERN_INFO "cm_i\n"); #ifdef CONFIG_SOUND_CMPCI_MIDI cm_handle_midi(s); #endif @@ -1576,6 +1619,12 @@ static ssize_t cm_read(struct file *file count -= cnt; buffer += cnt; ret += cnt; + + // soundscape: 2 lines of added code + // jws: moved to avail_read_timing + // s->in_timeproc.tv_sec = s->in_timestamp.tv_sec; + // s->in_timeproc.tv_usec = s->in_timestamp.tv_usec; + start_adc_unlocked(s); spin_unlock_irqrestore(&s->lock, flags); } @@ -2869,12 +2918,110 @@ static struct pci_device_id cmpci_pci_tb }; MODULE_DEVICE_TABLE(pci, cmpci_pci_tbl); +/* soundscape: proc info on when the sample was received */ +int in_proc_timing(char *buf, char **start, off_t offset, int count, + int *eof, void *data) { + + struct cm_state *s = devs; + + while(s && (int)s->dev_audio != (int)data){ + s = s->next; + } + + if(!s) return -EFAULT; + + return sprintf(buf, "%d.%06d", (int)s->in_timeproc.tv_sec, + (int)s->in_timeproc.tv_usec); +} + +/* soundscape: export through proc the timing audio signal was output. used + * for synchronous output/input operation in conjunction with the timestamp + * recorded when the signal is received. + */ +int out_proc_timing(char *buf, char **start, off_t offset, int count, + int *eof, void *data) { + + struct cm_state *s = devs; + + while(s && (int)s->dev_audio != (int)data){ + s = s->next; + } + + if(!s) return -EFAULT; + + return sprintf(buf, "%d.%06d", (int)s->out_timeproc.tv_sec, + (int)s->out_timeproc.tv_usec); +} + +int avail_proc_timing(char *buf, char **start, off_t offset, int count, int *eof, void *data) { + struct cm_state *s = devs; + unsigned long flags; + int avail; + while(s && (int)s->dev_audio != (int)data) { + s = s->next; + } + if(!s) return -EFAULT; + + spin_lock_irqsave(&s->lock, flags); + s->in_timeproc.tv_sec = s->in_timestamp.tv_sec; + s->in_timeproc.tv_usec = s->in_timestamp.tv_usec; + avail = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + return sprintf(buf, "%d", avail); +} + +#define SPW_BUFSIZE 100 +int speed_proc_write(struct file *file, const char *buffer, unsigned long count, void *data) { + struct cm_state *s = devs; + unsigned long flags; + int val; + char buf[SPW_BUFSIZE]; + + while (s && (int)s->dev_audio != (int)data) { + s = s->next; + } + if(!s) return -EFAULT; + if(count <= 0 || count >= SPW_BUFSIZE) return -EINVAL; + + memset(buf, 0, SPW_BUFSIZE); + memcpy(buf, buffer, count); + + val = -1; + sscanf(buf, "%d", &val); + if (val < 0) return -EINVAL; + + spin_lock_irqsave(&s->lock, flags); + stop_adc_unlocked(s); + s->dma_adc.ready = 0; + set_adc_rate_unlocked(s, val); + spin_unlock_irqrestore(&s->lock, flags); + return count; +} + +int speed_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) { + struct cm_state *s = devs; + while (s && (int)s->dev_audio != (int)data) { + s = s->next; + } + if(!s) return -EFAULT; + if(count <= 0) return -EINVAL; + return sprintf(buf, "%d", s->rateadc); +} + +int date_proc_timing(char *buf, char **start, off_t offset, int count, int *eof, void *data) { + struct timeval time; + do_gettimeofday(&time); + return sprintf(buf, "%d.%06d", (int)time.tv_sec, (int)time.tv_usec); +} + void initialize_chip(struct pci_dev *pcidev) { struct cm_state *s; mm_segment_t fs; int i, val; +#if defined(CONFIG_SOUND_CMPCI_MIDI) || defined(CONFIG_SOUND_CMPCI_FM) unsigned char reg_mask = 0; +#endif struct { unsigned short deviceid; char *devicename; @@ -3019,7 +3167,10 @@ void initialize_chip(struct pci_dev *pci devicename, s->iobase, s->irq); /* register devices */ if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0) + { + printk(KERN_ALERT "register_sound_dsp returned error: s->dev_audio is 0x%x\n", s->dev_audio); goto err_dev1; + } if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0) goto err_dev2; #ifdef CONFIG_SOUND_CMPCI_MIDI @@ -3047,6 +3198,38 @@ void initialize_chip(struct pci_dev *pci maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 1); s->deviceid = pcidev->device; + // soundscape: timing stuff init + do_gettimeofday(&(s->in_timestamp)); + do_gettimeofday(&(s->in_timeproc)); + s->out_timeproc.tv_sec = 0; + s->out_timeproc.tv_usec = 0; + + // soundscape: proc init + unsigned char proc_dir_name[32]; + sprintf(proc_dir_name, "%s_%d", PROC_DIR_PREFIX, s->dev_audio); + s->proc_root = create_proc_entry(proc_dir_name, S_IFDIR, + soundscape_proc); + if(s->proc_root){ + s->in_proc_timing = create_proc_read_entry(IN_TIMING_FILE, 0, + s->proc_root, in_proc_timing, NULL); + s->in_proc_timing->data = (void *)s->dev_audio; + + s->out_proc_timing = create_proc_read_entry(OUT_TIMING_FILE, 0, + s->proc_root, out_proc_timing, NULL); + s->out_proc_timing->data = (void *)s->dev_audio; + s->avail_proc_timing = create_proc_read_entry(AVAIL_TIMING_FILE, 0, s->proc_root, avail_proc_timing, NULL); + s->avail_proc_timing->data = (void *)s->dev_audio; + + s->speed_proc = create_proc_entry(SPEED_FILE, 0, s->proc_root); + s->speed_proc->write_proc = speed_proc_write; + s->speed_proc->read_proc = speed_proc_read; + s->speed_proc->data = (void *)s->dev_audio; + + } else { + printk(KERN_ALERT "cmpci: soundscape: can not create proc dir %s\n", + proc_dir_name); + } + if (pcidev->device == PCI_DEVICE_ID_CMEDIA_CM8738) { /* chip version and hw capability check */ @@ -3141,6 +3324,14 @@ static int __init init_cmpci(void) return -ENODEV; printk(KERN_INFO "cmpci: version $Revision: 5.64 $ time " __TIME__ " " __DATE__ "\n"); + // soundscape: register proc entries + soundscape_proc = create_proc_entry(SOUNDSCAPE_PROC_DIR, S_IFDIR, NULL); + if(!soundscape_proc){ + printk(KERN_ALERT "cmpci: soundscape: can not create proc root\n"); + } + + soundscape_procdate = create_proc_read_entry(DATE_FILE, 0, soundscape_proc, date_proc_timing, NULL); + while (index < NR_DEVICE && ( (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) { initialize_chip(pcidev); @@ -3170,6 +3361,18 @@ static void __exit cleanup_cmpci(void) { struct cm_state *s; + // soundscape: cleanup + s = devs; + while(s){ + remove_proc_entry(s->in_proc_timing->name, s->proc_root); + remove_proc_entry(s->out_proc_timing->name, s->proc_root); + remove_proc_entry(s->avail_proc_timing->name, s->proc_root); + remove_proc_entry(s->speed_proc->name, s->proc_root); + remove_proc_entry(s->proc_root->name, soundscape_proc); + s = s->next; + } + remove_proc_entry(soundscape_proc->name, NULL); + while ((s = devs)) { devs = devs->next; outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */