1 /* Layer 1 Power Measurement */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
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.
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.
27 #include <byteorder.h>
28 #include <osmocom/gsm/gsm_utils.h>
29 #include <osmocom/core/msgb.h>
30 #include <calypso/dsp_api.h>
31 #include <calypso/irq.h>
32 #include <calypso/tpu.h>
33 #include <calypso/tsp.h>
34 #include <calypso/dsp.h>
35 #include <calypso/timer.h>
36 #include <comm/sercomm.h>
37 #include <asm/system.h>
39 #include <layer1/sync.h>
40 #include <layer1/agc.h>
41 #include <layer1/tdma_sched.h>
42 #include <layer1/tpu_window.h>
43 #include <layer1/l23_api.h>
44 #include <layer1/prim.h>
47 #include <l1ctl_proto.h>
49 static void l1ddsp_meas_read(uint8_t nbmeas
, uint16_t *pm
)
53 for (i
= 0; i
< nbmeas
; i
++)
54 pm
[i
] = (uint16_t) ((dsp_api
.db_r
->a_pm
[i
] & 0xffff) >> 3);
55 dsp_api
.r_page_used
= 1;
58 /* scheduler callback to issue a power measurement task to the DSP */
59 static int l1s_pm_cmd(uint8_t num_meas
,
60 __unused
uint8_t p2
, uint16_t arfcn
)
64 dsp_api
.db_w
->d_task_md
= num_meas
; /* number of measurements */
65 dsp_api
.ndb
->d_fb_mode
= 0; /* wideband search */
67 /* Tell the RF frontend to set the gain appropriately */
68 rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL
);
71 /* FIXME: RXWIN_PW needs to set up multiple times in case
73 l1s_rx_win_ctrl(arfcn
, L1_RXWIN_PW
, 0);
74 //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB);
79 /* scheduler callback to read power measurement resposnse from the DSP */
80 static int l1s_pm_resp(uint8_t num_meas
, __unused
uint8_t p2
,
83 struct l1ctl_pm_conf
*pmr
;
88 l1ddsp_meas_read(num_meas
, pm_level
);
90 printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n",
91 arfcn
, pm_level
[0]/8, agc_inp_dbm8_by_pm(pm_level
[0])/8);
93 printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n",
94 agc_inp_dbm8_by_pm(pm_level
[0])/8,
95 agc_inp_dbm8_by_pm(pm_level
[1])/8, arfcn
);
98 l1s
.pm
.msg
= l1ctl_msgb_alloc(L1CTL_PM_CONF
);
100 if (msgb_tailroom(l1s
.pm
.msg
) < sizeof(*pmr
)) {
101 /* flush current msgb */
102 l1_queue_for_l2(l1s
.pm
.msg
);
103 /* allocate a new msgb and initialize header */
104 l1s
.pm
.msg
= l1ctl_msgb_alloc(L1CTL_PM_CONF
);
107 pmr
= (struct l1ctl_pm_conf
*) msgb_put(l1s
.pm
.msg
, sizeof(*pmr
));
108 pmr
->band_arfcn
= htons(arfcn
);
109 /* FIXME: do this as RxLev rather than DBM8 ? */
110 pmr
->pm
[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level
[0])/8);
112 pmr
->pm
[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level
[1])/8);
116 if (l1s
.pm
.mode
== 1) {
117 if (l1s
.pm
.range
.arfcn_next
!= l1s
.pm
.range
.arfcn_end
) {
118 /* schedule PM for next ARFCN in range */
119 l1s
.pm
.range
.arfcn_next
=
120 (l1s
.pm
.range
.arfcn_next
+1) & 0xfbff;
121 l1s_pm_test(1, l1s
.pm
.range
.arfcn_next
);
123 /* we have finished, flush the msgb to L2 */
124 struct l1ctl_hdr
*l1h
;
125 l1h
= (struct l1ctl_hdr
*) l1s
.pm
.msg
->l1h
;
126 l1h
->flags
|= L1CTL_F_DONE
;
127 l1_queue_for_l2(l1s
.pm
.msg
);
135 static const struct tdma_sched_item pm_sched_set
[] = {
136 SCHED_ITEM_DT(l1s_pm_cmd
, 0, 1, 0), SCHED_END_FRAME(),
138 SCHED_ITEM(l1s_pm_resp
, -4, 1, 0), SCHED_END_FRAME(),
142 /* Schedule a power measurement test */
143 void l1s_pm_test(uint8_t base_fn
, uint16_t arfcn
)
147 printd("l1s_pm_test(%u, %u)\n", base_fn
, arfcn
);
149 local_firq_save(flags
);
150 tdma_schedule_set(base_fn
, pm_sched_set
, arfcn
);
151 local_irq_restore(flags
);
155 * perform measurements of neighbour cells
158 /* scheduler callback to issue a power measurement task to the DSP */
159 static int l1s_neigh_pm_cmd(uint8_t num_meas
,
160 __unused
uint8_t p2
, __unused
uint16_t p3
)
162 uint8_t last_gain
= rffe_get_gain();
164 dsp_api
.db_w
->d_task_md
= num_meas
; /* number of measurements */
165 // dsp_api.ndb->d_fb_mode = 0; /* wideband search */
167 /* Tell the RF frontend to set the gain appropriately (keep last) */
168 rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL
);
171 /* FIXME: RXWIN_PW needs to set up multiple times in case
173 /* do measurement dummy, in case l1s.neigh_pm.n == 0 */
174 l1s_rx_win_ctrl((l1s
.neigh_pm
.n
) ?
175 l1s
.neigh_pm
.band_arfcn
[l1s
.neigh_pm
.pos
] : 0,
176 L1_RXWIN_PW
, l1s
.neigh_pm
.tn
[l1s
.neigh_pm
.pos
]);
178 /* restore last gain */
179 rffe_set_gain(last_gain
);
181 l1s
.neigh_pm
.running
= 1;
186 /* scheduler callback to read power measurement resposnse from the DSP */
187 static int l1s_neigh_pm_resp(__unused
uint8_t p1
, __unused
uint8_t p2
,
188 __unused
uint16_t p3
)
193 dsp_api
.r_page_used
= 1;
195 if (l1s
.neigh_pm
.n
== 0 || !l1s
.neigh_pm
.running
)
198 dbm
= (uint16_t) ((dsp_api
.db_r
->a_pm
[0] & 0xffff) >> 3);
199 level
= dbm2rxlev(agc_inp_dbm8_by_pm(dbm
)/8);
201 l1s
.neigh_pm
.level
[l1s
.neigh_pm
.pos
] = level
;
203 if (++l1s
.neigh_pm
.pos
>= l1s
.neigh_pm
.n
) {
205 struct l1ctl_neigh_pm_ind
*mi
;
208 l1s
.neigh_pm
.pos
= 0;
210 msg
= l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND
);
211 for (i
= 0; i
< l1s
.neigh_pm
.n
; i
++) {
212 if (msgb_tailroom(msg
) < (int) sizeof(*mi
)) {
213 l1_queue_for_l2(msg
);
214 msg
= l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND
);
216 mi
= (struct l1ctl_neigh_pm_ind
*)
217 msgb_put(msg
, sizeof(*mi
));
218 mi
->band_arfcn
= htons(l1s
.neigh_pm
.band_arfcn
[i
]);
219 mi
->tn
= l1s
.neigh_pm
.tn
[i
];
220 mi
->pm
[0] = l1s
.neigh_pm
.level
[i
];
223 l1_queue_for_l2(msg
);
227 l1s
.neigh_pm
.running
= 0;
232 const struct tdma_sched_item neigh_pm_sched_set
[] = {
233 SCHED_ITEM_DT(l1s_neigh_pm_cmd
, 0, 1, 0), SCHED_END_FRAME(),
235 SCHED_ITEM(l1s_neigh_pm_resp
, -4, 1, 0), SCHED_END_FRAME(),