2010-08-28 05:59:27 +08:00
|
|
|
/*
|
|
|
|
* Written by Oron Peled <oron@actcom.co.il>
|
|
|
|
* Copyright (C) 2004-2006, Xorcom
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#ifndef XBUS_CORE_H
|
|
|
|
#define XBUS_CORE_H
|
|
|
|
|
|
|
|
#include <linux/wait.h>
|
|
|
|
#include <linux/interrupt.h> /* for tasklets */
|
|
|
|
#include <linux/kref.h>
|
|
|
|
#include "xpd.h"
|
|
|
|
#include "xframe_queue.h"
|
|
|
|
#include "xbus-pcm.h"
|
|
|
|
|
2011-12-22 02:10:44 +08:00
|
|
|
#define MAX_BUSES 128
|
2011-11-02 22:08:12 +08:00
|
|
|
#define XFRAME_DATASIZE 512
|
|
|
|
#define MAX_ENV_STR 40
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* forward declarations */
|
|
|
|
struct xbus_workqueue;
|
|
|
|
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
|
|
|
|
struct xbus_ops {
|
2012-01-11 23:22:46 +08:00
|
|
|
int (*xframe_send_pcm) (xbus_t *xbus, xframe_t *xframe);
|
|
|
|
int (*xframe_send_cmd) (xbus_t *xbus, xframe_t *xframe);
|
|
|
|
xframe_t *(*alloc_xframe) (xbus_t *xbus, gfp_t gfp_flags);
|
|
|
|
void (*free_xframe) (xbus_t *xbus, xframe_t *xframe);
|
2010-08-28 05:59:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XBUS statistics counters
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
XBUS_N_UNITS,
|
|
|
|
XBUS_N_TX_XFRAME_PCM,
|
|
|
|
XBUS_N_RX_XFRAME_PCM,
|
|
|
|
XBUS_N_TX_PACK_PCM,
|
|
|
|
XBUS_N_RX_PACK_PCM,
|
|
|
|
XBUS_N_TX_BYTES,
|
|
|
|
XBUS_N_RX_BYTES,
|
|
|
|
XBUS_N_TX_PCM_FRAG,
|
|
|
|
XBUS_N_RX_CMD,
|
|
|
|
XBUS_N_TX_CMD,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define XBUS_COUNTER(xbus, counter) ((xbus)->counters[XBUS_N_ ## counter])
|
|
|
|
|
|
|
|
#define C_(x) [ XBUS_N_ ## x ] = { #x }
|
|
|
|
|
|
|
|
/* yucky, make an instance so we can size it... */
|
|
|
|
static struct xbus_counters {
|
2012-01-11 23:22:46 +08:00
|
|
|
char *name;
|
2010-08-28 05:59:27 +08:00
|
|
|
} xbus_counters[] = {
|
2012-01-11 23:22:46 +08:00
|
|
|
C_(UNITS), C_(TX_XFRAME_PCM), C_(RX_XFRAME_PCM), C_(TX_PACK_PCM),
|
|
|
|
C_(RX_PACK_PCM), C_(TX_BYTES), C_(RX_BYTES),
|
|
|
|
C_(TX_PCM_FRAG), C_(RX_CMD), C_(TX_CMD),};
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
#undef C_
|
|
|
|
|
|
|
|
#define XBUS_COUNTER_MAX ARRAY_SIZE(xbus_counters)
|
|
|
|
|
|
|
|
enum xbus_state {
|
|
|
|
XBUS_STATE_START,
|
|
|
|
XBUS_STATE_IDLE,
|
|
|
|
XBUS_STATE_SENT_REQUEST,
|
|
|
|
XBUS_STATE_RECVD_DESC,
|
|
|
|
XBUS_STATE_READY,
|
|
|
|
XBUS_STATE_DEACTIVATING,
|
|
|
|
XBUS_STATE_DEACTIVATED,
|
|
|
|
XBUS_STATE_FAIL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *xbus_statename(enum xbus_state st);
|
|
|
|
|
|
|
|
struct xbus_transport {
|
2012-01-11 23:22:46 +08:00
|
|
|
struct xbus_ops *ops;
|
|
|
|
void *priv;
|
|
|
|
struct device *transport_device;
|
|
|
|
ushort max_send_size;
|
|
|
|
enum xbus_state xbus_state;
|
|
|
|
unsigned long transport_flags;
|
|
|
|
spinlock_t state_lock;
|
|
|
|
atomic_t transport_refcount;
|
|
|
|
wait_queue_head_t transport_unused;
|
|
|
|
spinlock_t lock;
|
|
|
|
char model_string[MAX_ENV_STR];
|
2010-08-28 05:59:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define MAX_SEND_SIZE(xbus) ((xbus)->transport.max_send_size)
|
|
|
|
#define XBUS_STATE(xbus) ((xbus)->transport.xbus_state)
|
|
|
|
#define XBUS_IS(xbus, st) (XBUS_STATE(xbus) == XBUS_STATE_ ## st)
|
|
|
|
#define TRANSPORT_EXIST(xbus) ((xbus)->transport.ops != NULL)
|
|
|
|
|
|
|
|
#define XBUS_FLAG_CONNECTED 0
|
2012-01-12 03:56:35 +08:00
|
|
|
#define XBUS_FLAGS(xbus, flg) \
|
|
|
|
test_bit(XBUS_FLAG_ ## flg, &((xbus)->transport.transport_flags))
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
struct xbus_ops *transportops_get(xbus_t *xbus);
|
|
|
|
void transportops_put(xbus_t *xbus);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encapsulate all poll related data of a single xbus.
|
|
|
|
*/
|
|
|
|
struct xbus_workqueue {
|
2012-01-11 23:22:46 +08:00
|
|
|
struct workqueue_struct *wq;
|
|
|
|
struct work_struct xpds_init_work;
|
|
|
|
bool xpds_init_done;
|
|
|
|
struct list_head card_list;
|
|
|
|
int num_units;
|
|
|
|
int num_units_initialized;
|
|
|
|
wait_queue_head_t wait_for_xpd_initialization;
|
|
|
|
spinlock_t worker_lock;
|
|
|
|
struct semaphore running_initialization;
|
2010-08-28 05:59:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate/Free an xframe from pools of empty xframes.
|
2012-01-11 23:21:13 +08:00
|
|
|
* Calls to {get, put}_xframe are wrapped in
|
2010-08-28 05:59:27 +08:00
|
|
|
* the macros bellow, so we take/return it
|
|
|
|
* to the correct pool.
|
|
|
|
*/
|
|
|
|
xframe_t *get_xframe(struct xframe_queue *q);
|
|
|
|
void put_xframe(struct xframe_queue *q, xframe_t *xframe);
|
|
|
|
|
2012-01-12 03:56:35 +08:00
|
|
|
#define ALLOC_SEND_XFRAME(xbus) \
|
|
|
|
get_xframe(&(xbus)->send_pool)
|
|
|
|
#define ALLOC_RECV_XFRAME(xbus) \
|
|
|
|
get_xframe(&(xbus)->receive_pool)
|
|
|
|
#define FREE_SEND_XFRAME(xbus, xframe) \
|
|
|
|
put_xframe(&(xbus)->send_pool, (xframe))
|
|
|
|
#define FREE_RECV_XFRAME(xbus, xframe) \
|
|
|
|
put_xframe(&(xbus)->receive_pool, (xframe))
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
xbus_t *xbus_num(uint num);
|
|
|
|
xbus_t *get_xbus(const char *msg, uint num);
|
|
|
|
void put_xbus(const char *msg, xbus_t *xbus);
|
|
|
|
int refcount_xbus(xbus_t *xbus);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2011-06-29 02:23:00 +08:00
|
|
|
/*
|
|
|
|
* Echo canceller related data
|
|
|
|
*/
|
|
|
|
#define ECHO_TIMESLOTS 128
|
|
|
|
|
|
|
|
struct echoops {
|
2012-01-11 23:22:46 +08:00
|
|
|
int (*ec_set) (xpd_t *xpd, int pos, bool on);
|
|
|
|
int (*ec_get) (xpd_t *xpd, int pos);
|
|
|
|
int (*ec_update) (xbus_t *xbus);
|
|
|
|
void (*ec_dump) (xbus_t *xbus);
|
2011-06-29 02:23:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct xbus_echo_state {
|
2012-01-11 23:22:46 +08:00
|
|
|
const struct echoops *echoops;
|
|
|
|
__u8 timeslots[ECHO_TIMESLOTS];
|
|
|
|
int xpd_idx;
|
|
|
|
struct device_attribute *da[MAX_XPDS];
|
2011-06-29 02:23:00 +08:00
|
|
|
};
|
|
|
|
#define ECHOOPS(xbus) ((xbus)->echo_state.echoops)
|
|
|
|
#define EC_METHOD(name, xbus) (ECHOOPS(xbus)->name)
|
|
|
|
#define CALL_EC_METHOD(name, xbus, ...) (EC_METHOD(name, (xbus))(__VA_ARGS__))
|
|
|
|
|
2010-08-28 05:59:27 +08:00
|
|
|
/*
|
|
|
|
* An xbus is a transport layer for Xorcom Protocol commands
|
|
|
|
*/
|
|
|
|
struct xbus {
|
2012-01-11 23:22:46 +08:00
|
|
|
char busname[XBUS_NAMELEN]; /* set by xbus_new() */
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* low-level bus drivers set these 2 fields */
|
2012-01-11 23:22:46 +08:00
|
|
|
char connector[XBUS_DESCLEN];
|
|
|
|
char label[LABEL_SIZE];
|
|
|
|
__u8 revision; /* Protocol revision */
|
|
|
|
struct xbus_transport transport;
|
|
|
|
struct dahdi_device *ddev;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
int num;
|
|
|
|
struct xpd *xpds[MAX_XPDS];
|
|
|
|
struct xbus_echo_state echo_state;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
int command_tick_counter;
|
|
|
|
int usec_nosend; /* Firmware flow control */
|
|
|
|
struct xframe_queue command_queue;
|
|
|
|
wait_queue_head_t command_queue_empty;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
struct xframe_queue send_pool; /* empty xframes for send */
|
|
|
|
struct xframe_queue receive_pool; /* empty xframes for receive */
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* tasklet processing */
|
2012-01-11 23:22:46 +08:00
|
|
|
struct xframe_queue receive_queue;
|
|
|
|
struct tasklet_struct receive_tasklet;
|
|
|
|
int cpu_rcv_intr[NR_CPUS];
|
|
|
|
int cpu_rcv_tasklet[NR_CPUS];
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2011-07-04 22:04:33 +08:00
|
|
|
struct quirks {
|
2011-07-21 00:49:31 +08:00
|
|
|
unsigned int has_fxo:1;
|
|
|
|
unsigned int has_digital_span:1;
|
2012-01-11 23:22:46 +08:00
|
|
|
} quirks;
|
|
|
|
bool self_ticking;
|
|
|
|
enum sync_mode sync_mode;
|
2010-08-28 05:59:27 +08:00
|
|
|
/* Managed by low-level drivers: */
|
2012-01-11 23:22:46 +08:00
|
|
|
enum sync_mode sync_mode_default;
|
|
|
|
struct timer_list command_timer;
|
|
|
|
unsigned int xbus_frag_count;
|
|
|
|
struct xframe_queue pcm_tospan;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
struct xpp_ticker ticker; /* for tick rate */
|
|
|
|
struct xpp_drift drift; /* for tick offset */
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
atomic_t pcm_rx_counter;
|
|
|
|
unsigned int global_counter;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* Device-Model */
|
2012-01-11 23:22:46 +08:00
|
|
|
struct device astribank;
|
2010-08-28 05:59:27 +08:00
|
|
|
#define dev_to_xbus(dev) container_of(dev, struct xbus, astribank)
|
2012-01-11 23:22:46 +08:00
|
|
|
struct kref kref;
|
2010-08-28 05:59:27 +08:00
|
|
|
#define kref_to_xbus(k) container_of(k, struct xbus, kref)
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
spinlock_t lock;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* PCM metrics */
|
2012-01-11 23:22:46 +08:00
|
|
|
struct timeval last_tx_sync;
|
|
|
|
struct timeval last_rx_sync;
|
|
|
|
unsigned long max_tx_sync;
|
|
|
|
unsigned long min_tx_sync;
|
|
|
|
unsigned long max_rx_sync;
|
|
|
|
unsigned long min_rx_sync;
|
|
|
|
unsigned long max_rx_process; /* packet processing time (usec) */
|
2010-08-28 05:59:27 +08:00
|
|
|
#ifdef SAMPLE_TICKS
|
|
|
|
#define SAMPLE_SIZE 1000
|
2012-01-11 23:22:46 +08:00
|
|
|
int sample_ticks[SAMPLE_SIZE];
|
|
|
|
bool sample_running;
|
|
|
|
int sample_pos;
|
2010-08-28 05:59:27 +08:00
|
|
|
#endif
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
struct xbus_workqueue worker;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sync adjustment
|
|
|
|
*/
|
2012-01-11 23:22:46 +08:00
|
|
|
int sync_adjustment;
|
|
|
|
int sync_adjustment_offset;
|
|
|
|
long pll_updated_at;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
atomic_t num_xpds;
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PROC_FS
|
2012-01-11 23:22:46 +08:00
|
|
|
struct proc_dir_entry *proc_xbus_dir;
|
|
|
|
struct proc_dir_entry *proc_xbus_summary;
|
2010-08-28 05:59:27 +08:00
|
|
|
#ifdef PROTOCOL_DEBUG
|
2012-01-11 23:22:46 +08:00
|
|
|
struct proc_dir_entry *proc_xbus_command;
|
2010-08-28 05:59:27 +08:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* statistics */
|
2012-01-11 23:22:46 +08:00
|
|
|
int counters[XBUS_COUNTER_MAX];
|
2010-08-28 05:59:27 +08:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define XFRAME_MAGIC 123456L
|
|
|
|
|
|
|
|
struct xframe {
|
2012-01-11 23:22:46 +08:00
|
|
|
unsigned long xframe_magic;
|
|
|
|
struct list_head frame_list;
|
|
|
|
atomic_t frame_len;
|
|
|
|
xbus_t *xbus;
|
|
|
|
struct timeval tv_created;
|
|
|
|
struct timeval tv_queued;
|
|
|
|
struct timeval tv_submitted;
|
|
|
|
struct timeval tv_received;
|
2010-08-28 05:59:27 +08:00
|
|
|
/* filled by transport layer */
|
2012-01-11 23:22:46 +08:00
|
|
|
size_t frame_maxlen;
|
|
|
|
__u8 *packets; /* max XFRAME_DATASIZE */
|
|
|
|
__u8 *first_free;
|
|
|
|
int usec_towait; /* prevent overflowing AB */
|
|
|
|
void *priv;
|
2010-08-28 05:59:27 +08:00
|
|
|
};
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
void xframe_init(xbus_t *xbus, xframe_t *xframe, void *buf, size_t maxsize,
|
|
|
|
void *priv);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
#define XFRAME_LEN(frame) atomic_read(&(frame)->frame_len)
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
int xbus_core_init(void); /* Initializer */
|
|
|
|
void xbus_core_shutdown(void); /* Terminator */
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* Frame handling */
|
2012-01-11 23:22:46 +08:00
|
|
|
void dump_xframe(const char msg[], const xbus_t *xbus, const xframe_t *xframe,
|
|
|
|
int debug);
|
2010-08-28 05:59:27 +08:00
|
|
|
int send_cmd_frame(xbus_t *xbus, xframe_t *xframe);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return pointer to next packet slot in the frame
|
|
|
|
* or NULL if the frame is full.
|
|
|
|
*/
|
|
|
|
xpacket_t *xframe_next_packet(xframe_t *xframe, int len);
|
|
|
|
|
|
|
|
/* XBUS handling */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Map: unit+subunit <--> index in xbus->xpds[]
|
|
|
|
*/
|
2012-01-11 23:21:13 +08:00
|
|
|
#define XPD_IDX(unit, subunit) ((unit) * MAX_SUBUNIT + (subunit))
|
2010-08-28 05:59:27 +08:00
|
|
|
#define XBUS_UNIT(idx) ((idx) / MAX_SUBUNIT)
|
|
|
|
#define XBUS_SUBUNIT(idx) ((idx) % MAX_SUBUNIT)
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
xpd_t *xpd_of(const xbus_t *xbus, int xpd_num);
|
|
|
|
xpd_t *xpd_byaddr(const xbus_t *xbus, uint unit, uint subunit);
|
|
|
|
int xbus_check_unique(xbus_t *xbus);
|
|
|
|
bool xbus_setstate(xbus_t *xbus, enum xbus_state newstate);
|
|
|
|
bool xbus_setflags(xbus_t *xbus, int flagbit, bool on);
|
|
|
|
xbus_t *xbus_new(struct xbus_ops *ops, ushort max_send_size,
|
|
|
|
struct device *transport_device, void *priv);
|
|
|
|
void xbus_free(xbus_t *xbus);
|
|
|
|
int xbus_connect(xbus_t *xbus);
|
|
|
|
int xbus_activate(xbus_t *xbus);
|
|
|
|
void xbus_deactivate(xbus_t *xbus);
|
|
|
|
void xbus_disconnect(xbus_t *xbus);
|
|
|
|
void xbus_receive_xframe(xbus_t *xbus, xframe_t *xframe);
|
|
|
|
int xbus_process_worker(xbus_t *xbus);
|
|
|
|
int waitfor_xpds(xbus_t *xbus, char *buf);
|
|
|
|
|
|
|
|
int xbus_xpd_bind(xbus_t *xbus, xpd_t *xpd, int unit, int subunit);
|
|
|
|
int xbus_xpd_unbind(xbus_t *xbus, xpd_t *xpd);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
/* sysfs */
|
2012-01-11 23:22:46 +08:00
|
|
|
int xpd_device_register(xbus_t *xbus, xpd_t *xpd);
|
|
|
|
void xpd_device_unregister(xpd_t *xpd);
|
|
|
|
int echocancel_xpd(xpd_t *xpd, int on);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
int xbus_is_registered(xbus_t *xbus);
|
|
|
|
int xbus_register_dahdi_device(xbus_t *xbus);
|
|
|
|
void xbus_unregister_dahdi_device(xbus_t *xbus);
|
2011-11-11 00:56:29 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
int xpp_driver_init(void);
|
|
|
|
void xpp_driver_exit(void);
|
|
|
|
int xbus_sysfs_transport_create(xbus_t *xbus);
|
|
|
|
void xbus_sysfs_transport_remove(xbus_t *xbus);
|
|
|
|
int xbus_sysfs_create(xbus_t *xbus);
|
|
|
|
void xbus_sysfs_remove(xbus_t *xbus);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
|
|
|
#ifdef OLD_HOTPLUG_SUPPORT_269
|
|
|
|
/* Copy from new kernels lib/kobject_uevent.c */
|
|
|
|
enum kobject_action {
|
|
|
|
KOBJ_ADD,
|
|
|
|
KOBJ_REMOVE,
|
|
|
|
KOBJ_CHANGE,
|
|
|
|
KOBJ_MOUNT,
|
|
|
|
KOBJ_UMOUNT,
|
|
|
|
KOBJ_OFFLINE,
|
|
|
|
KOBJ_ONLINE,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
void astribank_uevent_send(xbus_t *xbus, enum kobject_action act);
|
2010-08-28 05:59:27 +08:00
|
|
|
|
2012-01-11 23:22:46 +08:00
|
|
|
#endif /* XBUS_CORE_H */
|