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