blob: 4ab7c367306a0c7c56c9807ad31fa97ab4bb174d [file] [log] [blame]
Sylvain Munautb559a532019-05-09 11:14:26 +02001/* OpenBSC Abis input driver for osmo-e1d */
2
3/* (C) 2019 by Sylvain Munaut <tnt@246tNt.com>
4 *
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
Sylvain Munautb559a532019-05-09 11:14:26 +020019 */
20
21#include "config.h"
Harald Welteac290ee2020-08-02 10:43:42 +020022#include "internal.h"
Sylvain Munautb559a532019-05-09 11:14:26 +020023
24#ifdef HAVE_E1D
25
26#include <errno.h>
27#include <unistd.h>
28#include <string.h>
29
30#include <osmocom/core/bits.h>
31#include <osmocom/core/logging.h>
32
33#include <osmocom/vty/vty.h>
34
35#include <osmocom/abis/subchan_demux.h>
36#include <osmocom/abis/e1_input.h>
37#include <osmocom/abis/lapd.h>
38
39#include <osmocom/e1d/proto.h>
40#include <osmocom/e1d/proto_clnt.h>
41
42
43#define TS_SIGN_ALLOC_SIZE 300
44
45struct osmo_e1dp_client *g_e1d;
46
Harald Weltef7766692020-06-30 19:04:07 +020047static int invertbits = 1;
48
Sylvain Munautb559a532019-05-09 11:14:26 +020049/* pre-declaration */
50extern struct e1inp_driver e1d_driver;
51static int e1d_want_write(struct e1inp_ts *e1i_ts);
52
53
54static int
55handle_ts_sign_read(struct osmo_fd *bfd)
56{
57 struct e1inp_line *line = bfd->data;
58 unsigned int ts_nr = bfd->priv_nr;
59 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
60 struct msgb *msg = msgb_alloc(TS_SIGN_ALLOC_SIZE, "E1D Signaling TS");
61 int ret;
62
63 if (!msg)
64 return -ENOMEM;
65
66 ret = read(bfd->fd, msg->data, TS_SIGN_ALLOC_SIZE - 16);
67 if (ret < 0) {
Harald Welte28898d12020-01-12 13:34:07 +010068 LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
Sylvain Munautb559a532019-05-09 11:14:26 +020069 return ret;
70 }
71
72 msgb_put(msg, ret);
73 if (ret <= 1) {
Harald Welte28898d12020-01-12 13:34:07 +010074 LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
Sylvain Munautb559a532019-05-09 11:14:26 +020075 return ret;
76 }
77
78 return e1inp_rx_ts_lapd(e1i_ts, msg);
79}
80
81static void
82timeout_ts_sign_write(void *data)
83{
84 struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
85
86 /* trigger write of ts1, due to tx delay timer */
87 e1d_want_write(e1i_ts);
88}
89
90static int
91handle_ts_sign_write(struct osmo_fd *bfd)
92{
93 struct e1inp_line *line = bfd->data;
94 unsigned int ts_nr = bfd->priv_nr;
95 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
96 struct e1inp_sign_link *sign_link;
97 struct msgb *msg;
98
Harald Welte949b8a22020-10-18 23:01:53 +020099 osmo_fd_write_disable(bfd);
Sylvain Munautb559a532019-05-09 11:14:26 +0200100
101 /* get the next msg for this timeslot */
102 msg = e1inp_tx_ts(e1i_ts, &sign_link);
103 if (!msg) {
104 /* no message after tx delay timer */
105 return 0;
106 }
107
108 DEBUGP(DLMI, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
109 lapd_transmit(e1i_ts->lapd, sign_link->tei,
110 sign_link->sapi, msg);
111
112 /* set tx delay timer for next event */
113 osmo_timer_setup(&e1i_ts->sign.tx_timer, timeout_ts_sign_write, e1i_ts);
114 osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, 50000);
115
116 return 0;
117}
118
Harald Weltef7766692020-06-30 19:04:07 +0200119#define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN)
120
121static int
122handle_ts_trau_write(struct osmo_fd *bfd)
123{
124 struct e1inp_line *line = bfd->data;
125 unsigned int ts_nr = bfd->priv_nr;
126 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
127 uint8_t tx_buf[D_BCHAN_TX_GRAN];
128 struct subch_mux *mx = &e1i_ts->trau.mux;
129 int ret;
130
131 ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN);
132
133 if (ret != D_BCHAN_TX_GRAN) {
134 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Huh, got ret of %d\n", ret);
135 if (ret < 0)
136 return ret;
137 }
138
139 LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN TX: %s\n", osmo_hexdump(tx_buf, D_BCHAN_TX_GRAN));
140
141 if (invertbits)
142 osmo_revbytebits_buf(tx_buf, ret);
143
144 ret = write(bfd->fd, tx_buf, ret);
145 if (ret < D_BCHAN_TX_GRAN)
146 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n",
147 ret, D_BCHAN_TX_GRAN);
148
149 return ret;
150}
151
152
153static int
154handle_ts_trau_read(struct osmo_fd *bfd)
155{
156 struct e1inp_line *line = bfd->data;
157 unsigned int ts_nr = bfd->priv_nr;
158 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
159 struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "E1D Rx TSx");
160 int ret;
161
162 if (!msg)
163 return -ENOMEM;
164
165 ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
166 if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
167 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "read error %d %s\n", ret, strerror(errno));
168 return ret;
169 }
170
171 if (invertbits)
172 osmo_revbytebits_buf(msg->data, ret);
173
174 msgb_put(msg, ret);
175
176 msg->l2h = msg->data;
177 LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret));
178 ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
179 /* physical layer indicates that data has been sent,
180 * we thus can send some more data */
181 ret = handle_ts_trau_write(bfd);
182
183 return ret;
184}
185
Harald Welteca712962020-06-30 19:12:42 +0200186/* write to a raw channel TS */
187static int handle_ts_raw_write(struct osmo_fd *bfd)
188{
189 struct e1inp_line *line = bfd->data;
190 unsigned int ts_nr = bfd->priv_nr;
191 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
192 struct msgb *msg;
193 int ret;
194
195 /* get the next msg for this timeslot */
196 msg = e1inp_tx_ts(e1i_ts, NULL);
197 if (!msg)
198 return 0;
199
200 if (msg->len != D_BCHAN_TX_GRAN) {
201 /* This might lead to a transmit underrun, as we call tx
202 * from the rx path, as there's no select/poll on dahdi
203 * */
204 LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unexpected msg->len = %u, "
205 "expected %u\n", msg->len, D_BCHAN_TX_GRAN);
206 }
207
208 LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN TX: %s\n", osmo_hexdump(msg->data, msg->len));
209
210 if (0/*invertbits*/)
211 osmo_revbytebits_buf(msg->data, msg->len);
212
213 ret = write(bfd->fd, msg->data, msg->len);
214 if (ret < msg->len)
215 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n", ret, msg->len);
216 msgb_free(msg);
217
218 return ret;
219}
220
221static int handle_ts_raw_read(struct osmo_fd *bfd)
222{
223 struct e1inp_line *line = bfd->data;
224 unsigned int ts_nr = bfd->priv_nr;
225 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
226 struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "E1D Raw TS");
227 int ret;
228
229 if (!msg)
230 return -ENOMEM;
231
232 ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
233 if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
234 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "read error %d %s\n", ret, strerror(errno));
Vadim Yanitskiybb9cba02022-04-18 02:12:00 +0300235 msgb_free(msg);
Harald Welteca712962020-06-30 19:12:42 +0200236 return ret;
237 }
238
239 if (0/*invertbits*/)
240 osmo_revbytebits_buf(msg->data, ret);
241
242 msgb_put(msg, ret);
243
244 msg->l2h = msg->data;
Vadim Yanitskiyadb2e162022-04-18 02:13:21 +0300245 LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN RX: %s\n", msgb_hexdump_l2(msg));
Harald Welteca712962020-06-30 19:12:42 +0200246 ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
247 /* physical layer indicates that data has been sent,
248 * we thus can send some more data */
249 ret = handle_ts_raw_write(bfd);
250
251 return ret;
252}
Sylvain Munautb559a532019-05-09 11:14:26 +0200253
254static void
255e1d_write_msg(struct msgb *msg, void *cbdata)
256{
257 struct osmo_fd *bfd = cbdata;
258 struct e1inp_line *line = bfd->data;
259 unsigned int ts_nr = bfd->priv_nr;
260 struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
261 int ret;
262
263 ret = write(bfd->fd, msg->data, msg->len);
264 msgb_free(msg);
265 if (ret < 0)
Harald Welte28898d12020-01-12 13:34:07 +0100266 LOGPITS(e1i_ts, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
Sylvain Munautb559a532019-05-09 11:14:26 +0200267}
268
269static int
270e1d_fd_cb(struct osmo_fd *bfd, unsigned int what)
271{
272 struct e1inp_line *line = bfd->data;
273 unsigned int ts_nr = bfd->priv_nr;
274 unsigned int idx = ts_nr-1;
275 struct e1inp_ts *e1i_ts = &line->ts[idx];
276 int ret = 0;
277
278 switch (e1i_ts->type) {
279 case E1INP_TS_TYPE_SIGN:
Harald Weltede3959e2020-10-18 22:28:50 +0200280 if (what & OSMO_FD_READ)
Sylvain Munautb559a532019-05-09 11:14:26 +0200281 ret = handle_ts_sign_read(bfd);
Harald Weltede3959e2020-10-18 22:28:50 +0200282 if (what & OSMO_FD_WRITE)
Sylvain Munautb559a532019-05-09 11:14:26 +0200283 ret = handle_ts_sign_write(bfd);
284 break;
Harald Weltef7766692020-06-30 19:04:07 +0200285 case E1INP_TS_TYPE_TRAU:
Harald Weltede3959e2020-10-18 22:28:50 +0200286 if (what & OSMO_FD_READ)
Harald Weltef7766692020-06-30 19:04:07 +0200287 ret = handle_ts_trau_read(bfd);
Harald Weltede3959e2020-10-18 22:28:50 +0200288 if (what & OSMO_FD_WRITE)
Harald Weltef7766692020-06-30 19:04:07 +0200289 ret = handle_ts_trau_write(bfd);
290 break;
Harald Welteca712962020-06-30 19:12:42 +0200291 case E1INP_TS_TYPE_RAW:
Harald Weltede3959e2020-10-18 22:28:50 +0200292 if (what & OSMO_FD_READ)
Harald Welteca712962020-06-30 19:12:42 +0200293 ret = handle_ts_raw_read(bfd);
Harald Weltede3959e2020-10-18 22:28:50 +0200294 if (what & OSMO_FD_WRITE)
Harald Welteca712962020-06-30 19:12:42 +0200295 ret = handle_ts_raw_write(bfd);
296 break;
Sylvain Munautb559a532019-05-09 11:14:26 +0200297 default:
Harald Welte28898d12020-01-12 13:34:07 +0100298 LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unknown/unsupported E1 TS type %u\n", e1i_ts->type);
Sylvain Munautb559a532019-05-09 11:14:26 +0200299 break;
300 }
301
302 return ret;
303}
304
305
306static int
307e1d_want_write(struct e1inp_ts *e1i_ts)
308{
309 /* We never include the DAHDI B-Channel FD into the writeset */
Harald Welteb3952c62020-06-20 22:07:36 +0200310 if (e1i_ts->type == E1INP_TS_TYPE_TRAU ||
311 e1i_ts->type == E1INP_TS_TYPE_I460) {
Harald Welte28898d12020-01-12 13:34:07 +0100312 LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Trying to write TRAU ts\n");
Sylvain Munautb559a532019-05-09 11:14:26 +0200313 return 0;
314 }
315
Harald Welte949b8a22020-10-18 23:01:53 +0200316 osmo_fd_write_enable(&e1i_ts->driver.e1d.fd);
Sylvain Munautb559a532019-05-09 11:14:26 +0200317
318 return 0;
319}
320
321static int
322e1d_line_update(struct e1inp_line *line)
323{
324 int ts;
325 int ret;
326
Harald Welteccf84ea2020-01-12 12:31:45 +0100327 /* we use higher 4 bits for interface, lower 4 bits for line,
328 * resulting in max. 16 interfaces with 16 lines each */
329 uint8_t e1d_intf = (line->port_nr >> 4) & 0xF;
330 uint8_t e1d_line = line->port_nr & 0xF;
Harald Welteeed39162020-01-12 13:46:34 +0100331 struct osmo_e1dp_ts_info *ts_info;
332 int num_ts_info;
Harald Welteccf84ea2020-01-12 12:31:45 +0100333
Sylvain Munautb559a532019-05-09 11:14:26 +0200334 if (line->driver != &e1d_driver)
335 return -EINVAL;
336
Harald Welteaccd6772020-01-12 13:36:56 +0100337 if (!g_e1d) {
338 /* Connect to daemon */
339 g_e1d = osmo_e1dp_client_create(NULL, "/tmp/osmo-e1d.ctl");
340 if (!g_e1d) {
341 LOGPIL(line, DLINP, LOGL_ERROR, "Unable to connect to osmo-e1d daemon\n");
342 return -EPIPE;
343 }
344 }
Sylvain Munautb559a532019-05-09 11:14:26 +0200345
Harald Welte28898d12020-01-12 13:34:07 +0100346 LOGPIL(line, DLINP, LOGL_NOTICE, "Line update %d %d=E1D(%d:%d) %d\n", line->num, line->port_nr,
Harald Welteccf84ea2020-01-12 12:31:45 +0100347 e1d_intf, e1d_line, line->num_ts);
Sylvain Munautb559a532019-05-09 11:14:26 +0200348
Harald Welteeed39162020-01-12 13:46:34 +0100349 ret = osmo_e1dp_client_ts_query(g_e1d, &ts_info, &num_ts_info, e1d_intf, e1d_line, E1DP_INVALID);
350 if (ret < 0) {
351 LOGPIL(line, DLINP, LOGL_ERROR, "Cannot query E1D for timeslot information: %d\n", ret);
352 return -EIO;
353 }
354
Sylvain Munautb559a532019-05-09 11:14:26 +0200355 for (ts=1; ts<line->num_ts; ts++)
356 {
357 unsigned int idx = ts-1;
358 struct e1inp_ts *e1i_ts = &line->ts[idx];
359 struct osmo_fd *bfd = &e1i_ts->driver.e1d.fd;
360
361 /* unregister FD if it was already registered */
362 if (bfd->list.next && bfd->list.next != LLIST_POISON1)
363 osmo_fd_unregister(bfd);
364
Harald Welteeed39162020-01-12 13:46:34 +0100365 if (e1i_ts->type != E1INP_TS_TYPE_NONE && ts >= num_ts_info) {
Vadim Yanitskiy4da31b82020-10-29 18:03:13 +0700366 LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Timeslot configured, but not existent "
Harald Welteeed39162020-01-12 13:46:34 +0100367 "on E1D side; skipping\n");
368 continue;
369 }
370
Sylvain Munautb559a532019-05-09 11:14:26 +0200371 switch (e1i_ts->type) {
372 case E1INP_TS_TYPE_NONE:
373 /* close/release LAPD instance, if any */
374 if (e1i_ts->lapd) {
375 lapd_instance_free(e1i_ts->lapd);
376 e1i_ts->lapd = NULL;
377 }
378 if (bfd->fd) {
379 close(bfd->fd);
380 bfd->fd = 0;
381 }
382 continue;
383 case E1INP_TS_TYPE_SIGN:
Harald Welteeed39162020-01-12 13:46:34 +0100384 if (bfd->fd > 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_HDLCFCS) {
385 close(bfd->fd);
386 bfd->fd = 0;
387 }
Harald Welteccf84ea2020-01-12 12:31:45 +0100388 if (bfd->fd <= 0) {
389 bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
Harald Welteac290ee2020-08-02 10:43:42 +0200390 E1DP_TSMODE_HDLCFCS, D_BCHAN_TX_GRAN);
Harald Welteccf84ea2020-01-12 12:31:45 +0100391 }
Sylvain Munautb559a532019-05-09 11:14:26 +0200392 if (bfd->fd < 0) {
Harald Welte28898d12020-01-12 13:34:07 +0100393 LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
Harald Welteeed39162020-01-12 13:46:34 +0100394 talloc_free(ts_info);
Sylvain Munautb559a532019-05-09 11:14:26 +0200395 return -EIO;
396 }
Harald Weltede3959e2020-10-18 22:28:50 +0200397 bfd->when = OSMO_FD_READ;
Sylvain Munautb559a532019-05-09 11:14:26 +0200398
Harald Welteb9031882020-05-02 21:09:15 +0200399 if (!e1i_ts->lapd) {
400 char name[32];
Harald Welte36166d02020-06-30 18:10:31 +0200401 e1inp_ts_name(name, sizeof(name), e1i_ts);
Harald Welteb9031882020-05-02 21:09:15 +0200402 e1i_ts->lapd = lapd_instance_alloc2(1,
Sylvain Munautb559a532019-05-09 11:14:26 +0200403 e1d_write_msg, bfd, e1inp_dlsap_up,
Harald Welteb9031882020-05-02 21:09:15 +0200404 e1i_ts, &lapd_profile_abis, name);
405 }
Sylvain Munautb559a532019-05-09 11:14:26 +0200406 break;
407 case E1INP_TS_TYPE_HDLC:
Harald Welteeed39162020-01-12 13:46:34 +0100408 /* close/release LAPD instance, if any */
409 if (e1i_ts->lapd) {
410 lapd_instance_free(e1i_ts->lapd);
411 e1i_ts->lapd = NULL;
412 }
413 /* close, if old timeslot mode doesn't match new config */
414 if (bfd->fd > 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_HDLCFCS) {
415 close(bfd->fd);
416 bfd->fd = 0;
417 }
418 if (bfd->fd <= 0) {
419 bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
Harald Welteac290ee2020-08-02 10:43:42 +0200420 E1DP_TSMODE_HDLCFCS, D_BCHAN_TX_GRAN);
Harald Welteeed39162020-01-12 13:46:34 +0100421 }
422 if (bfd->fd < 0) {
423 LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
424 talloc_free(ts_info);
425 return -EIO;
426 }
Harald Weltede3959e2020-10-18 22:28:50 +0200427 bfd->when = OSMO_FD_READ;
Sylvain Munautb559a532019-05-09 11:14:26 +0200428 break;
429 case E1INP_TS_TYPE_TRAU:
Harald Welteb3952c62020-06-20 22:07:36 +0200430 case E1INP_TS_TYPE_I460:
Sylvain Munautb559a532019-05-09 11:14:26 +0200431 case E1INP_TS_TYPE_RAW:
Harald Welteeed39162020-01-12 13:46:34 +0100432 /* close/release LAPD instance, if any */
433 if (e1i_ts->lapd) {
434 lapd_instance_free(e1i_ts->lapd);
435 e1i_ts->lapd = NULL;
436 }
437 /* close, if old timeslot mode doesn't match new config */
438 if (bfd->fd > 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_RAW) {
439 close(bfd->fd);
440 bfd->fd = 0;
441 }
442 if (bfd->fd <= 0) {
443 bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
Harald Welteac290ee2020-08-02 10:43:42 +0200444 E1DP_TSMODE_RAW, D_BCHAN_TX_GRAN);
Harald Welteeed39162020-01-12 13:46:34 +0100445 }
446 if (bfd->fd < 0) {
447 LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
448 talloc_free(ts_info);
449 return -EIO;
450 }
Harald Weltede3959e2020-10-18 22:28:50 +0200451 bfd->when = OSMO_FD_READ;
Sylvain Munautb559a532019-05-09 11:14:26 +0200452 break;
453 };
454
Harald Welte6e831b72020-10-18 22:59:58 +0200455 osmo_fd_setup(bfd, bfd->fd, bfd->when, e1d_fd_cb, line, ts);
Sylvain Munautb559a532019-05-09 11:14:26 +0200456 ret = osmo_fd_register(bfd);
457 if (ret < 0) {
Harald Welte28898d12020-01-12 13:34:07 +0100458 LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "could not register FD: %s\n", strerror(ret));
Harald Welteeed39162020-01-12 13:46:34 +0100459 talloc_free(ts_info);
Sylvain Munautb559a532019-05-09 11:14:26 +0200460 return ret;
461 }
462 }
463
Harald Welteeed39162020-01-12 13:46:34 +0100464 talloc_free(ts_info);
Sylvain Munautb559a532019-05-09 11:14:26 +0200465 return 0;
466}
467
Sylvain Munautb559a532019-05-09 11:14:26 +0200468struct e1inp_driver e1d_driver = {
469 .name = "e1d",
470 .want_write = e1d_want_write,
471 .line_update = e1d_line_update,
Sylvain Munautb559a532019-05-09 11:14:26 +0200472};
473
474int
475e1inp_e1d_init(void)
476{
Sylvain Munautb559a532019-05-09 11:14:26 +0200477 /* register the driver with the core */
478 return e1inp_driver_register(&e1d_driver);
479}
480
481#endif /* HAVE_E1D */