📄 amd7930.c
字号:
static int amd7930_get_input_rate(struct sparcaudio_driver *drv){ return AMD7930_RATE;}static intamd7930_set_input_rate(struct sparcaudio_driver *drv, int value){ return (value == AMD7930_RATE) ? 0 : -EINVAL;}static int amd7930_get_output_muted(struct sparcaudio_driver *drv){ return 0;}static void amd7930_loopback(struct sparcaudio_driver *drv, unsigned int value){ struct amd7930_info *info = (struct amd7930_info *) drv->private; if (value) info->map.mmr1 |= AM_MAP_MMR1_LOOPBACK; else info->map.mmr1 &= ~AM_MAP_MMR1_LOOPBACK; amd7930_update_map(drv);}static int amd7930_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg, struct sparcaudio_driver *drv){ int retval = 0; switch (cmd) { case AUDIO_DIAG_LOOPBACK: amd7930_loopback(drv, (unsigned int)arg); break; default: retval = -EINVAL; }; return retval;}/* * ISDN operations * * Many of these routines take an "int dev" argument, which is simply * an index into the drivers[] array. Currently, we only support a * single AMD 7930 chip, so the value should always be 0. B channel * operations require an "int chan", which should be 0 for channel B1 * and 1 for channel B2 * * int amd7930_get_irqnum(int dev) * * returns the interrupt number being used by the chip. ISDN4linux * uses this number to watch the interrupt during initialization and * make sure something is happening. * * int amd7930_get_liu_state(int dev) * * returns the current state of the ISDN Line Interface Unit (LIU) * as a number between 2 (state F2) and 7 (state F7). 0 may also be * returned if the chip doesn't exist or the LIU hasn't been * activated. The meanings of the states are defined in I.430, ISDN * BRI Physical Layer Interface. The most important two states are * F3 (shutdown) and F7 (syncronized). * * void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) * * initializes the LIU and optionally registers a callback to be * signaled upon a change of LIU state. The callback will be called * with a single opaque callback_arg Once the callback has been * triggered, amd7930_get_liu_state can be used to determine the LIU * current state. * * void amd7930_liu_activate(int dev, int priority) * * requests LIU activation at a given D-channel priority. * Successful activatation is achieved upon entering state F7, which * will trigger any callback previously registered with * amd7930_liu_init. * * void amd7930_liu_deactivate(int dev) * * deactivates LIU. Outstanding D and B channel transactions are * terminated rudely and without callback notification. LIU change * of state callback will be triggered, however. * * void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, * void (*callback)(void *, int), void *callback_arg) * * transmits a packet - specified with buffer, count - over the D-channel * interface. Buffer should begin with the LAPD address field and * end with the information field. FCS and flag sequences should not * be included, nor is bit-stuffing required - all these functions are * performed by the chip. The callback function will be called * DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed * both the arbitrary callback_arg and an integer error indication: * * 0 - successful transmission; ready for next packet * non-0 - error value from chip's DER (D-Channel Error Register): * 4 - collision detect * 128 - underrun; irq routine didn't service chip fast enough * * The callback routine should defer any time-consuming operations * to a bottom-half handler; however, amd7930_dxmit may be called * from within the callback to request back-to-back transmission of * a second packet (without repeating the priority/collision mechanism) * * A comment about the "collision detect" error, which is signalled * whenever the echoed D-channel data didn't match the transmitted * data. This is part of ISDN's normal multi-drop T-interface * operation, indicating that another device has attempted simultaneous * transmission, but can also result from line noise. An immediate * requeue via amd7930_dxmit is suggested, but repeated collision * errors may indicate a more serious problem. * * void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, * void (*callback)(void *, int, unsigned int), * void *callback_arg) * * register a buffer - buffer, size - into which a D-channel packet * can be received. The callback function will be called DURING * THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an * arbitrary callback_arg, an integer error indication and the length * of the received packet, which will start with the address field, * end with the information field, and not contain flag or FCS * bytes. Bit-stuffing will already have been corrected for. * Possible values of second callback argument "error": * * 0 - successful reception * non-0 - error value from chip's DER (D-Channel Error Register): * 1 - received packet abort * 2 - framing error; non-integer number of bytes received * 8 - FCS error; CRC sequence indicated corrupted data * 16 - overflow error; packet exceeded size of buffer * 32 - underflow error; packet smaller than required five bytes * 64 - overrun error; irq routine didn't service chip fast enough * * int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) * * This function should be called before any other operations on a B * channel. In addition to arranging for interrupt handling and * channel multiplexing, it sets the xmit_idle_char which is * transmitted on the interface when no data buffer is available. * Suggested values are: 0 for ISDN audio; FF for HDLC mark idle; 7E * for HDLC flag idle. Returns 0 on a successful open; -1 on error, * which is quite possible if audio and the other ISDN channel are * already in use, since the Am7930 can only send two of the three * channels to the processor * * void amd7930_bclose(int dev, int chan) * * Shuts down a B channel when no longer in use. * * void amd7930_bxmit(int dev, int chan, __u8 *buffer, unsigned int count, * void (*callback)(void *), void *callback_arg) * * transmits a raw data block - specified with buffer, count - over * the B channel interface specified by dev/chan. The callback * function will be called DURING THE TOP HALF OF AN INTERRUPT * HANDLER and will be passed the arbitrary callback_arg * * The callback routine should defer any time-consuming operations * to a bottom-half handler; however, amd7930_bxmit may be called * from within the callback to request back-to-back transmission of * another data block * * void amd7930_brecv(int dev, int chan, __u8 *buffer, unsigned int size, * void (*callback)(void *), void *callback_arg) * * receive a raw data block - specified with buffer, size - over the * B channel interface specified by dev/chan. The callback function * will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and * will be passed the arbitrary callback_arg * * The callback routine should defer any time-consuming operations * to a bottom-half handler; however, amd7930_brecv may be called * from within the callback to register another buffer and ensure * continuous B channel reception without loss of data * */#if defined (AMD79C30_ISDN)static int amd7930_get_irqnum(int dev){ struct amd7930_info *info; if (dev > num_drivers) return(0); info = (struct amd7930_info *) drivers[dev].private; return info->irq;}static int amd7930_get_liu_state(int dev){ struct amd7930_info *info; if (dev > num_drivers) return(0); info = (struct amd7930_info *) drivers[dev].private; return info->liu_state;}static void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg){ struct amd7930_info *info; unsigned long flags; if (dev > num_drivers) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); /* Set callback for LIU state change */ info->liu_callback = callback; info->liu_callback_arg = callback_arg; /* De-activate the ISDN Line Interface Unit (LIU) */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); sbus_writeb(0, info->regs + DR); /* Request interrupt when LIU changes state from/to F3/F7/F8 */ sbus_writeb(AMR_LIU_LMR2, info->regs + CR); sbus_writeb(AM_LIU_LMR2_EN_F3_INT | AM_LIU_LMR2_EN_F7_INT | AM_LIU_LMR2_EN_F8_INT, info->regs + DR); /* amd7930_enable_ints(info); */ /* Activate the ISDN Line Interface Unit (LIU) */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); sbus_writeb(AM_LIU_LMR1_LIU_ENABL, info->regs + DR); restore_flags(flags);}static void amd7930_liu_activate(int dev, int priority){ struct amd7930_info *info; unsigned long flags; if (dev > num_drivers) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); /* Set D-channel access priority * * I.430 defines a priority mechanism based on counting 1s * in the echo channel before transmitting * * Priority 0 is eight 1s; priority 1 is ten 1s; etc */ sbus_writeb(AMR_LIU_LPR, info->regs + CR); sbus_writeb(priority & 0x0f, info->regs + DR); /* request LIU activation */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); sbus_writeb(AM_LIU_LMR1_LIU_ENABL | AM_LIU_LMR1_REQ_ACTIV, info->regs + DR); restore_flags(flags);}static void amd7930_liu_deactivate(int dev){ struct amd7930_info *info; unsigned long flags; if (dev > num_drivers) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); /* deactivate LIU */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); sbus_writeb(0, info->regs + DR); restore_flags(flags);}static void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, void (*callback)(void *, int), void *callback_arg){ struct amd7930_info *info; unsigned long flags; __u8 dmr1; if (dev > num_drivers) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); if (info->D.output_ptr) { restore_flags(flags); printk("amd7930_dxmit: transmitter in use\n"); return; } info->D.output_ptr = buffer; info->D.output_count = count; info->D.output_callback = callback; info->D.output_callback_arg = callback_arg; /* Enable D-channel Transmit Threshold interrupt; disable addressing */ sbus_writeb(AMR_DLC_DMR1, info->regs + CR); dmr1 = sbus_readb(info->regs + DR); dmr1 |= AMR_DLC_DMR1_DTTHRSH_INT; dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS; sbus_writeb(dmr1, info->regs + DR); /* Begin xmit by setting D-channel Transmit Byte Count Reg (DTCR) */ sbus_writeb(AMR_DLC_DTCR, info->regs + CR); sbus_writeb(count & 0xff, info->regs + DR); sbus_writeb((count >> 8) & 0xff, info->regs + DR); /* Prime xmit FIFO */ /* fill_D_xmit_fifo(info); */ transceive_Dchannel(info); restore_flags(flags);}static void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, void (*callback)(void *, int, unsigned int), void *callback_arg){ struct amd7930_info *info; unsigned long flags; __u8 dmr1; if (dev > num_drivers) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); if (info->D.input_ptr) { restore_flags(flags); printk("amd7930_drecv: receiver already has buffer!\n"); return; } info->D.input_ptr = buffer; info->D.input_count = 0; info->D.input_limit = size; info->D.input_callback = callback; info->D.input_callback_arg = callback_arg; /* Enable D-channel Receive Threshold interrupt; * Enable D-channel End of Receive Packet interrupt; * Disable address recognition */ sbus_writeb(AMR_DLC_DMR1, info->regs + CR); dmr1 = sbus_readb(info->regs + DR); dmr1 |= AMR_DLC_DMR1_DRTHRSH_INT | AMR_DLC_DMR1_EORP_INT; dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS; sbus_writeb(dmr1, info->regs + DR); /* Set D-channel Receive Byte Count Limit Register */ sbus_writeb(AMR_DLC_DRCR, info->regs + CR); sbus_writeb(size & 0xff, info->regs + DR); sbus_writeb((size >> 8) & 0xff, info->regs + DR); restore_flags(flags);}static int amd7930_bopen(int dev, unsigned int chan, int mode, u_char xmit_idle_char){ struct amd7930_info *info; unsigned long flags; u8 tmp; if (dev > num_drivers || chan<0 || chan>1) return -1; if (mode == L1_MODE_HDLC) return -1; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); if (info->Bb.channel_status == CHANNEL_AVAILABLE) { info->Bb.channel_status = CHANNEL_INUSE; info->Bb.xmit_idle_char = xmit_idle_char; info->Bisdn[chan] = &info->Bb; /* Multiplexor map - isdn (B1/2) to Bb */ sbus_writeb(AMR_MUX_MCR2 + chan, info->regs + CR); sbus_writeb((AM_MUX_CHANNEL_B1 + chan) | (AM_MUX_CHANNEL_Bb << 4), info->regs + DR); } else if (info->Bc.channel_status == CHANNEL_AVAILABLE) { info->Bc.channel_status = CHANNEL_INUSE; info->Bc.xmit_idle_char = xmit_idle_char; info->Bisdn[chan] = &info->Bc; /* Multiplexor map - isdn (B1/2) to Bc */ sbus_writeb(AMR_MUX_MCR2 + chan, info->regs + CR); sbus_writeb((AM_MUX_CHANNEL_B1 + chan) | (AM_MUX_CHANNEL_Bc << 4), info->regs + DR); } else { restore_flags(flags); return (-1); } /* Enable B channel transmit */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); tmp = sbus_readb(info->regs + DR); tmp |= AM_LIU_LMR1_B1_ENABL + chan; sbus_writeb(tmp, info->regs + DR); /* Enable B channel interrupts */ sbus_writeb(AMR_MUX_MCR4, info->regs + CR); sbus_writeb(AM_MUX_MCR4_ENABLE_INTS | AM_MUX_MCR4_REVERSE_Bb | AM_MUX_MCR4_REVERSE_Bc, info->regs + DR); restore_flags(flags); return 0;}static void amd7930_bclose(int dev, unsigned int chan){ struct amd7930_info *info; unsigned long flags; if (dev > num_drivers || chan<0 || chan>1) return; info = (struct amd7930_info *) drivers[dev].private; save_and_cli(flags); if (info->Bisdn[chan]) { u8 tmp; info->Bisdn[chan]->channel_status = CHANNEL_AVAILABLE; sbus_writeb(AMR_MUX_MCR2 + chan, info->regs + CR); sbus_writeb(0, info->regs + DR); info->Bisdn[chan] = NULL; /* Disable B channel transmit */ sbus_writeb(AMR_LIU_LMR1, info->regs + CR); tmp = sbus_readb(info->regs + DR); tmp &= ~(AM_LIU_LMR1_B1_ENABL + chan); sbus_writeb(tmp, info->regs + DR); if (info->Bb.channel_status == CHANNEL_AVAILABLE && info->Bc.channel_status == CHANNEL_AVAILABLE) { /* Disable B channel interrupts */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -