📄 hal2.c
字号:
} return 0; case SNDCTL_DSP_CHANNELS: if (get_user(val, (int *)arg)) return -EFAULT; if (val != 0) { if (file->f_mode & FMODE_READ) { hal2_stop_adc(hal2); hal2->adc.voices = (val == 1) ? 1 : 2; hal2_setup_adc(hal2); } if (file->f_mode & FMODE_WRITE) { hal2_stop_dac(hal2); hal2->dac.voices = (val == 1) ? 1 : 2; hal2_setup_dac(hal2); } } val = -EINVAL; if (file->f_mode & FMODE_READ) val = hal2->adc.voices; if (file->f_mode & FMODE_WRITE) val = hal2->dac.voices; return put_user(val, (int *)arg); case SNDCTL_DSP_GETFMTS: /* Returns a mask */ return put_user(H2_SUPPORTED_FORMATS, (int *)arg); case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ if (get_user(val, (int *)arg)) return -EFAULT; if (val != AFMT_QUERY) { if (!(val & H2_SUPPORTED_FORMATS)) return -EINVAL; if (file->f_mode & FMODE_READ) { hal2_stop_adc(hal2); hal2->adc.format = val; hal2_setup_adc(hal2); } if (file->f_mode & FMODE_WRITE) { hal2_stop_dac(hal2); hal2->dac.format = val; hal2_setup_dac(hal2); } } else { val = -EINVAL; if (file->f_mode & FMODE_READ) val = hal2->adc.format; if (file->f_mode & FMODE_WRITE) val = hal2->dac.format; } return put_user(val, (int *)arg); case SNDCTL_DSP_POST: return 0; case SNDCTL_DSP_GETOSPACE: { audio_buf_info info; int i; unsigned long flags; struct hal2_codec *dac = &hal2->dac; if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; info.fragments = 0; spin_lock_irqsave(&dac->lock, flags); for (i = 0; i < dac->desc_count; i++) if (dac->desc[i].cnt == 0) info.fragments++; spin_unlock_irqrestore(&dac->lock, flags); info.fragstotal = dac->desc_count; info.fragsize = H2_BLOCK_SIZE; info.bytes = info.fragsize * info.fragments; return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; } case SNDCTL_DSP_GETISPACE: { audio_buf_info info; int i; unsigned long flags; struct hal2_codec *adc = &hal2->adc; if (!(file->f_mode & FMODE_READ)) return -EINVAL; info.fragments = 0; info.bytes = 0; spin_lock_irqsave(&adc->lock, flags); for (i = 0; i < adc->desc_count; i++) if (adc->desc[i].cnt > 0) { info.fragments++; info.bytes += adc->desc[i].cnt; } spin_unlock_irqrestore(&adc->lock, flags); info.fragstotal = adc->desc_count; info.fragsize = H2_BLOCK_SIZE; return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; } case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETBLKSIZE: return put_user(H2_BLOCK_SIZE, (int *)arg); case SNDCTL_DSP_SETFRAGMENT: return 0; case SOUND_PCM_READ_RATE: val = -EINVAL; if (file->f_mode & FMODE_READ) val = hal2->adc.sample_rate; if (file->f_mode & FMODE_WRITE) val = hal2->dac.sample_rate; return put_user(val, (int *)arg); case SOUND_PCM_READ_CHANNELS: val = -EINVAL; if (file->f_mode & FMODE_READ) val = hal2->adc.voices; if (file->f_mode & FMODE_WRITE) val = hal2->dac.voices; return put_user(val, (int *)arg); case SOUND_PCM_READ_BITS: return put_user(16, (int *)arg); } return hal2_mixer_ioctl(hal2, cmd, arg);}static ssize_t hal2_read(struct file *file, char *buffer, size_t count, loff_t *ppos){ ssize_t err; struct hal2_card *hal2 = (struct hal2_card *) file->private_data; struct hal2_codec *adc = &hal2->adc; if (!count) return 0; if (mutex_lock_interruptible(&adc->sem)) return -EINTR; if (file->f_flags & O_NONBLOCK) { err = hal2_get_buffer(hal2, buffer, count); err = err == 0 ? -EAGAIN : err; } else { do { /* ~10% longer */ signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * adc->voices * HZ / adc->sample_rate / 900; unsigned long flags; DECLARE_WAITQUEUE(wait, current); ssize_t cnt = 0; err = hal2_get_buffer(hal2, buffer, count); if (err > 0) { count -= err; cnt += err; buffer += err; err = cnt; } if (count > 0 && err >= 0) { add_wait_queue(&adc->dma_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(timeout); spin_lock_irqsave(&adc->lock, flags); if (!adc->desc[adc->tail].cnt) err = -EAGAIN; spin_unlock_irqrestore(&adc->lock, flags); if (signal_pending(current)) err = -ERESTARTSYS; remove_wait_queue(&adc->dma_wait, &wait); if (err < 0) { hal2_stop_adc(hal2); hal2_reset_adc_pointer(hal2); } } } while (count > 0 && err >= 0); } mutex_unlock(&adc->sem); return err;}static ssize_t hal2_write(struct file *file, const char *buffer, size_t count, loff_t *ppos){ ssize_t err; char *buf = (char*) buffer; struct hal2_card *hal2 = (struct hal2_card *) file->private_data; struct hal2_codec *dac = &hal2->dac; if (!count) return 0; if (mutex_lock_interruptible(&dac->sem)) return -EINTR; if (file->f_flags & O_NONBLOCK) { err = hal2_add_buffer(hal2, buf, count); err = err == 0 ? -EAGAIN : err; } else { do { /* ~10% longer */ signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * dac->voices * HZ / dac->sample_rate / 900; unsigned long flags; DECLARE_WAITQUEUE(wait, current); ssize_t cnt = 0; err = hal2_add_buffer(hal2, buf, count); if (err > 0) { count -= err; cnt += err; buf += err; err = cnt; } if (count > 0 && err >= 0) { add_wait_queue(&dac->dma_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(timeout); spin_lock_irqsave(&dac->lock, flags); if (dac->desc[dac->head].cnt) err = -EAGAIN; spin_unlock_irqrestore(&dac->lock, flags); if (signal_pending(current)) err = -ERESTARTSYS; remove_wait_queue(&dac->dma_wait, &wait); if (err < 0) { hal2_stop_dac(hal2); hal2_reset_dac_pointer(hal2); } } } while (count > 0 && err >= 0); } mutex_unlock(&dac->sem); return err;}static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait){ unsigned long flags; unsigned int mask = 0; struct hal2_card *hal2 = (struct hal2_card *) file->private_data; if (file->f_mode & FMODE_READ) { struct hal2_codec *adc = &hal2->adc; poll_wait(file, &adc->dma_wait, wait); spin_lock_irqsave(&adc->lock, flags); if (adc->desc[adc->tail].cnt > 0) mask |= POLLIN; spin_unlock_irqrestore(&adc->lock, flags); } if (file->f_mode & FMODE_WRITE) { struct hal2_codec *dac = &hal2->dac; poll_wait(file, &dac->dma_wait, wait); spin_lock_irqsave(&dac->lock, flags); if (dac->desc[dac->head].cnt == 0) mask |= POLLOUT; spin_unlock_irqrestore(&dac->lock, flags); } return mask;}static int hal2_open(struct inode *inode, struct file *file){ int err; struct hal2_card *hal2 = hal2_dsp_find_card(iminor(inode)); if (!hal2) return -ENODEV; file->private_data = hal2; if (file->f_mode & FMODE_READ) { struct hal2_codec *adc = &hal2->adc; if (adc->usecount) return -EBUSY; /* OSS spec wanted us to use 8 bit, 8 kHz mono by default, * but HAL2 can't do 8bit audio */ adc->format = AFMT_S16_BE; adc->voices = 1; adc->sample_rate = hal2_compute_rate(adc, 8000); hal2_set_adc_rate(hal2); err = hal2_alloc_adc_dmabuf(adc); if (err) return err; hal2_setup_adc(hal2); adc->usecount++; } if (file->f_mode & FMODE_WRITE) { struct hal2_codec *dac = &hal2->dac; if (dac->usecount) return -EBUSY; dac->format = AFMT_S16_BE; dac->voices = 1; dac->sample_rate = hal2_compute_rate(dac, 8000); hal2_set_dac_rate(hal2); err = hal2_alloc_dac_dmabuf(dac); if (err) return err; hal2_setup_dac(hal2); dac->usecount++; } return nonseekable_open(inode, file);}static int hal2_release(struct inode *inode, struct file *file){ struct hal2_card *hal2 = (struct hal2_card *) file->private_data; if (file->f_mode & FMODE_READ) { struct hal2_codec *adc = &hal2->adc; mutex_lock(&adc->sem); hal2_stop_adc(hal2); hal2_free_adc_dmabuf(adc); adc->usecount--; mutex_unlock(&adc->sem); } if (file->f_mode & FMODE_WRITE) { struct hal2_codec *dac = &hal2->dac; mutex_lock(&dac->sem); hal2_sync_dac(hal2); hal2_free_dac_dmabuf(dac); dac->usecount--; mutex_unlock(&dac->sem); } return 0;}static const struct file_operations hal2_audio_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = hal2_read, .write = hal2_write, .poll = hal2_poll, .ioctl = hal2_ioctl, .open = hal2_open, .release = hal2_release,};static const struct file_operations hal2_mixer_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = hal2_ioctl_mixdev, .open = hal2_open_mixdev, .release = hal2_release_mixdev,};static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3, int index){ codec->pbus.pbusnr = index; codec->pbus.pbus = &hpc3->pbdma[index]; init_waitqueue_head(&codec->dma_wait); mutex_init(&codec->sem); spin_lock_init(&codec->lock);}static int hal2_detect(struct hal2_card *hal2){ unsigned short board, major, minor; unsigned short rev; /* reset HAL2 */ hal2_isr_write(hal2, 0); /* release reset */ hal2_isr_write(hal2, H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N); hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE); if ((rev = hal2_rev_look(hal2)) & H2_REV_AUDIO_PRESENT) return -ENODEV; board = (rev & H2_REV_BOARD_M) >> 12; major = (rev & H2_REV_MAJOR_CHIP_M) >> 4; minor = (rev & H2_REV_MINOR_CHIP_M); printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n", board, major, minor); return 0;}static int hal2_init_card(struct hal2_card **phal2, struct hpc3_regs *hpc3){ int ret = 0; struct hal2_card *hal2; hal2 = kzalloc(sizeof(struct hal2_card), GFP_KERNEL); if (!hal2) return -ENOMEM; hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0]; hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1]; hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2]; hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3]; if (hal2_detect(hal2) < 0) { ret = -ENODEV; goto free_card; } hal2_init_codec(&hal2->dac, hpc3, 0); hal2_init_codec(&hal2->adc, hpc3, 1); /* * All DMA channel interfaces in HAL2 are designed to operate with * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles * in D5. HAL2 is a 16-bit device which can accept both big and little * endian format. It assumes that even address bytes are on high * portion of PBUS (15:8) and assumes that HPC3 is programmed to * accept a live (unsynchronized) version of P_DREQ_N from HAL2. */#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \ (2 << HPC3_DMACFG_D4R_SHIFT) | \ (2 << HPC3_DMACFG_D5R_SHIFT) | \ (0 << HPC3_DMACFG_D3W_SHIFT) | \ (2 << HPC3_DMACFG_D4W_SHIFT) | \ (2 << HPC3_DMACFG_D5W_SHIFT) | \ HPC3_DMACFG_DS16 | \ HPC3_DMACFG_EVENHI | \ HPC3_DMACFG_RTIME | \ (8 << HPC3_DMACFG_BURST_SHIFT) | \ HPC3_DMACFG_DRQLIVE) /* * Ignore what's mentioned in the specification and write value which * works in The Real World (TM) */ hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844; hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844; if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED, hal2str, hal2)) { printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ); ret = -EAGAIN; goto free_card; } hal2->dev_dsp = register_sound_dsp(&hal2_audio_fops, -1); if (hal2->dev_dsp < 0) { ret = hal2->dev_dsp; goto free_irq; } hal2->dev_mixer = register_sound_mixer(&hal2_mixer_fops, -1); if (hal2->dev_mixer < 0) { ret = hal2->dev_mixer; goto unregister_dsp; } hal2_init_mixer(hal2); *phal2 = hal2; return 0;unregister_dsp: unregister_sound_dsp(hal2->dev_dsp);free_irq: free_irq(SGI_HPCDMA_IRQ, hal2);free_card: kfree(hal2); return ret;}extern void (*indy_volume_button)(int);/* * Assuming only one HAL2 card. Mail me if you ever meet machine with * more than one. */static int __init init_hal2(void){ int i, error; for (i = 0; i < MAXCARDS; i++) hal2_card[i] = NULL; error = hal2_init_card(&hal2_card[0], hpc3c0); /* let Indy's volume buttons work */ if (!error && !ip22_is_fullhouse()) indy_volume_button = hal2_volume_control; return error;}static void __exit exit_hal2(void){ int i; /* unregister volume butons callback function */ indy_volume_button = NULL; for (i = 0; i < MAXCARDS; i++) if (hal2_card[i]) { free_irq(SGI_HPCDMA_IRQ, hal2_card[i]); unregister_sound_dsp(hal2_card[i]->dev_dsp); unregister_sound_mixer(hal2_card[i]->dev_mixer); kfree(hal2_card[i]); }}module_init(init_hal2);module_exit(exit_hal2);MODULE_DESCRIPTION("OSS compatible driver for SGI HAL2 audio");MODULE_AUTHOR("Ladislav Michl");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -