Revert "Remove support for xpp drivers."
[dahdi/linux.git] / drivers / dahdi / xpp / card_echo.c
1 /*
2  * Written by Oron Peled <oron@actcom.co.il>
3  * Copyright (C) 2011, Xorcom
4  *
5  * All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <linux/init.h>
24 #include <linux/module.h>
25 #include <linux/fs.h>
26 #include <linux/delay.h>
27 #include "xpd.h"
28 #include "xproto.h"
29 #include "card_echo.h"
30 #include "xpp_dahdi.h"
31 #include "dahdi_debug.h"
32 #include "xpd.h"
33 #include "xbus-core.h"
34
35 static const char rcsid[] = "$Id$";
36
37 /* must be before dahdi_debug.h: */
38 static DEF_PARM(int, debug, 0, 0644, "Print DBG statements");
39
40 /*---------------- ECHO Protocol Commands ----------------------------------*/
41
42 static bool echo_packet_is_valid(xpacket_t *pack);
43 static void echo_packet_dump(const char *msg, xpacket_t *pack);
44
45 DEF_RPACKET_DATA(ECHO, SET, __u8 timeslots[ECHO_TIMESLOTS];);
46
47 DEF_RPACKET_DATA(ECHO, SET_REPLY, __u8 status; __u8 reserved;);
48
49 struct ECHO_priv_data {
50 };
51
52 static xproto_table_t PROTO_TABLE(ECHO);
53
54 /*---------------- ECHO: Methods -------------------------------------------*/
55
56 static xpd_t *ECHO_card_new(xbus_t *xbus, int unit, int subunit,
57                             const xproto_table_t *proto_table,
58                             const struct unit_descriptor *unit_descriptor,
59                             bool to_phone)
60 {
61         xpd_t *xpd = NULL;
62         int channels = 0;
63
64         if (unit_descriptor->ports_per_chip != 1) {
65                 XBUS_ERR(xbus, "Bad subunit_ports=%d\n", unit_descriptor->ports_per_chip);
66                 return NULL;
67         }
68         XBUS_DBG(GENERAL, xbus, "\n");
69         xpd =
70             xpd_alloc(xbus, unit, subunit,
71                       sizeof(struct ECHO_priv_data), proto_table, unit_descriptor, channels);
72         if (!xpd)
73                 return NULL;
74         xpd->type_name = "ECHO";
75         return xpd;
76 }
77
78 static int ECHO_card_init(xbus_t *xbus, xpd_t *xpd)
79 {
80         int ret = 0;
81
82         BUG_ON(!xpd);
83         XPD_DBG(GENERAL, xpd, "\n");
84         xpd->xpd_type = XPD_TYPE_ECHO;
85         XPD_DBG(DEVICES, xpd, "%s\n", xpd->type_name);
86         ret = CALL_EC_METHOD(ec_update, xbus, xbus);
87         return ret;
88 }
89
90 static int ECHO_card_remove(xbus_t *xbus, xpd_t *xpd)
91 {
92         BUG_ON(!xpd);
93         XPD_DBG(GENERAL, xpd, "\n");
94         return 0;
95 }
96
97 static int ECHO_card_tick(xbus_t *xbus, xpd_t *xpd)
98 {
99         struct ECHO_priv_data *priv;
100
101         BUG_ON(!xpd);
102         priv = xpd->priv;
103         BUG_ON(!priv);
104         return 0;
105 }
106
107 static int ECHO_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
108 {
109         unsigned long flags;
110         struct xpd_addr addr;
111         xpd_t *orig_xpd;
112
113         /* Map UNIT + PORTNUM to XPD */
114         orig_xpd = xpd;
115         addr.unit = orig_xpd->addr.unit;
116         addr.subunit = info->h.portnum;
117         xpd = xpd_byaddr(xbus, addr.unit, addr.subunit);
118         if (!xpd) {
119                 static int rate_limit;
120
121                 if ((rate_limit++ % 1003) < 5)
122                         notify_bad_xpd(__func__, xbus, addr, orig_xpd->xpdname);
123                 return -EPROTO;
124         }
125         spin_lock_irqsave(&xpd->lock, flags);
126         /* Update /proc info only if reply related to last reg read request */
127         if (REG_FIELD(&xpd->requested_reply, regnum) ==
128                         REG_FIELD(info, regnum)
129                 && REG_FIELD(&xpd->requested_reply, do_subreg) ==
130                         REG_FIELD(info, do_subreg)
131                 && REG_FIELD(&xpd->requested_reply, subreg) ==
132                         REG_FIELD(info, subreg)) {
133                 xpd->last_reply = *info;
134         }
135         spin_unlock_irqrestore(&xpd->lock, flags);
136         return 0;
137 }
138
139 /*---------------- ECHO: HOST COMMANDS -------------------------------------*/
140
141 static /* 0x39 */ HOSTCMD(ECHO, SET)
142 {
143         struct xbus_echo_state *es;
144         __u8 *ts;
145         xframe_t *xframe;
146         xpacket_t *pack;
147         int ret;
148         uint16_t frm_len;
149         int xpd_idx;
150
151         BUG_ON(!xbus);
152         /*
153          * Find echo canceller XPD address
154          */
155         es = &xbus->echo_state;
156         xpd_idx = es->xpd_idx;
157         XFRAME_NEW_CMD(xframe, pack, xbus, ECHO, SET, xpd_idx);
158         ts = RPACKET_FIELD(pack, ECHO, SET, timeslots);
159         memcpy(ts, es->timeslots, ECHO_TIMESLOTS);
160         frm_len = XFRAME_LEN(xframe);
161         XBUS_DBG(GENERAL, xbus, "ECHO SET: (len = %d)\n", frm_len);
162         ret = send_cmd_frame(xbus, xframe);
163         return ret;
164 }
165
166 static int ECHO_ec_set(xpd_t *xpd, int pos, bool on)
167 {
168         int ts_number;
169         int ts_mask;
170         __u8 *ts;
171
172         ts = xpd->xbus->echo_state.timeslots;
173         /*
174          * ts_number = PCM time slot ("channel number" in the PCM XPP packet)
175          *
176          * Bit 0 is for UNIT=0
177          * PRI: ts_number * 4 + SUBUNIT
178          * BRI: ts_number
179          * FXS/FXO(all units): UNIT * 32 + ts_number
180          *
181          * Bit 1 is for UNIT=1-3: FXS/FXO
182          *
183          */
184         ts_mask = (xpd->addr.unit == 0) ? 0x1 : 0x2;    /* Which bit? */
185         ts_number = CALL_PHONE_METHOD(echocancel_timeslot, xpd, pos);
186         if (ts_number >= ECHO_TIMESLOTS || ts_number < 0) {
187                 XPD_ERR(xpd, "Bad ts_number=%d\n", ts_number);
188                 return -EINVAL;
189         } else {
190                 if (on)
191                         ts[ts_number] |= ts_mask;
192                 else
193                         ts[ts_number] &= ~ts_mask;
194         }
195         LINE_DBG(GENERAL, xpd, pos, "%s = %d -- ts_number=%d ts_mask=0x%X\n",
196                  __func__, on, ts_number, ts_mask);
197         return 0;
198 }
199
200 static int ECHO_ec_get(xpd_t *xpd, int pos)
201 {
202         int ts_number;
203         int ts_mask;
204         int is_on;
205         __u8 *ts;
206
207         ts = xpd->xbus->echo_state.timeslots;
208         ts_mask = (xpd->addr.unit == 0) ? 0x1 : 0x2;    /* Which bit? */
209         ts_number = CALL_PHONE_METHOD(echocancel_timeslot, xpd, pos);
210         if (ts_number >= ECHO_TIMESLOTS || ts_number < 0) {
211                 XPD_ERR(xpd, "Bad ts_number=%d\n", ts_number);
212                 return -EINVAL;
213         } else {
214                 is_on = ts[ts_number] & ts_mask;
215         }
216 #if 0
217         LINE_DBG(GENERAL, xpd, pos, "ec_get=%d -- ts_number=%d ts_mask=0x%X\n",
218                  is_on, ts_number, ts_mask);
219 #endif
220         return is_on;
221 }
222
223 static void ECHO_ec_dump(xbus_t *xbus)
224 {
225         __u8 *ts;
226         int i;
227
228         ts = xbus->echo_state.timeslots;
229         for (i = 0; i + 15 < ECHO_TIMESLOTS; i += 16) {
230                 XBUS_DBG(GENERAL, xbus,
231                         "EC-DUMP[%03d]: "
232                         "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X "
233                         "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
234                         i, ts[i + 0], ts[i + 1], ts[i + 2], ts[i + 3],
235                         ts[i + 4], ts[i + 5], ts[i + 6], ts[i + 7], ts[i + 8],
236                         ts[i + 9], ts[i + 10], ts[i + 11], ts[i + 12],
237                         ts[i + 13], ts[i + 14], ts[i + 15]
238                     );
239         }
240 }
241
242 static int ECHO_ec_update(xbus_t *xbus)
243 {
244         XBUS_DBG(GENERAL, xbus, "%s\n", __func__);
245         //ECHO_ec_dump(xbus);
246         return CALL_PROTO(ECHO, SET, xbus, NULL);
247 }
248
249 /*---------------- ECHO: Astribank Reply Handlers --------------------------*/
250 HANDLER_DEF(ECHO, SET_REPLY)
251 {
252         __u8 status;
253
254         BUG_ON(!xpd);
255         status = RPACKET_FIELD(pack, ECHO, SET_REPLY, status);
256         XPD_DBG(GENERAL, xpd, "status=0x%X\n", status);
257         return 0;
258 }
259
260 static const struct xops echo_xops = {
261         .card_new = ECHO_card_new,
262         .card_init = ECHO_card_init,
263         .card_remove = ECHO_card_remove,
264         .card_tick = ECHO_card_tick,
265         .card_register_reply = ECHO_card_register_reply,
266 };
267
268 static const struct echoops echoops = {
269         .ec_set = ECHO_ec_set,
270         .ec_get = ECHO_ec_get,
271         .ec_update = ECHO_ec_update,
272         .ec_dump = ECHO_ec_dump,
273 };
274
275 static xproto_table_t PROTO_TABLE(ECHO) = {
276         .owner = THIS_MODULE,
277         .entries = {
278                 /*      Table   Card    Opcode          */
279                 XENTRY( ECHO,   ECHO,   SET_REPLY       ),
280         },
281         .name = "ECHO",
282         .ports_per_subunit = 1,
283         .type = XPD_TYPE_ECHO,
284         .xops = &echo_xops,
285         .echoops = &echoops,
286         .packet_is_valid = echo_packet_is_valid,
287         .packet_dump = echo_packet_dump,
288 };
289
290 static bool echo_packet_is_valid(xpacket_t *pack)
291 {
292         const xproto_entry_t *xe = NULL;
293         // DBG(GENERAL, "\n");
294         xe = xproto_card_entry(&PROTO_TABLE(ECHO), XPACKET_OP(pack));
295         return xe != NULL;
296 }
297
298 static void echo_packet_dump(const char *msg, xpacket_t *pack)
299 {
300         DBG(GENERAL, "%s\n", msg);
301 }
302
303 /*------------------------- sysfs stuff --------------------------------*/
304 static int echo_xpd_probe(struct device *dev)
305 {
306         xpd_t *ec_xpd;
307         int ret = 0;
308
309         ec_xpd = dev_to_xpd(dev);
310         /* Is it our device? */
311         if (ec_xpd->xpd_type != XPD_TYPE_ECHO) {
312                 XPD_ERR(ec_xpd, "drop suggestion for %s (%d)\n", dev_name(dev),
313                         ec_xpd->xpd_type);
314                 return -EINVAL;
315         }
316         XPD_DBG(DEVICES, ec_xpd, "SYSFS\n");
317         return ret;
318 }
319
320 static int echo_xpd_remove(struct device *dev)
321 {
322         xpd_t *ec_xpd;
323
324         ec_xpd = dev_to_xpd(dev);
325         XPD_DBG(DEVICES, ec_xpd, "SYSFS\n");
326         return 0;
327 }
328
329 static struct xpd_driver echo_driver = {
330         .xpd_type = XPD_TYPE_ECHO,
331         .driver = {
332                    .name = "echo",
333                    .owner = THIS_MODULE,
334                    .probe = echo_xpd_probe,
335                    .remove = echo_xpd_remove}
336 };
337
338 static int __init card_echo_startup(void)
339 {
340         int ret;
341
342         ret = xpd_driver_register(&echo_driver.driver);
343         if (ret < 0)
344                 return ret;
345         INFO("FEATURE: WITH Octasic echo canceller\n");
346         xproto_register(&PROTO_TABLE(ECHO));
347         return 0;
348 }
349
350 static void __exit card_echo_cleanup(void)
351 {
352         DBG(GENERAL, "\n");
353         xproto_unregister(&PROTO_TABLE(ECHO));
354         xpd_driver_unregister(&echo_driver.driver);
355 }
356
357 MODULE_DESCRIPTION("XPP ECHO Card Driver");
358 MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>");
359 MODULE_LICENSE("GPL");
360 MODULE_ALIAS_XPD(XPD_TYPE_ECHO);
361
362 module_init(card_echo_startup);
363 module_exit(card_echo_cleanup);