blob: 4836e940e4dcd4c292b0c16c54b6459373918cbf [file] [log] [blame]
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +04001/* pcu_l1_if.cpp
2 *
3 * Copyright (C) 2012 Ivan Klyuchnikov
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
Andreas Eversberg0aed6542012-06-23 10:33:16 +020020#include <errno.h>
21#include <string.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040022#include <gprs_rlcmac.h>
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040023#include <pcu_l1_if.h>
Ivan Kluchnikova9f1ff22012-05-24 22:25:06 +040024#include <gprs_debug.h>
Andreas Eversberg0aed6542012-06-23 10:33:16 +020025#include <bitvector.h>
26#include <gsmL1prim.h>
27#include <sys/socket.h>
28#include <linux/in.h>
29extern "C" {
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/write_queue.h>
32#include <osmocom/core/socket.h>
33#include <osmocom/core/timer.h>
34#include <osmocom/gsm/gsm_utils.h>
35}
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +040036
37#define MAX_UDP_LENGTH 1500
38
Andreas Eversberg0aed6542012-06-23 10:33:16 +020039#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
40
41struct femtol1_hdl {
42 struct gsm_time gsm_time;
43 uint32_t hLayer1; /* handle to the L1 instance in the DSP */
44 uint32_t dsp_trace_f;
45 uint16_t clk_cal;
46 struct llist_head wlc_list;
47
48 void *priv; /* user reference */
49
50 struct osmo_timer_list alive_timer;
51 unsigned int alive_prim_cnt;
52
53 struct osmo_fd read_ofd; /* osmo file descriptors */
54 struct osmo_wqueue write_q;
55
56 struct {
57 uint16_t arfcn;
58 uint8_t tn;
59 uint8_t tsc;
60 uint16_t ta;
61 } channel_info;
62
63};
64
65struct l1fwd_hdl {
66 struct sockaddr_storage remote_sa;
67 socklen_t remote_sa_len;
68
69 struct osmo_wqueue udp_wq;
70
71 struct femtol1_hdl *fl1h;
72};
73
74struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
75
76struct pcu_l1if_bts pcu_l1if_bts;
77
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +040078// Variable for storage current FN.
79int frame_number;
80
81int get_current_fn()
82{
83 return frame_number;
84}
85
86void set_current_fn(int fn)
87{
88 frame_number = fn;
89}
Ivan Kluchnikove3a05962012-03-18 15:48:51 +040090
91struct msgb *l1p_msgb_alloc(void)
92{
93 struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
94
95 if (msg)
96 msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
97
98 return msg;
99}
100
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200101// Send RLC/MAC block to OpenBTS.
102void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
103 uint32_t fn, uint8_t block_nr)
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400104{
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200105 struct msgb *nmsg = l1p_msgb_alloc();
106 GsmL1_Prim_t *prim = msgb_l1prim(nmsg);
107
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400108 prim->id = GsmL1_PrimId_PhDataReq;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200109 prim->u.phDataReq.sapi = GsmL1_Sapi_Pdtch;
110 memcpy(prim->u.phDataReq.msgUnitParam.u8Buffer, msg->data, msg->len);
111 prim->u.phDataReq.msgUnitParam.u8Size = msg->len;
112 osmo_wqueue_enqueue(&l1fh->udp_wq, nmsg);
113 msgb_free(msg);
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400114}
115
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200116void pcu_l1if_tx_agch(bitvec * block, int len)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400117{
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400118 struct msgb *msg = l1p_msgb_alloc();
119 GsmL1_Prim_t *prim = msgb_l1prim(msg);
120
121 prim->id = GsmL1_PrimId_PhDataReq;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200122 prim->u.phDataReq.sapi = GsmL1_Sapi_Agch;
Ivan Kluchnikov835f91e2012-04-30 18:00:36 +0400123 bitvec_pack(block, prim->u.phDataReq.msgUnitParam.u8Buffer);
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400124 prim->u.phDataReq.msgUnitParam.u8Size = len;
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200125 osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400126}
127
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400128int pcu_l1if_rx_pdch(GsmL1_PhDataInd_t *data_ind)
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400129{
Ivan Kluchnikov835f91e2012-04-30 18:00:36 +0400130 bitvec *block = bitvec_alloc(data_ind->msgUnitParam.u8Size);
131 bitvec_unpack(block, data_ind->msgUnitParam.u8Buffer);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400132 gprs_rlcmac_rcv_block(block);
Ivan Kluchnikov835f91e2012-04-30 18:00:36 +0400133 bitvec_free(block);
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200134
135 return 0;
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400136}
137
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400138static int handle_ph_connect_ind(struct femtol1_hdl *fl1, GsmL1_PhConnectInd_t *connect_ind)
139{
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200140 pcu_l1if_bts.trx[0].arfcn = connect_ind->u16Arfcn;
141 pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].enable = 1;
142 pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].tsc = connect_ind->u8Tsc;
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400143 (l1fh->fl1h)->channel_info.arfcn = connect_ind->u16Arfcn;
144 (l1fh->fl1h)->channel_info.tn = connect_ind->u8Tn;
145 (l1fh->fl1h)->channel_info.tsc = connect_ind->u8Tsc;
Ivan Kluchnikova9f1ff22012-05-24 22:25:06 +0400146 LOGP(DL1IF, LOGL_NOTICE, "RX: [ PCU <- BTS ] PhConnectInd: ARFCN: %u TN: %u TSC: %u \n",
147 connect_ind->u16Arfcn, (unsigned)connect_ind->u8Tn, (unsigned)connect_ind->u8Tsc);
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200148
149 return 0;
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400150}
151
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400152static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, GsmL1_PhReadyToSendInd_t *readytosend_ind)
153{
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200154 gprs_rlcmac_rcv_rts_block(0,0, (l1fh->fl1h)->channel_info.arfcn, readytosend_ind->u32Fn, 0);
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400155 return 1;
156}
157
158static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind)
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400159{
160 int rc = 0;
161 switch (data_ind->sapi) {
162 case GsmL1_Sapi_Rach:
163 break;
164 case GsmL1_Sapi_Pdtch:
165 case GsmL1_Sapi_Pacch:
166 pcu_l1if_rx_pdch(data_ind);
167 break;
168 case GsmL1_Sapi_Pbcch:
169 case GsmL1_Sapi_Pagch:
170 case GsmL1_Sapi_Ppch:
171 case GsmL1_Sapi_Pnch:
172 case GsmL1_Sapi_Ptcch:
173 case GsmL1_Sapi_Prach:
174 break;
175 default:
Ivan Kluchnikova9f1ff22012-05-24 22:25:06 +0400176 LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %u \n", data_ind->sapi);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400177 break;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400178 }
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400179
180 return rc;
181}
182
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400183static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
184{
185 int rc = 0;
186 (l1fh->fl1h)->channel_info.ta = ra_ind->measParam.i16BurstTiming;
187 rc = gprs_rlcmac_rcv_rach(ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn, ra_ind->measParam.i16BurstTiming);
188 return rc;
189}
190
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400191/* handle any random indication from the L1 */
192int pcu_l1if_handle_l1prim(struct femtol1_hdl *fl1, struct msgb *msg)
193{
194 GsmL1_Prim_t *l1p = msgb_l1prim(msg);
195 int rc = 0;
196
197 switch (l1p->id) {
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400198 case GsmL1_PrimId_PhConnectInd:
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400199 rc = handle_ph_connect_ind(fl1, &l1p->u.phConnectInd);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400200 break;
201 case GsmL1_PrimId_PhReadyToSendInd:
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400202 rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400203 break;
204 case GsmL1_PrimId_PhDataInd:
Ivan Kluchnikov4277f0c2012-04-12 14:24:35 +0400205 rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400206 break;
207 case GsmL1_PrimId_PhRaInd:
Ivan Kluchnikov5310d452012-04-17 22:00:31 +0400208 rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd);
Ivan Kluchnikove3a05962012-03-18 15:48:51 +0400209 break;
210 default:
211 break;
212 }
213
214 /* Special return value '1' means: do not free */
215 if (rc != 1)
216 msgb_free(msg);
217
218 return rc;
Ivan Kluchnikov8ee60512012-03-05 19:24:57 +0400219}
Andreas Eversberg0aed6542012-06-23 10:33:16 +0200220
221/* OpenBTS socket functions */
222
223// TODO: We should move this parameters to config file.
224#define PCU_L1_IF_PORT 5944
225
226/* data has arrived on the udp socket */
227static int udp_read_cb(struct osmo_fd *ofd)
228{
229 struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
230 struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
231 struct femtol1_hdl *fl1h = l1fh->fl1h;
232 int rc;
233
234 if (!msg)
235 return -ENOMEM;
236
237 msg->l1h = msg->data;
238
239 l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
240 rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
241 (struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
242 if (rc < 0) {
243 perror("read from udp");
244 msgb_free(msg);
245 return rc;
246 } else if (rc == 0) {
247 perror("len=0 read from udp");
248 msgb_free(msg);
249 return rc;
250 }
251 msgb_put(msg, rc);
252
253 rc = pcu_l1if_handle_l1prim(fl1h, msg);
254 return rc;
255}
256
257/* callback when we can write to the UDP socket */
258static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
259{
260 int rc;
261 struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
262
263 //DEBUGP(DGPRS, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_l1len(msg));
264
265 rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
266 (const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
267 if (rc < 0) {
268 LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
269 strerror(errno));
270 return rc;
271 } else if (rc < (int)msgb_l1len(msg)) {
272 LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
273 "%u < %u\n", rc, msgb_l1len(msg));
274 return -EIO;
275 }
276
277 return 0;
278}
279
280int pcu_l1if_open()
281{
282 //struct l1fwd_hdl *l1fh;
283 struct femtol1_hdl *fl1h;
284 int rc;
285
286 memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
287
288 /* allocate new femtol1_handle */
289 fl1h = talloc_zero(NULL, struct femtol1_hdl);
290 INIT_LLIST_HEAD(&fl1h->wlc_list);
291
292 l1fh->fl1h = fl1h;
293 fl1h->priv = l1fh;
294
295 struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
296 osmo_wqueue_init(queue, 10);
297 queue->bfd.when |= BSC_FD_READ;
298 queue->bfd.data = l1fh;
299 queue->bfd.priv_nr = 0;
300
301 /* Open UDP */
302 struct osmo_wqueue *wq = &l1fh->udp_wq;
303
304 osmo_wqueue_init(wq, 10);
305 wq->write_cb = udp_write_cb;
306 wq->read_cb = udp_read_cb;
307 wq->bfd.when |= BSC_FD_READ;
308 wq->bfd.data = l1fh;
309 wq->bfd.priv_nr = 0;
310 rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
311 IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
312 OSMO_SOCK_F_BIND);
313 if (rc < 0) {
314 perror("sock_init");
315 exit(1);
316 }
317
318 return 0;
319}
320