📄 harmony.c
字号:
if (harmony.nb_filled_record < 2) return -EBUSY; buf_to_read = harmony.first_filled_record; /* Copy the page to an aligned buffer */ if (copy_to_user(buffer+count, recorded_buf.addr + (HARMONY_BUF_SIZE*buf_to_read), HARMONY_BUF_SIZE)) { count = -EFAULT; break; } harmony.nb_filled_record--; harmony.first_filled_record++; harmony.first_filled_record %= MAX_BUFS; count += HARMONY_BUF_SIZE; } return count;}/* * Here is the place where we try to recognize file format. * Sun/NeXT .au files begin with the string .snd * At offset 12 is specified the encoding. * At offset 16 is specified speed rate * At Offset 20 is specified the numbers of voices */#define four_bytes_to_u32(start) (file_header[start] << 24)|\ (file_header[start+1] << 16)|\ (file_header[start+2] << 8)|\ (file_header[start+3]);#define test_rate(tested,real_value,harmony_value) if ((tested)<=(real_value))\ static int harmony_format_auto_detect(const char *buffer, int block_size){ u8 file_header[24]; u32 start_string; int ret = 0; if (block_size>24) { if (copy_from_user(file_header, buffer, sizeof(file_header))) ret = -EFAULT; start_string = four_bytes_to_u32(0); if ((file_header[4]==0) && (start_string==0x2E736E64)) { u32 format; u32 nb_voices; u32 speed; format = four_bytes_to_u32(12); nb_voices = four_bytes_to_u32(20); speed = four_bytes_to_u32(16); switch (format) { case HARMONY_MAGIC_8B_ULAW: harmony.data_format = HARMONY_DF_8BIT_ULAW; break; case HARMONY_MAGIC_8B_ALAW: harmony.data_format = HARMONY_DF_8BIT_ALAW; break; case HARMONY_MAGIC_16B_LINEAR: harmony.data_format = HARMONY_DF_16BIT_LINEAR; break; default: harmony_set_control(HARMONY_DF_16BIT_LINEAR, HARMONY_SR_44KHZ, HARMONY_SS_STEREO); goto out; } switch (nb_voices) { case HARMONY_MAGIC_MONO: harmony.stereo_select = HARMONY_SS_MONO; break; case HARMONY_MAGIC_STEREO: harmony.stereo_select = HARMONY_SS_STEREO; break; default: harmony.stereo_select = HARMONY_SS_MONO; break; } harmony_set_rate(harmony_detect_rate(&speed)); harmony.dac_rate = speed; goto out; } } harmony_set_control(HARMONY_DF_8BIT_ULAW, HARMONY_SR_8KHZ, HARMONY_SS_MONO);out: return ret;}#undef four_bytes_to_u32static ssize_t harmony_audio_write(struct file *file, const char *buffer, size_t size_count, loff_t *ppos){ int total_count = (int) size_count; int count = 0; int frame_size; int buf_to_fill; int fresh_buffer; if (!harmony.format_initialized) { if (harmony_format_auto_detect(buffer, total_count)) return -EFAULT; } while (count<total_count) { /* Wait until we're out of control mode */ harmony_wait_CNTL(); /* Figure out which buffer to fill in */ if (harmony.nb_filled_play+2 >= MAX_BUFS && !harmony.play_offset) { harmony.blocked_playing = 1; interruptible_sleep_on(&harmony.wq_play); harmony.blocked_playing = 0; } if (harmony.nb_filled_play+2 >= MAX_BUFS && !harmony.play_offset) return -EBUSY; buf_to_fill = (harmony.first_filled_play+harmony.nb_filled_play); if (harmony.play_offset) { buf_to_fill--; buf_to_fill += MAX_BUFS; } buf_to_fill %= MAX_BUFS; fresh_buffer = (harmony.play_offset == 0); /* Figure out the size of the frame */ if ((total_count-count) >= HARMONY_BUF_SIZE - harmony.play_offset) { frame_size = HARMONY_BUF_SIZE - harmony.play_offset; } else { frame_size = total_count - count; /* Clear out the buffer, since there we'll only be overlaying part of the old buffer with the new one */ harmony_silence(&played_buf, HARMONY_BUF_SIZE*buf_to_fill+frame_size+harmony.play_offset, HARMONY_BUF_SIZE-frame_size-harmony.play_offset); } /* Copy the page to an aligned buffer */ if (copy_from_user(played_buf.addr +(HARMONY_BUF_SIZE*buf_to_fill) + harmony.play_offset, buffer+count, frame_size)) return -EFAULT; CHECK_WBACK_INV_OFFSET(played_buf, (HARMONY_BUF_SIZE*buf_to_fill + harmony.play_offset), frame_size); if (fresh_buffer) harmony.nb_filled_play++; count += frame_size; harmony.play_offset += frame_size; harmony.play_offset %= HARMONY_BUF_SIZE; if (harmony.suspended_playing && (harmony.nb_filled_play>=4)) harmony_enable_interrupts(); } return count;}static unsigned int harmony_audio_poll(struct file *file, struct poll_table_struct *wait){ unsigned int mask = 0; if (file->f_mode & FMODE_READ) { if (!harmony.suspended_recording) poll_wait(file, &harmony.wq_record, wait); if (harmony.nb_filled_record) mask |= POLLIN | POLLRDNORM; } if (file->f_mode & FMODE_WRITE) { if (!harmony.suspended_playing) poll_wait(file, &harmony.wq_play, wait); if (harmony.nb_filled_play) mask |= POLLOUT | POLLWRNORM; } return mask;}static int harmony_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int ival, new_format; int frag_size, frag_buf; struct audio_buf_info info; switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, (int *) arg); case SNDCTL_DSP_GETCAPS: ival = DSP_CAP_DUPLEX; return put_user(ival, (int *) arg); case SNDCTL_DSP_GETFMTS: ival = (AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW ); return put_user(ival, (int *) arg); case SNDCTL_DSP_SETFMT: if (get_user(ival, (int *) arg)) return -EFAULT; if (ival != AFMT_QUERY) { switch (ival) { case AFMT_MU_LAW: new_format = HARMONY_DF_8BIT_ULAW; break; case AFMT_A_LAW: new_format = HARMONY_DF_8BIT_ALAW; break; case AFMT_S16_BE: new_format = HARMONY_DF_16BIT_LINEAR; break; default: { DPRINTK(KERN_WARNING PFX "unsupported sound format 0x%04x requested.\n", ival); ival = AFMT_S16_BE; return put_user(ival, (int *) arg); } } harmony_set_format(new_format); return 0; } else { switch (harmony.data_format) { case HARMONY_DF_8BIT_ULAW: ival = AFMT_MU_LAW; break; case HARMONY_DF_8BIT_ALAW: ival = AFMT_A_LAW; break; case HARMONY_DF_16BIT_LINEAR: ival = AFMT_U16_BE; break; default: ival = 0; } return put_user(ival, (int *) arg); } case SOUND_PCM_READ_RATE: ival = harmony.dac_rate; return put_user(ival, (int *) arg); case SNDCTL_DSP_SPEED: if (get_user(ival, (int *) arg)) return -EFAULT; harmony_set_rate(harmony_detect_rate(&ival)); harmony.dac_rate = ival; return put_user(ival, (int*) arg); case SNDCTL_DSP_STEREO: if (get_user(ival, (int *) arg)) return -EFAULT; if (ival != 0 && ival != 1) return -EINVAL; harmony_set_stereo(ival); return 0; case SNDCTL_DSP_CHANNELS: if (get_user(ival, (int *) arg)) return -EFAULT; if (ival != 1 && ival != 2) { ival = harmony.stereo_select == HARMONY_SS_MONO ? 1 : 2; return put_user(ival, (int *) arg); } harmony_set_stereo(ival-1); return 0; case SNDCTL_DSP_GETBLKSIZE: ival = HARMONY_BUF_SIZE; return put_user(ival, (int *) arg); case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_RESET: if (!harmony.suspended_recording) { /* TODO: stop_recording() */ } return 0; case SNDCTL_DSP_SETFRAGMENT: if (get_user(ival, (int *)arg)) return -EFAULT; frag_size = ival & 0xffff; frag_buf = (ival>>16) & 0xffff; /* TODO: We use hardcoded fragment sizes and numbers for now */ frag_size = 12; /* 4096 == 2^12 */ frag_buf = MAX_BUFS; ival = (frag_buf << 16) + frag_size; return put_user(ival, (int *) arg); case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; info.fragstotal = MAX_BUFS; info.fragments = MAX_BUFS - harmony.nb_filled_play; info.fragsize = HARMONY_BUF_SIZE; info.bytes = info.fragments * info.fragsize; return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; info.fragstotal = MAX_BUFS; info.fragments = /*MAX_BUFS-*/ harmony.nb_filled_record; info.fragsize = HARMONY_BUF_SIZE; info.bytes = info.fragments * info.fragsize; return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; case SNDCTL_DSP_SYNC: return 0; } return -EINVAL;}/* * harmony_interrupt() * * harmony interruption service routine * */static irqreturn_t harmony_interrupt(int irq, void *dev, struct pt_regs *regs){ u32 dstatus; struct harmony_hpa *hpa; /* Setup the hpa */ hpa = ((struct harmony_dev *)dev)->hpa; harmony_wait_CNTL(); /* Read dstatus and pcuradd (the current address) */ dstatus = gsc_readl(&hpa->dstatus); /* Turn off interrupts */ harmony_disable_interrupts(); /* Check if this is a request to get the next play buffer */ if (dstatus & DSTATUS_PN) { if (!harmony.nb_filled_play) { harmony.suspended_playing = 1; gsc_writel((unsigned long)silent.dma_handle, &hpa->pnxtadd); if (!harmony.suspended_recording) harmony_enable_interrupts(); } else { harmony.suspended_playing = 0; gsc_writel((unsigned long)played_buf.dma_handle + (HARMONY_BUF_SIZE*harmony.first_filled_play), &hpa->pnxtadd); harmony.first_filled_play++; harmony.first_filled_play %= MAX_BUFS; harmony.nb_filled_play--; harmony_enable_interrupts(); } if (harmony.blocked_playing) wake_up_interruptible(&harmony.wq_play); } /* Check if we're being asked to fill in a recording buffer */ if (dstatus & DSTATUS_RN) { if((harmony.nb_filled_record+2>=MAX_BUFS) || harmony.suspended_recording) { harmony.nb_filled_record = 0; harmony.first_filled_record = 0; harmony.suspended_recording = 1; gsc_writel((unsigned long)graveyard.dma_handle, &hpa->rnxtadd); if (!harmony.suspended_playing) harmony_enable_interrupts(); } else { int buf_to_fill; buf_to_fill = (harmony.first_filled_record+harmony.nb_filled_record) % MAX_BUFS; CHECK_WBACK_INV_OFFSET(recorded_buf, HARMONY_BUF_SIZE*buf_to_fill, HARMONY_BUF_SIZE); gsc_writel((unsigned long)recorded_buf.dma_handle + HARMONY_BUF_SIZE*buf_to_fill, &hpa->rnxtadd); harmony.nb_filled_record++; harmony_enable_interrupts(); } if (harmony.blocked_recording && harmony.nb_filled_record>3) wake_up_interruptible(&harmony.wq_record); } return IRQ_HANDLED;}/* * Sound playing functions */static struct file_operations harmony_audio_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = harmony_audio_read, .write = harmony_audio_write, .poll = harmony_audio_poll, .ioctl = harmony_audio_ioctl, .open = harmony_audio_open, .release = harmony_audio_release,};static int harmony_audio_init(void){ /* Request that IRQ */ if (request_irq(harmony.dev->irq, harmony_interrupt, 0 ,"harmony", &harmony)) { printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony.dev->irq); return -EFAULT; } harmony.dsp_unit = register_sound_dsp(&harmony_audio_fops, -1); if (harmony.dsp_unit < 0) { printk(KERN_ERR PFX "Error registering dsp\n"); free_irq(harmony.dev->irq, &harmony); return -EFAULT; } /* Clear the buffers so you don't end up with crap in the buffers. */ harmony_silence(&played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); /* Make sure this makes it to cache */ CHECK_WBACK_INV_OFFSET(played_buf, 0, HARMONY_BUF_SIZE*MAX_BUFS); /* Clear out the silent buffer and flush to cache */ harmony_silence(&silent, 0, HARMONY_BUF_SIZE); CHECK_WBACK_INV_OFFSET(silent, 0, HARMONY_BUF_SIZE); harmony.audio_open = 0; return 0;}/* * mixer functions
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -