summaryrefslogtreecommitdiff
path: root/drivers/dahdi/xpp/xbus-core.h
blob: 659d3d471641cd3d3b6386ce24e27b9b64fed315 (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
/*
 * 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		32
#define	XFRAME_DATASIZE		512

/* 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;
};

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

/*
 * 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];
	byte			revision;		/* Protocol revision */
	struct xbus_transport	transport;

	int			num;
	struct xpd		*xpds[MAX_XPDS];

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

	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;
	byte			*packets;	/* max XFRAME_DATASIZE */
	byte			*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	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 */