summaryrefslogtreecommitdiff
path: root/drivers/dahdi/xpp/xbus-core.h
blob: 9bafb82c476b5179cd1c5b15e8e2604474ade1e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*
 * 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"

#define	MAX_BUSES	128
#define	XFRAME_DATASIZE	512
#define	MAX_ENV_STR	40

/* forward declarations */
struct xbus_workqueue;

#ifdef	__KERNEL__

struct xbus_ops {
	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);
};

/*
 * 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 {
	char *name;
} xbus_counters[] = {
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),};

#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 {
	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];
};

#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
#define	XBUS_FLAGS(xbus, flg) \
	test_bit(XBUS_FLAG_ ## flg, &((xbus)->transport.transport_flags))

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 {
	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;
};

/*
 * Allocate/Free an xframe from pools of empty xframes.
 * Calls to {get, put}_xframe are wrapped in
 * 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);

#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))

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);

/*
 * Echo canceller related data
 */
#define	ECHO_TIMESLOTS	128

struct echoops {
	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);
};

struct xbus_echo_state {
	const struct echoops *echoops;
	__u8 timeslots[ECHO_TIMESLOTS];
	int xpd_idx;
	struct device_attribute *da[MAX_XPDS];
};
#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__))

/*
 * An xbus is a transport layer for Xorcom Protocol commands
 */
struct xbus {
	char busname[XBUS_NAMELEN];	/* set by xbus_new() */

	/* low-level bus drivers set these 2 fields */
	char connector[XBUS_DESCLEN];
	char label[LABEL_SIZE];
	__u8 revision;		/* Protocol revision */
	struct xbus_transport transport;
	struct dahdi_device *ddev;

	int num;
	struct xpd *xpds[MAX_XPDS];
	struct xbus_echo_state echo_state;

	int command_tick_counter;
	int usec_nosend;	/* Firmware flow control */
	struct xframe_queue command_queue;
	wait_queue_head_t command_queue_empty;

	struct xframe_queue send_pool;	/* empty xframes for send */
	struct xframe_queue receive_pool;	/* empty xframes for receive */

	/* tasklet processing */
	struct xframe_queue receive_queue;
	struct tasklet_struct receive_tasklet;
	int cpu_rcv_intr[NR_CPUS];
	int cpu_rcv_tasklet[NR_CPUS];

	struct quirks {
		unsigned int has_fxo:1;
		unsigned int has_digital_span:1;
	} quirks;
	bool self_ticking;
	enum sync_mode sync_mode;
	/* Managed by low-level drivers: */
	enum sync_mode sync_mode_default;
	struct timer_list command_timer;
	unsigned int xbus_frag_count;
	struct xframe_queue pcm_tospan;

	struct xpp_ticker ticker;	/* for tick rate */
	struct xpp_drift drift;	/* for tick offset */

	atomic_t pcm_rx_counter;
	unsigned int global_counter;

	/* Device-Model */
	struct device astribank;
#define	dev_to_xbus(dev)	container_of(dev, struct xbus, astribank)
	struct kref kref;
#define kref_to_xbus(k) container_of(k, struct xbus, kref)

	spinlock_t lock;

	/* PCM metrics */
	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) */
#ifdef	SAMPLE_TICKS
#define	SAMPLE_SIZE	1000
	int sample_ticks[SAMPLE_SIZE];
	bool sample_running;
	int sample_pos;
#endif

	struct xbus_workqueue worker;

	/*
	 * Sync adjustment
	 */
	int sync_adjustment;
	int sync_adjustment_offset;
	long pll_updated_at;

	atomic_t num_xpds;

#ifdef CONFIG_PROC_FS
	struct proc_dir_entry *proc_xbus_dir;
	struct proc_dir_entry *proc_xbus_summary;
#ifdef	PROTOCOL_DEBUG
	struct proc_dir_entry *proc_xbus_command;
#endif
#endif

	/* statistics */
	int counters[XBUS_COUNTER_MAX];
};
#endif

#define	XFRAME_MAGIC	123456L

struct xframe {
	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;
	/* filled by transport layer */
	size_t frame_maxlen;
	__u8 *packets;		/* max XFRAME_DATASIZE */
	__u8 *first_free;
	int usec_towait;	/* prevent overflowing AB */
	void *priv;
};

void xframe_init(xbus_t *xbus, xframe_t *xframe, void *buf, size_t maxsize,
		 void *priv);

#define XFRAME_LEN(frame)	atomic_read(&(frame)->frame_len)

int xbus_core_init(void);	/* Initializer */
void xbus_core_shutdown(void);	/* Terminator */

/* Frame handling */
void dump_xframe(const char msg[], const xbus_t *xbus, const xframe_t *xframe,
		 int debug);
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[]
 */
#define	XPD_IDX(unit, subunit)	((unit) * MAX_SUBUNIT + (subunit))
#define	XBUS_UNIT(idx)		((idx) / MAX_SUBUNIT)
#define	XBUS_SUBUNIT(idx)	((idx) % MAX_SUBUNIT)

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);

/* sysfs */
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);

int xbus_is_registered(xbus_t *xbus);
int xbus_register_dahdi_device(xbus_t *xbus);
void xbus_unregister_dahdi_device(xbus_t *xbus);

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);

#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

void astribank_uevent_send(xbus_t *xbus, enum kobject_action act);

#endif /* XBUS_CORE_H */