📄 sm_wss.c
字号:
/* --------------------------------------------------------------------- */static int wss_close(struct net_device *dev, struct sm_state *sm) { if (!dev || !sm) return -EINVAL; /* * disable interrupts */ disable_dma(dev->dma); write_codec(dev, 9, 0xc); /* disable codec */ free_irq(dev->irq, dev); free_dma(dev->dma); release_region(dev->base_addr, WSS_EXTENT); kfree(sm->dma.obuf); return 0;}/* --------------------------------------------------------------------- */static int wss_sethw(struct net_device *dev, struct sm_state *sm, char *mode){ char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; int i, j; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; sm->mode_rx = NULL; return 0; } if (cp) *cp++ = '\0'; else cp = mode; for (; *mtp; mtp++) { if ((*mtp)->loc_storage > sizeof(sm->m)) { printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", sm_drvname, (*mtp)->name, (*mtp)->loc_storage); continue; } if (!(*mtp)->name || strcmp((*mtp)->name, mode)) continue; if ((i = wss_srate_index((*mtp)->srate)) < 0) continue; for (mrp = sm_modem_rx_table; *mrp; mrp++) { if ((*mrp)->loc_storage > sizeof(sm->d)) { printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", sm_drvname, (*mrp)->name, (*mrp)->loc_storage); continue; } if ((*mrp)->name && !strcmp((*mrp)->name, cp) && ((j = wss_srate_index((*mrp)->srate)) >= 0)) { sm->mode_tx = *mtp; sm->mode_rx = *mrp; SCSTATE->fmt[0] = j; SCSTATE->fmt[1] = i; sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; if (sm->dma.ifragsz < sm->mode_rx->overlap) sm->dma.ifragsz = sm->mode_rx->overlap; /* prefer same data format if possible to minimize switching times */ sm->dma.i16bit = sm->dma.o16bit = 2; if (sm->mode_rx->srate == sm->mode_tx->srate) { if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16) sm->dma.i16bit = sm->dma.o16bit = 1; else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8) sm->dma.i16bit = sm->dma.o16bit = 0; } if (sm->dma.i16bit == 2) { if (sm->mode_rx->demodulator_s16) sm->dma.i16bit = 1; else if (sm->mode_rx->demodulator_u8) sm->dma.i16bit = 0; } if (sm->dma.o16bit == 2) { if (sm->mode_tx->modulator_s16) sm->dma.o16bit = 1; else if (sm->mode_tx->modulator_u8) sm->dma.o16bit = 0; } if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, sm->mode_rx->name, sm->mode_tx->name); sm->mode_tx = NULL; sm->mode_rx = NULL; return -EINVAL; }#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ if (sm->dma.i16bit) { SCSTATE->fmt[0] |= 0xc0; sm->dma.ifragsz <<= 1; } if (sm->dma.o16bit) { SCSTATE->fmt[1] |= 0xc0; sm->dma.ofragsz <<= 1; }#else /* __BIG_ENDIAN */ if (sm->dma.i16bit) { SCSTATE->fmt[0] |= 0x40; sm->dma.ifragsz <<= 1; } if (sm->dma.o16bit) { SCSTATE->fmt[1] |= 0x40; sm->dma.ofragsz <<= 1; }#endif /* __BIG_ENDIAN */ return 0; } } } return -EINVAL;}/* --------------------------------------------------------------------- */static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, struct hdlcdrv_ioctl *hi, int cmd){ struct sm_ioctl bi; int i; if (cmd != SIOCDEVPRIVATE) return -ENOIOCTLCMD; if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) return -EFAULT; switch (bi.cmd) { default: return -ENOIOCTLCMD; case SMCTL_GETMIXER: i = 0; bi.data.mix.sample_rate = sm->mode_rx->srate; bi.data.mix.bit_rate = sm->hdrv.par.bitrate; bi.data.mix.mixer_type = SCSTATE->crystal ? SM_MIXER_CRYSTAL : SM_MIXER_AD1848; if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) >> bi.data.mix.reg) & 1) { bi.data.mix.data = read_codec(dev, bi.data.mix.reg); i = 1; } if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) return -EFAULT; return i; case SMCTL_SETMIXER: if (!capable(CAP_SYS_RAWIO)) return -EACCES; if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || !SCSTATE->crystal) && (bi.data.mix.mixer_type != SM_MIXER_AD1848 || bi.data.mix.reg >= 0x10)) return -EINVAL; if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1)) return -EACCES; write_codec(dev, bi.data.mix.reg, bi.data.mix.data); return 0; } if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) return -EFAULT; return 0;}/* --------------------------------------------------------------------- */const struct hardware_info sm_hw_wss = { "wss", sizeof(struct sc_state_wss), wss_open, wss_close, wss_ioctl, wss_sethw};/* --------------------------------------------------------------------- */static void setup_fdx_dma_wss(struct net_device *dev, struct sm_state *sm){ unsigned long flags; unsigned char oldcodecmode, codecdma; long abrt; unsigned int osamps, isamps; save_flags(flags); cli(); /* * perform the final DMA sequence to disable the codec request */ oldcodecmode = read_codec(dev, 9); write_codec(dev, 9, 0); /* disable codec DMA */ wss_ack_int(dev); if ((codecdma = read_codec(dev, 11)) & 0x10) { dma_setup(sm, 1, dev->dma); dma_setup(sm, 0, sm->hdrv.ptt_out.dma2); abrt = 0; while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000)); } wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1); osamps = dma_setup(sm, 1, dev->dma) - 1; isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; write_codec(dev, 15, osamps & 0xff); write_codec(dev, 14, osamps >> 8); if (SCSTATE->crystal) { write_codec(dev, 31, isamps & 0xff); write_codec(dev, 30, isamps >> 8); } write_codec(dev, 9, 3); restore_flags(flags);}/* --------------------------------------------------------------------- */static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *)dev_id; struct sm_state *sm = (struct sm_state *)dev->priv; unsigned long flags; unsigned char cry_int_src; unsigned icfrag, ocfrag, isamps, osamps; if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || sm->hdrv.magic != HDLCDRV_MAGIC) return; save_flags(flags); cli(); if (SCSTATE->crystal) { /* Crystal has an essentially different interrupt handler! */ cry_int_src = read_codec(dev, 0x18); wss_ack_int(dev); if (cry_int_src & 0x10) { /* playback interrupt */ disable_dma(dev->dma); clear_dma_ff(dev->dma); osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; write_codec(dev, 15, osamps & 0xff); write_codec(dev, 14, osamps >> 8); enable_dma(dev->dma); } if (cry_int_src & 0x20) { /* capture interrupt */ disable_dma(sm->hdrv.ptt_out.dma2); clear_dma_ff(sm->hdrv.ptt_out.dma2); isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; write_codec(dev, 31, isamps & 0xff); write_codec(dev, 30, isamps >> 8); enable_dma(sm->hdrv.ptt_out.dma2); } restore_flags(flags); sm_int_freq(sm); sti(); if (cry_int_src & 0x10) { if (dma_end_transmit(sm, ocfrag)) dma_clear_transmit(sm); dma_transmit(sm); } if (cry_int_src & 0x20) { dma_receive(sm, icfrag); hdlcdrv_arbitrate(dev, &sm->hdrv); } sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv); return; } wss_ack_int(dev); disable_dma(dev->dma); disable_dma(sm->hdrv.ptt_out.dma2); clear_dma_ff(dev->dma); clear_dma_ff(sm->hdrv.ptt_out.dma2); osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; write_codec(dev, 15, osamps & 0xff); write_codec(dev, 14, osamps >> 8); if (SCSTATE->crystal) { write_codec(dev, 31, isamps & 0xff); write_codec(dev, 30, isamps >> 8); } enable_dma(dev->dma); enable_dma(sm->hdrv.ptt_out.dma2); restore_flags(flags); sm_int_freq(sm); sti(); if (dma_end_transmit(sm, ocfrag)) dma_clear_transmit(sm); dma_transmit(sm); dma_receive(sm, icfrag); hdlcdrv_arbitrate(dev, &sm->hdrv); sm_output_status(sm); hdlcdrv_transmitter(dev, &sm->hdrv); hdlcdrv_receiver(dev, &sm->hdrv);}/* --------------------------------------------------------------------- */static int wssfdx_open(struct net_device *dev, struct sm_state *sm) { if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) return -ENXIO; if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || dev->irq < 2 || dev->irq > 15 || dev->dma > 3) return -ENXIO; if (check_region(dev->base_addr, WSS_EXTENT)) return -EACCES; /* * check if a card is available */ if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45)) return -ENODEV; /* * initialize some variables */ if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) return -ENOMEM; if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { kfree(sm->dma.ibuf); return -ENOMEM; } dma_init_transmit(sm); dma_init_receive(sm); memset(&sm->m, 0, sizeof(sm->m)); memset(&sm->d, 0, sizeof(sm->d)); if (sm->mode_tx->init) sm->mode_tx->init(sm); if (sm->mode_rx->init) sm->mode_rx->init(sm); if (request_dma(dev->dma, sm->hwdrv->hw_name)) { kfree(sm->dma.ibuf); kfree(sm->dma.obuf); return -EBUSY; } if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { kfree(sm->dma.ibuf); kfree(sm->dma.obuf); free_dma(dev->dma); return -EBUSY; } if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, sm->hwdrv->hw_name, dev)) { kfree(sm->dma.ibuf); kfree(sm->dma.obuf); free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); return -EBUSY; } request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); setup_fdx_dma_wss(dev, sm); return 0;}/* --------------------------------------------------------------------- */static int wssfdx_close(struct net_device *dev, struct sm_state *sm) { if (!dev || !sm) return -EINVAL; /* * disable interrupts */ disable_dma(dev->dma); disable_dma(sm->hdrv.ptt_out.dma2); write_codec(dev, 9, 0xc); /* disable codec */ free_irq(dev->irq, dev); free_dma(dev->dma); free_dma(sm->hdrv.ptt_out.dma2); release_region(dev->base_addr, WSS_EXTENT); kfree(sm->dma.ibuf); kfree(sm->dma.obuf); return 0;}/* --------------------------------------------------------------------- */static int wssfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode){ char *cp = strchr(mode, '.'); const struct modem_tx_info **mtp = sm_modem_tx_table; const struct modem_rx_info **mrp; int i; if (!strcmp(mode, "off")) { sm->mode_tx = NULL; sm->mode_rx = NULL; return 0; } if (cp) *cp++ = '\0'; else cp = mode; for (; *mtp; mtp++) { if ((*mtp)->loc_storage > sizeof(sm->m)) { printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", sm_drvname, (*mtp)->name, (*mtp)->loc_storage); continue; } if (!(*mtp)->name || strcmp((*mtp)->name, mode)) continue; if ((i = wss_srate_index((*mtp)->srate)) < 0) continue; for (mrp = sm_modem_rx_table; *mrp; mrp++) { if ((*mrp)->loc_storage > sizeof(sm->d)) { printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", sm_drvname, (*mrp)->name, (*mrp)->loc_storage); continue; } if ((*mrp)->name && !strcmp((*mrp)->name, cp) && (*mtp)->srate == (*mrp)->srate) { sm->mode_tx = *mtp; sm->mode_rx = *mrp; SCSTATE->fmt[0] = SCSTATE->fmt[1] = i; sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100; if (sm->dma.ifragsz < sm->mode_rx->overlap) sm->dma.ifragsz = sm->mode_rx->overlap; sm->dma.i16bit = sm->dma.o16bit = 2; if (sm->mode_rx->demodulator_s16) { sm->dma.i16bit = 1; sm->dma.ifragsz <<= 1;#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ SCSTATE->fmt[0] |= 0xc0;#else /* __BIG_ENDIAN */ SCSTATE->fmt[0] |= 0x40;#endif /* __BIG_ENDIAN */ } else if (sm->mode_rx->demodulator_u8) sm->dma.i16bit = 0; if (sm->mode_tx->modulator_s16) { sm->dma.o16bit = 1; sm->dma.ofragsz <<= 1;#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ SCSTATE->fmt[1] |= 0xc0;#else /* __BIG_ENDIAN */ SCSTATE->fmt[1] |= 0x40;#endif /* __BIG_ENDIAN */ } else if (sm->mode_tx->modulator_u8) sm->dma.o16bit = 0; if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, sm->mode_rx->name, sm->mode_tx->name); sm->mode_tx = NULL; sm->mode_rx = NULL; return -EINVAL; } return 0; } } } return -EINVAL;}/* --------------------------------------------------------------------- */static int wssfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, struct hdlcdrv_ioctl *hi, int cmd){ if (cmd != SIOCDEVPRIVATE) return -ENOIOCTLCMD; if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; return wss_ioctl(dev, sm, ifr, hi, cmd);}/* --------------------------------------------------------------------- */const struct hardware_info sm_hw_wssfdx = { "wssfdx", sizeof(struct sc_state_wss), wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw};/* --------------------------------------------------------------------- */#undef SCSTATE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -