blob: f2ed1718d18048dc78d4e7a51787c0fd69f0169d [file] [log] [blame]
Michael Krufky2e5c1ec82008-05-19 18:56:13 -03001#include <linux/module.h>
2#include <linux/init.h>
3
4#include "dmxdev.h"
5#include "dvbdev.h"
6#include "dvb_demux.h"
7#include "dvb_frontend.h"
8
9#include "smskdefs.h" // page, scatterlist, kmutex
10#include "smscoreapi.h"
11#include "smstypes.h"
12
13typedef struct _smsdvb_client
14{
15 struct list_head entry;
16
17 smscore_device_t *coredev;
18 smscore_client_t *smsclient;
19
20 struct dvb_adapter adapter;
21 struct dvb_demux demux;
22 struct dmxdev dmxdev;
23 struct dvb_frontend frontend;
24
25 fe_status_t fe_status;
26 int fe_ber, fe_snr, fe_signal_strength;
27
28 struct completion tune_done, stat_done;
29
30 // todo: save freq/band instead whole struct
31 struct dvb_frontend_parameters fe_params;
32
33} smsdvb_client_t;
34
35struct list_head g_smsdvb_clients;
36kmutex_t g_smsdvb_clientslock;
37
38int smsdvb_onresponse(void *context, smscore_buffer_t *cb)
39{
40 smsdvb_client_t *client = (smsdvb_client_t *) context;
41 SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)(((u8*) cb->p) + cb->offset);
42
43 switch(phdr->msgType)
44 {
45 case MSG_SMS_DVBT_BDA_DATA:
46 dvb_dmx_swfilter(&client->demux, (u8*)(phdr + 1), cb->size - sizeof(SmsMsgHdr_ST));
47 break;
48
49 case MSG_SMS_RF_TUNE_RES:
50 complete(&client->tune_done);
51 break;
52
53 case MSG_SMS_GET_STATISTICS_RES:
54 {
55 SmsMsgStatisticsInfo_ST* p = (SmsMsgStatisticsInfo_ST*)(phdr + 1);
56
57 if (p->Stat.IsDemodLocked)
58 {
59 client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
60 client->fe_snr = p->Stat.SNR;
61 client->fe_ber = p->Stat.BER;
62
63 if (p->Stat.InBandPwr < -95)
64 client->fe_signal_strength = 0;
65 else if (p->Stat.InBandPwr > -29)
66 client->fe_signal_strength = 100;
67 else
68 client->fe_signal_strength = (p->Stat.InBandPwr + 95) * 3 / 2;
69 }
70 else
71 {
72 client->fe_status = 0;
73 client->fe_snr =
74 client->fe_ber =
75 client->fe_signal_strength = 0;
76 }
77
78 complete(&client->stat_done);
79 break;
80 }
81 }
82
83 smscore_putbuffer(client->coredev, cb);
84
85 return 0;
86}
87
88void smsdvb_unregister_client(smsdvb_client_t* client)
89{
90 // must be called under clientslock
91
92 list_del(&client->entry);
93
94 smscore_unregister_client(client->smsclient);
95 dvb_unregister_frontend(&client->frontend);
96 dvb_dmxdev_release(&client->dmxdev);
97 dvb_dmx_release(&client->demux);
98 dvb_unregister_adapter(&client->adapter);
99 kfree(client);
100}
101
102void smsdvb_onremove(void *context)
103{
104 kmutex_lock(&g_smsdvb_clientslock);
105
106 smsdvb_unregister_client((smsdvb_client_t*) context);
107
108 kmutex_unlock(&g_smsdvb_clientslock);
109}
110
111static int smsdvb_start_feed(struct dvb_demux_feed *feed)
112{
113 smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux);
114 SmsMsgData_ST PidMsg;
115
116 printk("%s add pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid);
117
118 PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
119 PidMsg.xMsgHeader.msgDstId = HIF_TASK;
120 PidMsg.xMsgHeader.msgFlags = 0;
121 PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ;
122 PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
123 PidMsg.msgData[0] = feed->pid;
124
125 return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg));
126}
127
128static int smsdvb_stop_feed(struct dvb_demux_feed *feed)
129{
130 smsdvb_client_t *client = container_of(feed->demux, smsdvb_client_t, demux);
131 SmsMsgData_ST PidMsg;
132
133 printk("%s remove pid %d(%x)\n", __FUNCTION__, feed->pid, feed->pid);
134
135 PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
136 PidMsg.xMsgHeader.msgDstId = HIF_TASK;
137 PidMsg.xMsgHeader.msgFlags = 0;
138 PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ;
139 PidMsg.xMsgHeader.msgLength = sizeof(PidMsg);
140 PidMsg.msgData[0] = feed->pid;
141
142 return smsclient_sendrequest(client->smsclient, &PidMsg, sizeof(PidMsg));
143}
144
145static int smsdvb_sendrequest_and_wait(smsdvb_client_t *client, void* buffer, size_t size, struct completion *completion)
146{
147 int rc = smsclient_sendrequest(client->smsclient, buffer, size);
148 if (rc < 0)
149 return rc;
150
151 return wait_for_completion_timeout(completion, msecs_to_jiffies(2000)) ? 0 : -ETIME;
152}
153
154static int smsdvb_send_statistics_request(smsdvb_client_t *client)
155{
156 SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(SmsMsgHdr_ST), 0 };
157 return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->stat_done);
158}
159
160static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
161{
162 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
163 int rc = smsdvb_send_statistics_request(client);
164
165 if (!rc)
166 *stat = client->fe_status;
167
168 return rc;
169}
170
171static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
172{
173 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
174 int rc = smsdvb_send_statistics_request(client);
175
176 if (!rc)
177 *ber = client->fe_ber;
178
179 return rc;
180}
181
182static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
183{
184 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
185 int rc = smsdvb_send_statistics_request(client);
186
187 if (!rc)
188 *strength = client->fe_signal_strength;
189
190 return rc;
191}
192
193static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
194{
195 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
196 int rc = smsdvb_send_statistics_request(client);
197
198 if (!rc)
199 *snr = client->fe_snr;
200
201 return rc;
202}
203
204static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune)
205{
206 printk("%s\n", __FUNCTION__);
207
208 tune->min_delay_ms = 400;
209 tune->step_size = 250000;
210 tune->max_drift = 0;
211 return 0;
212}
213
214static int smsdvb_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
215{
216 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
217
218 struct
219 {
220 SmsMsgHdr_ST Msg;
221 u32 Data[3];
222 } Msg;
223
224 Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
225 Msg.Msg.msgDstId = HIF_TASK;
226 Msg.Msg.msgFlags = 0;
227 Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ;
228 Msg.Msg.msgLength = sizeof(Msg);
229 Msg.Data[0] = fep->frequency;
230 Msg.Data[2] = 12000000;
231
232 printk("%s freq %d band %d\n", __FUNCTION__, fep->frequency, fep->u.ofdm.bandwidth);
233
234 switch(fep->u.ofdm.bandwidth)
235 {
236 case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break;
237 case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break;
238 case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break;
239// case BANDWIDTH_5_MHZ: Msg.Data[1] = BW_5_MHZ; break;
240 case BANDWIDTH_AUTO: return -EOPNOTSUPP;
241 default: return -EINVAL;
242 }
243
244 return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), &client->tune_done);
245}
246
247static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
248{
249 smsdvb_client_t *client = container_of(fe, smsdvb_client_t, frontend);
250
251 printk("%s\n", __FUNCTION__);
252
253 // todo:
254 memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters));
255 return 0;
256}
257
258static void smsdvb_release(struct dvb_frontend *fe)
259{
260 // do nothing
261}
262
263static struct dvb_frontend_ops smsdvb_fe_ops = {
264 .info = {
265 .name = "Siano Mobile Digital SMS10xx",
266 .type = FE_OFDM,
267 .frequency_min = 44250000,
268 .frequency_max = 867250000,
269 .frequency_stepsize = 250000,
270 .caps = FE_CAN_INVERSION_AUTO |
271 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
272 FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
273 FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
274 FE_CAN_TRANSMISSION_MODE_AUTO |
275 FE_CAN_GUARD_INTERVAL_AUTO |
276 FE_CAN_RECOVER |
277 FE_CAN_HIERARCHY_AUTO,
278 },
279
280 .release = smsdvb_release,
281
282 .set_frontend = smsdvb_set_frontend,
283 .get_frontend = smsdvb_get_frontend,
284 .get_tune_settings = smsdvb_get_tune_settings,
285
286 .read_status = smsdvb_read_status,
287 .read_ber = smsdvb_read_ber,
288 .read_signal_strength = smsdvb_read_signal_strength,
289 .read_snr = smsdvb_read_snr,
290};
291
292int smsdvb_hotplug(smscore_device_t *coredev, struct device* device, int arrival)
293{
294 smsclient_params_t params;
295 smsdvb_client_t* client;
296 int rc;
297
298 // device removal handled by onremove callback
299 if (!arrival)
300 return 0;
301
302 if (smscore_get_device_mode(coredev) != 4)
303 {
304 rc = smscore_set_device_mode(coredev, 4);
305 if (rc < 0)
306 return rc;
307 }
308
309 client = kzalloc(sizeof(smsdvb_client_t), GFP_KERNEL);
310 if (!client)
311 {
312 printk(KERN_INFO "%s kmalloc() failed\n", __FUNCTION__);
313 return -ENOMEM;
314 }
315
316 // register dvb adapter
317 rc = dvb_register_adapter(&client->adapter, "Siano Digital Receiver", THIS_MODULE, device);
318 if (rc < 0)
319 {
320 printk("%s dvb_register_adapter() failed %d\n", __func__, rc);
321 goto adapter_error;
322 }
323
324 // init dvb demux
325 client->demux.dmx.capabilities = DMX_TS_FILTERING;
326 client->demux.filternum = 32; // todo: nova ???
327 client->demux.feednum = 32;
328 client->demux.start_feed = smsdvb_start_feed;
329 client->demux.stop_feed = smsdvb_stop_feed;
330
331 rc = dvb_dmx_init(&client->demux);
332 if (rc < 0)
333 {
334 printk("%s dvb_dmx_init failed %d\n\n", __FUNCTION__, rc);
335 goto dvbdmx_error;
336 }
337
338 // init dmxdev
339 client->dmxdev.filternum = 32;
340 client->dmxdev.demux = &client->demux.dmx;
341 client->dmxdev.capabilities = 0;
342
343 rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter);
344 if (rc < 0)
345 {
346 printk("%s dvb_dmxdev_init failed %d\n", __FUNCTION__, rc);
347 goto dmxdev_error;
348 }
349
350 // init and register frontend
351 memcpy(&client->frontend.ops, &smsdvb_fe_ops, sizeof(struct dvb_frontend_ops));
352
353 rc = dvb_register_frontend(&client->adapter, &client->frontend);
354 if (rc < 0)
355 {
356 printk("%s frontend registration failed %d\n", __FUNCTION__, rc);
357 goto frontend_error;
358 }
359
360 params.initial_id = 0;
361 params.data_type = MSG_SMS_DVBT_BDA_DATA;
362 params.onresponse_handler = smsdvb_onresponse;
363 params.onremove_handler = smsdvb_onremove;
364 params.context = client;
365
366 rc = smscore_register_client(coredev, &params, &client->smsclient);
367 if (rc < 0)
368 {
369 printk(KERN_INFO "%s smscore_register_client() failed %d\n", __FUNCTION__, rc);
370 goto client_error;
371 }
372
373 client->coredev = coredev;
374
375 init_completion(&client->tune_done);
376 init_completion(&client->stat_done);
377
378 kmutex_lock(&g_smsdvb_clientslock);
379
380 list_add(&client->entry, &g_smsdvb_clients);
381
382 kmutex_unlock(&g_smsdvb_clientslock);
383
384 printk(KERN_INFO "%s success\n", __FUNCTION__);
385
386 return 0;
387
388client_error:
389 dvb_unregister_frontend(&client->frontend);
390
391frontend_error:
392 dvb_dmxdev_release(&client->dmxdev);
393
394dmxdev_error:
395 dvb_dmx_release(&client->demux);
396
397dvbdmx_error:
398 dvb_unregister_adapter(&client->adapter);
399
400adapter_error:
401 kfree(client);
402 return rc;
403}
404
405int smsdvb_module_init(void)
406{
407 int rc;
408
409 INIT_LIST_HEAD(&g_smsdvb_clients);
410 kmutex_init(&g_smsdvb_clientslock);
411
412 rc = smscore_register_hotplug(smsdvb_hotplug);
413
414 printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
415
416 return rc;
417}
418
419void smsdvb_module_exit(void)
420{
421 smscore_unregister_hotplug(smsdvb_hotplug);
422
423 kmutex_lock(&g_smsdvb_clientslock);
424
425 while (!list_empty(&g_smsdvb_clients))
426 smsdvb_unregister_client((smsdvb_client_t*) g_smsdvb_clients.next);
427
428 kmutex_unlock(&g_smsdvb_clientslock);
429
430 printk(KERN_INFO "%s\n", __FUNCTION__);
431}
432
433module_init(smsdvb_module_init);
434module_exit(smsdvb_module_exit);
435
436MODULE_DESCRIPTION("smsdvb dvb-api module");
437MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
438MODULE_LICENSE("GPL");