static void update_dahdi_ring(xpd_t *xpd, int pos, bool on) { dahdi_rxsig_t rxsig; BUG_ON(!xpd); if(on) { if(caller_id_style == CID_STYLE_BELL) { LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n"); BIT_CLR(xpd->cid_on, pos); } rxsig = DAHDI_RXSIG_RING; } else { if(caller_id_style == CID_STYLE_BELL) { LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: on\n"); BIT_SET(xpd->cid_on, pos); } rxsig = DAHDI_RXSIG_OFFHOOK; } pcm_recompute(xpd, 0); /* * We should not spinlock before calling dahdi_hooksig() as * it may call back into our xpp_hooksig() and cause * a nested spinlock scenario */ if(SPAN_REGISTERED(xpd)) dahdi_hooksig(&xpd->chans[pos], rxsig); }
static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_dahdi) { struct FXO_priv_data *priv; priv = xpd->priv; BUG_ON(!priv); atomic_set(&priv->ring_debounce[pos], 0); /* Stop debouncing */ /* * We don't want to check battery during ringing * due to voltage fluctuations. */ reset_battery_readings(xpd, pos); if (on && !PHONEDEV(xpd).ringing[pos]) { LINE_DBG(SIGNAL, xpd, pos, "START\n"); PHONEDEV(xpd).ringing[pos] = 1; priv->cidtimer[pos] = xpd->timer_count; MARK_BLINK(priv, pos, LED_GREEN, LED_BLINK_RING); if (update_dahdi) update_dahdi_ring(xpd, pos, on); } else if (!on && PHONEDEV(xpd).ringing[pos]) { LINE_DBG(SIGNAL, xpd, pos, "STOP\n"); PHONEDEV(xpd).ringing[pos] = 0; priv->cidtimer[pos] = xpd->timer_count; if (IS_BLINKING(priv, pos, LED_GREEN)) MARK_BLINK(priv, pos, LED_GREEN, 0); if (update_dahdi) update_dahdi_ring(xpd, pos, on); } }
static int do_sethook(xpd_t *xpd, int pos, bool to_offhook) { unsigned long flags; xbus_t *xbus; struct FXO_priv_data *priv; int ret = 0; byte value; BUG_ON(!xpd); BUG_ON(xpd->direction == TO_PHONE); // We can SETHOOK state only on PSTN xbus = xpd->xbus; priv = xpd->priv; BUG_ON(!priv); if(priv->battery[pos] != BATTERY_ON && to_offhook) { LINE_NOTICE(xpd, pos, "Cannot take offhook while battery is off!\n"); return -EINVAL; } spin_lock_irqsave(&xpd->lock, flags); mark_ring(xpd, pos, 0, 0); // No more rings value = REG_DAA_CONTROL1_ONHM; /* Bit 3 is for CID */ if(to_offhook) value |= REG_DAA_CONTROL1_OH; LINE_DBG(SIGNAL, xpd, pos, "SETHOOK: value=0x%02X %s\n", value, (to_offhook)?"OFFHOOK":"ONHOOK"); if(to_offhook) MARK_ON(priv, pos, LED_GREEN); else MARK_OFF(priv, pos, LED_GREEN); ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, REG_DAA_CONTROL1, value); if(to_offhook) { BIT_SET(xpd->offhook, pos); } else { BIT_CLR(xpd->offhook, pos); } if(caller_id_style != CID_STYLE_PASS_ALWAYS) { LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n"); BIT_CLR(xpd->cid_on, pos); } #ifdef WITH_METERING priv->metering_count[pos] = 0; priv->metering_tone_state = 0L; DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_METERING, 0x2D); #endif reset_battery_readings(xpd, pos); /* unstable during hook changes */ priv->power_denial_safezone[pos] = (to_offhook) ? POWER_DENIAL_SAFEZONE : 0; if(!to_offhook) priv->power[pos] = POWER_UNKNOWN; spin_unlock_irqrestore(&xpd->lock, flags); return ret; }
/* * LED control is done via DAA register 0x20 */ static int do_led(xpd_t *xpd, lineno_t chan, __u8 which, bool on) { int ret = 0; struct FXO_priv_data *priv; xbus_t *xbus; __u8 value; BUG_ON(!xpd); xbus = xpd->xbus; priv = xpd->priv; which = which % NUM_LEDS; if (IS_SET(PHONEDEV(xpd).digital_outputs, chan) || IS_SET(PHONEDEV(xpd).digital_inputs, chan)) goto out; if (chan == PORT_BROADCAST) { priv->ledstate[which] = (on) ? ~0 : 0; } else { if (on) BIT_SET(priv->ledstate[which], chan); else BIT_CLR(priv->ledstate[which], chan); } value = 0; value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]); value |= (on) ? BIT(0) : 0; value |= (on) ? BIT(1) : 0; LINE_DBG(LEDS, xpd, chan, "LED: which=%d -- %s\n", which, (on) ? "on" : "off"); ret = DAA_DIRECT_REQUEST(xbus, xpd, chan, DAA_WRITE, 0x20, value); out: return ret; }
static int do_sethook(xpd_t *xpd, int pos, bool to_offhook) { unsigned long flags; xbus_t *xbus; struct FXO_priv_data *priv; int ret = 0; __u8 value; BUG_ON(!xpd); /* We can SETHOOK state only on PSTN */ BUG_ON(PHONEDEV(xpd).direction == TO_PHONE); xbus = xpd->xbus; priv = xpd->priv; BUG_ON(!priv); if (priv->battery[pos] != BATTERY_ON && to_offhook) { LINE_NOTICE(xpd, pos, "Cannot take offhook while battery is off!\n"); return -EINVAL; } spin_lock_irqsave(&xpd->lock, flags); mark_ring(xpd, pos, 0, 0); // No more rings value = REG_DAA_CONTROL1_ONHM; /* Bit 3 is for CID */ if (to_offhook) value |= REG_DAA_CONTROL1_OH; LINE_DBG(SIGNAL, xpd, pos, "SETHOOK: value=0x%02X %s\n", value, (to_offhook) ? "OFFHOOK" : "ONHOOK"); if (to_offhook) MARK_ON(priv, pos, LED_GREEN); else MARK_OFF(priv, pos, LED_GREEN); ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, REG_DAA_CONTROL1, value); mark_offhook(xpd, pos, to_offhook); switch (caller_id_style) { case CID_STYLE_ETSI_DTMF: case CID_STYLE_PASSTHROUGH: break; default: oht_pcm(xpd, pos, 0); break; } #ifdef WITH_METERING priv->metering_count[pos] = 0; priv->metering_tone_state = 0L; DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, DAA_REG_METERING, 0x2D); #endif /* unstable during hook changes */ reset_battery_readings(xpd, pos); if (to_offhook) { priv->power_denial_safezone[pos] = power_denial_safezone; } else { priv->power_denial_length[pos] = 0; priv->power_denial_safezone[pos] = 0; } priv->cidtimer[pos] = xpd->timer_count; spin_unlock_irqrestore(&xpd->lock, flags); return ret; }
static void power_change(xpd_t *xpd, int portno, enum power_state pw) { struct FXO_priv_data *priv; priv = xpd->priv; LINE_DBG(SIGNAL, xpd, portno, "power: %s -> %s\n", power2str(priv->power[portno]), power2str(pw)); priv->power[portno] = pw; }
static void handle_fxo_leds(xpd_t *xpd) { int i; unsigned long flags; const enum fxo_leds colors[] = { LED_GREEN, LED_RED }; enum fxo_leds color; unsigned int timer_count; struct FXO_priv_data *priv; BUG_ON(!xpd); spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; timer_count = xpd->timer_count; for (color = 0; color < ARRAY_SIZE(colors); color++) { for_each_line(xpd, i) { if (IS_SET(PHONEDEV(xpd).digital_outputs, i) || IS_SET(PHONEDEV(xpd).digital_inputs, i)) continue; /* Blinking? */ if ((xpd->blink_mode & BIT(i)) || IS_BLINKING(priv, i, color)) { int mod_value = LED_COUNTER(priv, i, color); if (!mod_value) /* safety value */ mod_value = DEFAULT_LED_PERIOD; // led state is toggled if ((timer_count % mod_value) == 0) { LINE_DBG(LEDS, xpd, i, "ledstate=%s\n", (IS_SET (priv->ledstate[color], i)) ? "ON" : "OFF"); if (!IS_SET(priv->ledstate[color], i)) do_led(xpd, i, color, 1); else do_led(xpd, i, color, 0); } } else if (IS_SET(priv->ledcontrol[color], i) && !IS_SET(priv->ledstate[color], i)) { do_led(xpd, i, color, 1); } else if (!IS_SET(priv->ledcontrol[color], i) && IS_SET(priv->ledstate[color], i)) { do_led(xpd, i, color, 0); } } } spin_unlock_irqrestore(&xpd->lock, flags); }