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); } }
/* * 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 xpd_t *FXO_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, __u8 subtype, int subunits, int subunit_ports, bool to_phone) { xpd_t *xpd = NULL; int channels; if (to_phone) { XBUS_NOTICE(xbus, "XPD=%d%d: try to instanciate FXO with " "reverse direction\n", unit, subunit); return NULL; } if (subtype == 2) channels = min(2, subunit_ports); else channels = min(8, subunit_ports); xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct FXO_priv_data), proto_table, channels); if (!xpd) return NULL; PHONEDEV(xpd).direction = TO_PSTN; xpd->type_name = "FXO"; if (fxo_proc_create(xbus, xpd) < 0) goto err; return xpd; err: xpd_free(xpd); return NULL; }
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); }
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 DEVICE_ATTR_READER(span_show, dev, buf) { xpd_t *xpd; unsigned long flags; int len = 0; BUG_ON(!dev); xpd = dev_to_xpd(dev); if (!xpd) return -ENODEV; spin_lock_irqsave(&xpd->lock, flags); len += sprintf(buf, "%d\n", SPAN_REGISTERED(xpd) ? PHONEDEV(xpd).span.spanno : 0); spin_unlock_irqrestore(&xpd->lock, flags); return len; }