aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Krufky <mkrufky@linuxtv.org>2008-05-19 17:56:13 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-07-20 06:19:35 -0400
commit2e5c1ec8865abd81e24a394918c7ba315e0b7b70 (patch)
treecd241de9b5eeddc27455b07e3530f69bc3e7d95d
parenta9e285856112e5e721b6a341d15437a164128b30 (diff)
V4L/DVB (8258): add support for SMS1010 and SMS1150 based digital television devices
initial driver drop, provided by Siano Mobile Silicon, Inc. Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--drivers/media/Kconfig14
-rw-r--r--drivers/media/Makefile1
-rw-r--r--drivers/media/mdtv/Kconfig36
-rw-r--r--drivers/media/mdtv/Makefile8
-rw-r--r--drivers/media/mdtv/smschar.c575
-rw-r--r--drivers/media/mdtv/smschar.h7
-rw-r--r--drivers/media/mdtv/smscharioctl.h17
-rw-r--r--drivers/media/mdtv/smscoreapi.c1170
-rw-r--r--drivers/media/mdtv/smscoreapi.h101
-rw-r--r--drivers/media/mdtv/smsdvb.c438
-rw-r--r--drivers/media/mdtv/smskdefs.h21
-rw-r--r--drivers/media/mdtv/smsnet.c447
-rw-r--r--drivers/media/mdtv/smstypes.h361
-rw-r--r--drivers/media/mdtv/smsusb.c428
14 files changed, 3624 insertions, 0 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 7a7803b5d497..e6a5879ea5f6 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -115,6 +115,20 @@ source "drivers/media/radio/Kconfig"
115 115
116source "drivers/media/dvb/Kconfig" 116source "drivers/media/dvb/Kconfig"
117 117
118#
119# Mobile Digital TV devices (DVB-H, T-DMB, etc.)
120#
121menuconfig MDTV_ADAPTERS
122 bool "Mobile Digital TV adapter"
123 default y
124
125if MDTV_ADAPTERS
126
127source "drivers/media/mdtv/Kconfig"
128
129endif # MDTV_ADAPTERS
130
131
118config DAB 132config DAB
119 boolean "DAB adapters" 133 boolean "DAB adapters"
120 ---help--- 134 ---help---
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 09a829d8a7e7..ec2102bcb282 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -6,3 +6,4 @@ obj-y += common/ video/
6 6
7obj-$(CONFIG_VIDEO_DEV) += radio/ 7obj-$(CONFIG_VIDEO_DEV) += radio/
8obj-$(CONFIG_DVB_CORE) += dvb/ 8obj-$(CONFIG_DVB_CORE) += dvb/
9obj-$(CONFIG_MDTV_ADAPTERS) += mdtv/ \ No newline at end of file
diff --git a/drivers/media/mdtv/Kconfig b/drivers/media/mdtv/Kconfig
new file mode 100644
index 000000000000..f3bae45000d1
--- /dev/null
+++ b/drivers/media/mdtv/Kconfig
@@ -0,0 +1,36 @@
1#
2# Mobile Digital TV device configuration
3#
4
5config MDTV_SIANO_STELLAR_COMMON
6 tristate "Siano SMS10xx adapter"
7 default m
8 ---help---
9 Choose Y here if you have SMS10xx chipset.
10
11 In order to control the SMS10xx chipset you will need SMS Host Control library.
12
13 Further documentation on this driver can be found on the WWW at
14 <http://www.siano-ms.com/>.
15
16 To compile this driver as a module, choose M here: the
17 modules will be called smschar and smsnet.
18
19config MDTV_SIANO_STELLAR_USB
20 tristate "Siano SMS10xx USB dongle support"
21 depends on MDTV_SIANO_STELLAR_COMMON
22 default m
23 ---help---
24 Choose Y here if you have USB dongle with SMS10xx chipset.
25
26 In order to control the SMS10xx chipset you will need SMS Host Control library.
27
28 Further documentation on this driver can be found on the WWW at
29 <http://www.siano-ms.com/>.
30
31 To compile this driver as a module, choose M here: the
32 module will be called smsusb.
33
34
35
36
diff --git a/drivers/media/mdtv/Makefile b/drivers/media/mdtv/Makefile
new file mode 100644
index 000000000000..1e54d8f796dc
--- /dev/null
+++ b/drivers/media/mdtv/Makefile
@@ -0,0 +1,8 @@
1#
2# Makefile for the kernel MDTV driver
3#
4
5obj-$(CONFIG_MDTV_SIANO_STELLAR_COMMON) += smschar.o smsnet.o
6obj-$(CONFIG_MDTV_SIANO_STELLAR_USB) += smsusb.o
7
8EXTRA_CFLAGS +=
diff --git a/drivers/media/mdtv/smschar.c b/drivers/media/mdtv/smschar.c
new file mode 100644
index 000000000000..0477ad0ed6d6
--- /dev/null
+++ b/drivers/media/mdtv/smschar.c
@@ -0,0 +1,575 @@
1/*!
2
3 \file smschar.c
4
5 \brief Implementation of smscore client for cdev based access
6
7 \par Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
8
9 \par This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License version 3 as
11 published by the Free Software Foundation;
12
13 Software distributed under the License is distributed on an "AS
14 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
15 implied.
16
17 \author Anatoly Greenblat
18
19*/
20
21#include <linux/module.h>
22#include <linux/moduleparam.h>
23#include <linux/init.h>
24
25#include <linux/kernel.h> /* printk() */
26#include <linux/fs.h> /* everything... */
27#include <linux/types.h> /* size_t */
28#include <linux/cdev.h>
29#include <linux/sched.h>
30#include <asm/system.h> /* cli(), *_flags */
31#include <asm/uaccess.h> /* copy_*_user */
32
33#include "smskdefs.h" // page, scatterlist, kmutex
34#include "smscoreapi.h"
35#include "smstypes.h"
36
37#include "smscharioctl.h"
38
39#define SMS_CHR_MAX_Q_LEN 10 // max number of packets allowed to be pending on queue
40#define SMSCHAR_NR_DEVS 7
41
42typedef struct _smschar_device
43{
44 struct cdev cdev; //!< Char device structure - kernel's device model representation
45
46 wait_queue_head_t waitq; /* Processes waiting */
47 spinlock_t lock; //!< critical section
48 int pending_count;
49 struct list_head pending_data; //!< list of pending data
50
51 smscore_buffer_t *currentcb;
52
53 int device_index;
54
55 smscore_device_t *coredev;
56 smscore_client_t *smsclient;
57} smschar_device_t;
58
59//! Holds the major number of the device node. may be changed at load time.
60int smschar_major = 251;
61
62//! Holds the first minor number of the device node. may be changed at load time.
63int smschar_minor = 0;
64
65// macros that allow the load time parameters change
66module_param ( smschar_major, int, S_IRUGO );
67module_param ( smschar_minor, int, S_IRUGO );
68
69#ifdef SMSCHAR_DEBUG
70
71 #undef PERROR
72# define PERROR(fmt, args...) printk( KERN_INFO "smschar error: line %d- %s(): " fmt,__LINE__, __FUNCTION__, ## args)
73 #undef PWARNING
74# define PWARNING(fmt, args...) printk( KERN_INFO "smschar warning: line %d- %s(): " fmt,__LINE__, __FUNCTION__, ## args)
75 #undef PDEBUG /* undef it, just in case */
76# define PDEBUG(fmt, args...) printk( KERN_INFO "smschar - %s(): " fmt, __FUNCTION__, ## args)
77
78#else /* not debugging: nothing */
79
80 #define PDEBUG(fmt, args...)
81 #define PERROR(fmt, args...)
82 #define PWARNING(fmt, args...)
83
84#endif
85
86smschar_device_t smschar_devices[SMSCHAR_NR_DEVS];
87static int g_smschar_inuse = 0;
88
89/**
90 * unregisters sms client and returns all queued buffers
91 *
92 * @param dev pointer to the client context (smschar parameters block)
93 *
94 */
95void smschar_unregister_client(smschar_device_t* dev)
96{
97 unsigned long flags;
98
99 if (dev->coredev && dev->smsclient)
100 {
101 wake_up_interruptible(&dev->waitq);
102
103 spin_lock_irqsave(&dev->lock, flags);
104
105 while (!list_empty(&dev->pending_data))
106 {
107 smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
108 list_del(&cb->entry);
109
110 smscore_putbuffer(dev->coredev, cb);
111
112 dev->pending_count --;
113 }
114
115 if (dev->currentcb)
116 {
117 smscore_putbuffer(dev->coredev, dev->currentcb);
118 dev->currentcb = NULL;
119 dev->pending_count --;
120 }
121
122 smscore_unregister_client(dev->smsclient);
123 dev->smsclient = NULL;
124
125 spin_unlock_irqrestore(&dev->lock, flags);
126 }
127}
128
129/**
130 * queues incoming buffers into buffers queue
131 *
132 * @param context pointer to the client context (smschar parameters block)
133 * @param cb pointer to incoming buffer descriptor
134 *
135 * @return 0 on success, <0 on queue overflow.
136 */
137int smschar_onresponse(void *context, smscore_buffer_t *cb)
138{
139 smschar_device_t *dev = context;
140 unsigned long flags;
141
142 spin_lock_irqsave(&dev->lock, flags);
143
144 if (dev->pending_count > SMS_CHR_MAX_Q_LEN)
145 {
146 spin_unlock_irqrestore(&dev->lock, flags);
147 return -EBUSY;
148 }
149
150 dev->pending_count ++;
151
152 // if data channel, remove header
153 if (dev->device_index)
154 {
155 cb->size -= sizeof(SmsMsgHdr_ST);
156 cb->offset += sizeof(SmsMsgHdr_ST);
157 }
158
159 list_add_tail(&cb->entry, &dev->pending_data);
160 spin_unlock_irqrestore(&dev->lock, flags);
161
162 if (waitqueue_active(&dev->waitq))
163 wake_up_interruptible(&dev->waitq);
164
165 return 0;
166}
167
168/**
169 * handles device removal event
170 *
171 * @param context pointer to the client context (smschar parameters block)
172 *
173 */
174void smschar_onremove(void *context)
175{
176 smschar_device_t *dev = (smschar_device_t *) context;
177
178 smschar_unregister_client(dev);
179 dev->coredev = NULL;
180}
181
182/**
183 * registers client associated with the node
184 *
185 * @param inode Inode concerned.
186 * @param file File concerned.
187 *
188 * @return 0 on success, <0 on error.
189 */
190int smschar_open (struct inode *inode, struct file *file)
191{
192 smschar_device_t *dev = container_of(inode->i_cdev, smschar_device_t, cdev);
193 int rc = -ENODEV;
194
195 PDEBUG("entering index %d\n", dev->device_index);
196
197 if (dev->coredev)
198 {
199 smsclient_params_t params;
200
201 params.initial_id = dev->device_index ? dev->device_index : SMS_HOST_LIB;
202 params.data_type = dev->device_index ? MSG_SMS_DAB_CHANNEL : 0;
203 params.onresponse_handler = smschar_onresponse;
204 params.onremove_handler = smschar_onremove;
205 params.context = dev;
206
207 rc = smscore_register_client(dev->coredev, &params, &dev->smsclient);
208 if (!rc)
209 {
210 file->private_data = dev;
211 }
212 }
213
214 PDEBUG("exiting, rc %d\n", rc);
215
216 return rc;
217}
218
219/**
220 * unregisters client associated with the node
221 *
222 * @param inode Inode concerned.
223 * @param file File concerned.
224 *
225 */
226int smschar_release(struct inode *inode, struct file *file)
227{
228 smschar_unregister_client(file->private_data);
229
230 PDEBUG("exiting\n");
231
232 return 0;
233}
234
235/**
236 * copies data from buffers in incoming queue into a user buffer
237 *
238 * @param file File structure.
239 * @param buf Source buffer.
240 * @param count Size of source buffer.
241 * @param f_pos Position in file (ignored).
242 *
243 * @return Number of bytes read, or <0 on error.
244 */
245ssize_t smschar_read ( struct file * file, char __user * buf, size_t count, loff_t * f_pos )
246{
247 smschar_device_t *dev = file->private_data;
248 unsigned long flags;
249 int copied = 0;
250
251 if (!dev->coredev || !dev->smsclient)
252 {
253 PERROR("no client\n");
254 return -ENODEV;
255 }
256
257 while (copied != count)
258 {
259 if (0 > wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data)))
260 {
261 PERROR("wait_event_interruptible error\n");
262 return -ENODEV;
263 }
264
265 if (!dev->smsclient)
266 {
267 PERROR("no client\n");
268 return -ENODEV;
269 }
270
271 spin_lock_irqsave(&dev->lock, flags);
272
273 while (!list_empty(&dev->pending_data) && (copied != count))
274 {
275 smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
276 int actual_size = min(((int) count - copied), cb->size);
277
278 copy_to_user(&buf[copied], &((char*)cb->p)[cb->offset], actual_size);
279
280 copied += actual_size;
281 cb->offset += actual_size;
282 cb->size -= actual_size;
283
284 if (!cb->size)
285 {
286 list_del(&cb->entry);
287 smscore_putbuffer(dev->coredev, cb);
288
289 dev->pending_count --;
290 }
291 }
292
293 spin_unlock_irqrestore(&dev->lock, flags);
294 }
295
296 return copied;
297}
298
299/**
300 * sends the buffer to the associated device
301 *
302 * @param file File structure.
303 * @param buf Source buffer.
304 * @param count Size of source buffer.
305 * @param f_pos Position in file (ignored).
306 *
307 * @return Number of bytes read, or <0 on error.
308 */
309ssize_t smschar_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
310{
311 smschar_device_t *dev = file->private_data;
312 void *buffer;
313
314 if (!dev->smsclient)
315 {
316 PERROR("no client\n");
317 return -ENODEV;
318 }
319
320 buffer = kmalloc(ALIGN(count, SMS_ALLOC_ALIGNMENT) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
321 if (buffer)
322 {
323 void *msg_buffer = (void*) SMS_ALIGN_ADDRESS(buffer);
324
325 if (!copy_from_user(msg_buffer, buf, count))
326 smsclient_sendrequest(dev->smsclient, msg_buffer, count);
327 else
328 count = 0;
329
330 kfree(buffer);
331 }
332
333 return count;
334}
335
336int smschar_mmap(struct file *file, struct vm_area_struct *vma)
337{
338 smschar_device_t *dev = file->private_data;
339 return smscore_map_common_buffer(dev->coredev, vma);
340}
341
342/**
343 * waits until buffer inserted into a queue. when inserted buffer offset are reported
344 * to the calling process. previously reported buffer is returned to smscore pool
345 *
346 * @param dev pointer to smschar parameters block
347 * @param touser pointer to a structure that receives incoming buffer offsets
348 *
349 * @return 0 on success, <0 on error.
350 */
351int smschar_wait_get_buffer(smschar_device_t* dev, smschar_buffer_t* touser)
352{
353 unsigned long flags;
354 int rc;
355
356 spin_lock_irqsave(&dev->lock, flags);
357
358 if (dev->currentcb)
359 {
360 smscore_putbuffer(dev->coredev, dev->currentcb);
361 dev->currentcb = NULL;
362 dev->pending_count --;
363 }
364
365 spin_unlock_irqrestore(&dev->lock, flags);
366
367 rc = wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data));
368 if (rc < 0)
369 {
370 PERROR("wait_event_interruptible error\n");
371 return rc;
372 }
373
374 if (!dev->smsclient)
375 {
376 PERROR("no client\n");
377 return -ENODEV;
378 }
379
380 spin_lock_irqsave(&dev->lock, flags);
381
382 if (!list_empty(&dev->pending_data))
383 {
384 smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
385
386 touser->offset = cb->offset_in_common + cb->offset;
387 touser->size = cb->size;
388
389 list_del(&cb->entry);
390
391 dev->currentcb = cb;
392 }
393 else
394 {
395 touser->offset = 0;
396 touser->size = 0;
397 }
398
399 spin_unlock_irqrestore(&dev->lock, flags);
400
401 return 0;
402}
403
404int smschar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
405{
406 smschar_device_t *dev = file->private_data;
407 void __user *up = (void __user *) arg;
408
409 if (!dev->coredev || !dev->smsclient)
410 {
411 PERROR("no client\n");
412 return -ENODEV;
413 }
414
415 switch(cmd)
416 {
417 case SMSCHAR_SET_DEVICE_MODE:
418 return smscore_set_device_mode(dev->coredev, (int) arg);
419
420 case SMSCHAR_GET_DEVICE_MODE:
421 {
422 if (put_user(smscore_get_device_mode(dev->coredev), (int*) up))
423 return -EFAULT;
424
425 break;
426 }
427
428 case SMSCHAR_GET_BUFFER_SIZE:
429 {
430 if (put_user(smscore_get_common_buffer_size(dev->coredev), (int*) up))
431 return -EFAULT;
432
433 break;
434 }
435
436 case SMSCHAR_WAIT_GET_BUFFER:
437 {
438 smschar_buffer_t touser;
439 int rc;
440
441 rc = smschar_wait_get_buffer(dev, &touser);
442 if (rc < 0)
443 return rc;
444
445 if (copy_to_user(up, &touser, sizeof(smschar_buffer_t)))
446 return -EFAULT;
447
448 break;
449 }
450
451 default:
452 return -ENOIOCTLCMD;
453 }
454
455 return 0;
456}
457
458struct file_operations smschar_fops =
459{
460 .owner = THIS_MODULE,
461 .read = smschar_read,
462 .write = smschar_write,
463 .open = smschar_open,
464 .release = smschar_release,
465 .mmap = smschar_mmap,
466 .ioctl = smschar_ioctl,
467};
468
469static int smschar_setup_cdev ( smschar_device_t *dev, int index )
470{
471 int rc, devno = MKDEV ( smschar_major, smschar_minor + index );
472
473 cdev_init ( &dev->cdev, &smschar_fops );
474
475 dev->cdev.owner = THIS_MODULE;
476 dev->cdev.ops = &smschar_fops;
477
478 kobject_set_name(&dev->cdev.kobj, "Siano_sms%d", index);
479
480 rc = cdev_add ( &dev->cdev, devno, 1 );
481
482 PDEBUG("exiting %p %d, rc %d\n", dev, index, rc);
483
484 return rc;
485}
486
487/**
488 * smschar callback that called when device plugged in/out. the function
489 * register or unregisters char device interface according to plug in/out
490 *
491 * @param coredev pointer to device that is being plugged in/out
492 * @param device pointer to system device object
493 * @param arrival 1 on plug-on, 0 othewise
494 *
495 * @return 0 on success, <0 on error.
496 */
497int smschar_hotplug(smscore_device_t* coredev, struct device* device, int arrival)
498{
499 int rc = 0, i;
500
501 PDEBUG("entering %d\n", arrival);
502
503 if (arrival)
504 {
505 // currently only 1 instance supported
506 if (!g_smschar_inuse)
507 {
508 /* data notification callbacks assignment */
509 memset ( smschar_devices, 0, SMSCHAR_NR_DEVS * sizeof ( smschar_device_t ) );
510
511 /* Initialize each device. */
512 for (i = 0; i < SMSCHAR_NR_DEVS; i++)
513 {
514 smschar_setup_cdev ( &smschar_devices[i], i );
515
516 INIT_LIST_HEAD(&smschar_devices[i].pending_data);
517 spin_lock_init(&smschar_devices[i].lock);
518 init_waitqueue_head(&smschar_devices[i].waitq);
519
520 smschar_devices[i].coredev = coredev;
521 smschar_devices[i].device_index = i;
522 }
523
524 g_smschar_inuse = 1;
525 }
526 }
527 else
528 {
529 // currently only 1 instance supported
530 if (g_smschar_inuse)
531 {
532 /* Get rid of our char dev entries */
533 for(i = 0; i < SMSCHAR_NR_DEVS; i++)
534 cdev_del(&smschar_devices[i].cdev);
535
536 g_smschar_inuse = 0;
537 }
538 }
539
540 PDEBUG("exiting, rc %d\n", rc);
541
542 return rc; /* succeed */
543}
544
545int smschar_initialize(void)
546{
547 dev_t devno = MKDEV ( smschar_major, smschar_minor );
548 int rc;
549
550 if(smschar_major)
551 {
552 rc = register_chrdev_region ( devno, SMSCHAR_NR_DEVS, "smschar" );
553 }
554 else
555 {
556 rc = alloc_chrdev_region ( &devno, smschar_minor, SMSCHAR_NR_DEVS, "smschar" );
557 smschar_major = MAJOR ( devno );
558 }
559
560 if (rc < 0)
561 {
562 PWARNING ( "smschar: can't get major %d\n", smschar_major );
563 return rc;
564 }
565
566 return smscore_register_hotplug(smschar_hotplug);
567}
568
569void smschar_terminate(void)
570{
571 dev_t devno = MKDEV ( smschar_major, smschar_minor );
572
573 unregister_chrdev_region(devno, SMSCHAR_NR_DEVS);
574 smscore_unregister_hotplug(smschar_hotplug);
575}
diff --git a/drivers/media/mdtv/smschar.h b/drivers/media/mdtv/smschar.h
new file mode 100644
index 000000000000..1cd2f32a7f71
--- /dev/null
+++ b/drivers/media/mdtv/smschar.h
@@ -0,0 +1,7 @@
1#ifndef __smschar_h__
2#define __smschar_h__
3
4extern int smschar_initialize(void);
5extern void smschar_terminate(void);
6
7#endif // __smschar_h__
diff --git a/drivers/media/mdtv/smscharioctl.h b/drivers/media/mdtv/smscharioctl.h
new file mode 100644
index 000000000000..e57b89efc499
--- /dev/null
+++ b/drivers/media/mdtv/smscharioctl.h
@@ -0,0 +1,17 @@
1#ifndef __smscharioctl_h__
2#define __smscharioctl_h__
3
4#include <linux/ioctl.h>
5
6typedef struct _smschar_buffer_t
7{
8 unsigned long offset; // offset in common buffer (mapped to user space)
9 int size;
10} smschar_buffer_t;
11
12#define SMSCHAR_SET_DEVICE_MODE _IOW('K', 0, int)
13#define SMSCHAR_GET_DEVICE_MODE _IOR('K', 1, int)
14#define SMSCHAR_GET_BUFFER_SIZE _IOR('K', 2, int)
15#define SMSCHAR_WAIT_GET_BUFFER _IOR('K', 3, smschar_buffer_t)
16
17#endif // __smscharioctl_h__
diff --git a/drivers/media/mdtv/smscoreapi.c b/drivers/media/mdtv/smscoreapi.c
new file mode 100644
index 000000000000..a354912391a0
--- /dev/null
+++ b/drivers/media/mdtv/smscoreapi.c
@@ -0,0 +1,1170 @@
1/*!
2
3 \file smscoreapi.c
4
5 \brief Siano core API module
6 This file contains implementation for the interface to sms core component
7
8 \par Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
9
10 \par This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License version 3 as
12 published by the Free Software Foundation;
13
14 Software distributed under the License is distributed on an "AS
15 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16 implied.
17
18 \author Anatoly Greenblat
19
20*/
21
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/moduleparam.h>
26#include <linux/dma-mapping.h>
27#include <linux/delay.h>
28#include <asm/io.h>
29
30#include "smskdefs.h" // device, page, scatterlist, kmutex
31
32#include <linux/firmware.h>
33
34#include "smscoreapi.h"
35#include "smstypes.h"
36
37#include "smschar.h"
38
39typedef struct _smscore_device_notifyee
40{
41 struct list_head entry;
42 hotplug_t hotplug;
43} smscore_device_notifyee_t;
44
45typedef struct _smscore_client
46{
47 struct list_head entry;
48 smscore_device_t *coredev;
49
50 void *context;
51
52 int data_type;
53
54 onresponse_t onresponse_handler;
55 onremove_t onremove_handler;
56} *psmscore_client_t;
57
58typedef struct _smscore_subclient
59{
60 struct list_head entry;
61 smscore_client_t *client;
62
63 int id;
64} smscore_subclient_t;
65
66typedef struct _smscore_device
67{
68 struct list_head entry;
69
70 struct list_head clients;
71 struct list_head subclients;
72 spinlock_t clientslock;
73
74 struct list_head buffers;
75 spinlock_t bufferslock;
76 int num_buffers;
77
78 void *common_buffer;
79 int common_buffer_size;
80 dma_addr_t common_buffer_phys;
81
82 void *context;
83 struct device *device;
84
85 char devpath[32];
86 unsigned long device_flags;
87
88 setmode_t setmode_handler;
89 detectmode_t detectmode_handler;
90 sendrequest_t sendrequest_handler;
91 preload_t preload_handler;
92 postload_t postload_handler;
93
94 int mode, modes_supported;
95
96 struct completion version_ex_done, data_download_done, trigger_done;
97 struct completion init_device_done, reload_start_done, resume_done;
98} *psmscore_device_t;
99
100typedef struct _smscore_registry_entry
101{
102 struct list_head entry;
103 char devpath[32];
104 int mode;
105} smscore_registry_entry_t;
106
107struct list_head g_smscore_notifyees;
108struct list_head g_smscore_devices;
109kmutex_t g_smscore_deviceslock;
110
111struct list_head g_smscore_registry;
112kmutex_t g_smscore_registrylock;
113
114static int default_mode = 1;
115module_param(default_mode, int, 0644);
116MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
117
118int smscore_registry_getmode(char* devpath)
119{
120 smscore_registry_entry_t *entry;
121 struct list_head *next;
122
123 kmutex_lock(&g_smscore_registrylock);
124
125 for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
126 {
127 entry = (smscore_registry_entry_t *) next;
128
129 if (!strcmp(entry->devpath, devpath))
130 {
131 kmutex_unlock(&g_smscore_registrylock);
132 return entry->mode;
133 }
134 }
135
136 entry = (smscore_registry_entry_t *) kmalloc(sizeof(smscore_registry_entry_t), GFP_KERNEL);
137 if (entry)
138 {
139 entry->mode = default_mode;
140 strcpy(entry->devpath, devpath);
141
142 list_add(&entry->entry, &g_smscore_registry);
143 }
144
145 kmutex_unlock(&g_smscore_registrylock);
146
147 return default_mode;
148}
149
150void smscore_registry_setmode(char* devpath, int mode)
151{
152 smscore_registry_entry_t *entry;
153 struct list_head *next;
154
155 kmutex_lock(&g_smscore_registrylock);
156
157 for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
158 {
159 entry = (smscore_registry_entry_t *) next;
160
161 if (!strcmp(entry->devpath, devpath))
162 {
163 entry->mode = mode;
164 break;
165 }
166 }
167
168 kmutex_unlock(&g_smscore_registrylock);
169}
170
171
172void list_add_locked(struct list_head *new, struct list_head *head, spinlock_t* lock)
173{
174 unsigned long flags;
175
176 spin_lock_irqsave(lock, flags);
177
178 list_add(new, head);
179
180 spin_unlock_irqrestore(lock, flags);
181}
182
183/**
184 * register a client callback that called when device plugged in/unplugged
185 * NOTE: if devices exist callback is called immediately for each device
186 *
187 * @param hotplug callback
188 *
189 * @return 0 on success, <0 on error.
190 */
191int smscore_register_hotplug(hotplug_t hotplug)
192{
193 smscore_device_notifyee_t *notifyee;
194 struct list_head *next, *first;
195 int rc = 0;
196
197 kmutex_lock(&g_smscore_deviceslock);
198
199 notifyee = kmalloc(sizeof(smscore_device_notifyee_t), GFP_KERNEL);
200 if (notifyee)
201 {
202 // now notify callback about existing devices
203 first = &g_smscore_devices;
204 for (next = first->next; next != first && !rc; next = next->next)
205 {
206 smscore_device_t *coredev = (smscore_device_t *) next;
207 rc = hotplug(coredev, coredev->device, 1);
208 }
209
210 if (rc >= 0)
211 {
212 notifyee->hotplug = hotplug;
213 list_add(&notifyee->entry, &g_smscore_notifyees);
214 }
215 else
216 kfree(notifyee);
217 }
218 else
219 rc = -ENOMEM;
220
221 kmutex_unlock(&g_smscore_deviceslock);
222
223 return rc;
224}
225
226/**
227 * unregister a client callback that called when device plugged in/unplugged
228 *
229 * @param hotplug callback
230 *
231 */
232void smscore_unregister_hotplug(hotplug_t hotplug)
233{
234 struct list_head *next, *first;
235
236 kmutex_lock(&g_smscore_deviceslock);
237
238 first = &g_smscore_notifyees;
239
240 for (next = first->next; next != first;)
241 {
242 smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) next;
243 next = next->next;
244
245 if (notifyee->hotplug == hotplug)
246 {
247 list_del(&notifyee->entry);
248 kfree(notifyee);
249 }
250 }
251
252 kmutex_unlock(&g_smscore_deviceslock);
253}
254
255void smscore_notify_clients(smscore_device_t *coredev)
256{
257 smscore_client_t* client;
258
259 // the client must call smscore_unregister_client from remove handler
260 while (!list_empty(&coredev->clients))
261 {
262 client = (smscore_client_t *) coredev->clients.next;
263 client->onremove_handler(client->context);
264 }
265}
266
267int smscore_notify_callbacks(smscore_device_t *coredev, struct device *device, int arrival)
268{
269 struct list_head *next, *first;
270 int rc = 0;
271
272 // note: must be called under g_deviceslock
273
274 first = &g_smscore_notifyees;
275
276 for (next = first->next; next != first; next = next->next)
277 {
278 rc = ((smscore_device_notifyee_t *) next)->hotplug(coredev, device, arrival);
279 if (rc < 0)
280 break;
281 }
282
283 return rc;
284}
285
286smscore_buffer_t *smscore_createbuffer(u8* buffer, void* common_buffer, dma_addr_t common_buffer_phys)
287{
288 smscore_buffer_t *cb = kmalloc(sizeof(smscore_buffer_t), GFP_KERNEL);
289 if (!cb)
290 {
291 printk(KERN_INFO "%s kmalloc(...) failed\n", __FUNCTION__);
292 return NULL;
293 }
294
295 cb->p = buffer;
296 cb->offset_in_common = buffer - (u8*) common_buffer;
297 cb->phys = common_buffer_phys + cb->offset_in_common;
298
299 return cb;
300}
301
302/**
303 * creates coredev object for a device, prepares buffers, creates buffer mappings, notifies
304 * registered hotplugs about new device.
305 *
306 * @param params device pointer to struct with device specific parameters and handlers
307 * @param coredev pointer to a value that receives created coredev object
308 *
309 * @return 0 on success, <0 on error.
310 */
311int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev)
312{
313 smscore_device_t* dev;
314 u8 *buffer;
315
316 dev = kzalloc(sizeof(smscore_device_t), GFP_KERNEL);
317 if (!dev)
318 {
319 printk(KERN_INFO "%s kzalloc(...) failed\n", __FUNCTION__);
320 return -ENOMEM;
321 }
322
323 // init list entry so it could be safe in smscore_unregister_device
324 INIT_LIST_HEAD(&dev->entry);
325
326 // init queues
327 INIT_LIST_HEAD(&dev->clients);
328 INIT_LIST_HEAD(&dev->subclients);
329 INIT_LIST_HEAD(&dev->buffers);
330
331 // init locks
332 spin_lock_init(&dev->clientslock);
333 spin_lock_init(&dev->bufferslock);
334
335 // init completion events
336 init_completion(&dev->version_ex_done);
337 init_completion(&dev->data_download_done);
338 init_completion(&dev->trigger_done);
339 init_completion(&dev->init_device_done);
340 init_completion(&dev->reload_start_done);
341 init_completion(&dev->resume_done);
342
343 // alloc common buffer
344 dev->common_buffer_size = params->buffer_size * params->num_buffers;
345 dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, &dev->common_buffer_phys, GFP_KERNEL | GFP_DMA);
346 if (!dev->common_buffer)
347 {
348 smscore_unregister_device(dev);
349 return -ENOMEM;
350 }
351
352 // prepare dma buffers
353 for (buffer = dev->common_buffer; dev->num_buffers < params->num_buffers; dev->num_buffers ++, buffer += params->buffer_size)
354 {
355 smscore_buffer_t *cb = smscore_createbuffer(buffer, dev->common_buffer, dev->common_buffer_phys);
356 if (!cb)
357 {
358 smscore_unregister_device(dev);
359 return -ENOMEM;
360 }
361
362 smscore_putbuffer(dev, cb);
363 }
364
365 printk(KERN_INFO "%s allocated %d buffers\n", __FUNCTION__, dev->num_buffers);
366
367 dev->mode = DEVICE_MODE_NONE;
368 dev->context = params->context;
369 dev->device = params->device;
370 dev->setmode_handler = params->setmode_handler;
371 dev->detectmode_handler = params->detectmode_handler;
372 dev->sendrequest_handler = params->sendrequest_handler;
373 dev->preload_handler = params->preload_handler;
374 dev->postload_handler = params->postload_handler;
375
376 dev->device_flags = params->flags;
377 strcpy(dev->devpath, params->devpath);
378
379 // add device to devices list
380 kmutex_lock(&g_smscore_deviceslock);
381 list_add(&dev->entry, &g_smscore_devices);
382 kmutex_unlock(&g_smscore_deviceslock);
383
384 *coredev = dev;
385
386 printk(KERN_INFO "%s device %p created\n", __FUNCTION__, dev);
387
388 return 0;
389}
390
391/**
392 * sets initial device mode and notifies client hotplugs that device is ready
393 *
394 * @param coredev pointer to a coredev object returned by smscore_register_device
395 *
396 * @return 0 on success, <0 on error.
397 */
398int smscore_start_device(smscore_device_t *coredev)
399{
400 int rc = smscore_set_device_mode(coredev, smscore_registry_getmode(coredev->devpath));
401 if (rc < 0)
402 return rc;
403
404 kmutex_lock(&g_smscore_deviceslock);
405
406 rc = smscore_notify_callbacks(coredev, coredev->device, 1);
407
408 printk(KERN_INFO "%s device %p started, rc %d\n", __FUNCTION__, coredev, rc);
409
410 kmutex_unlock(&g_smscore_deviceslock);
411
412 return rc;
413}
414
415int smscore_sendrequest_and_wait(smscore_device_t *coredev, void* buffer, size_t size, struct completion *completion)
416{
417 int rc = coredev->sendrequest_handler(coredev->context, buffer, size);
418 if (rc < 0)
419 return rc;
420
421 return wait_for_completion_timeout(completion, msecs_to_jiffies(1000)) ? 0 : -ETIME;
422}
423
424int smscore_load_firmware_family2(smscore_device_t *coredev, void *buffer, size_t size)
425{
426 SmsFirmware_ST* firmware = (SmsFirmware_ST*) buffer;
427 SmsMsgHdr_ST *msg;
428 UINT32 mem_address = firmware->StartAddress;
429 u8* payload = firmware->Payload;
430 int rc = 0;
431
432 if (coredev->preload_handler)
433 {
434 rc = coredev->preload_handler(coredev->context);
435 if (rc < 0)
436 return rc;
437 }
438
439 // PAGE_SIZE buffer shall be enough and dma aligned
440 msg = (SmsMsgHdr_ST *) kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
441 if (!msg)
442 return -ENOMEM;
443
444 if (coredev->mode != DEVICE_MODE_NONE)
445 {
446 SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, sizeof(SmsMsgHdr_ST));
447 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->reload_start_done);
448 mem_address = *(UINT32*) &payload[20];
449 }
450
451 while (size && rc >= 0)
452 {
453 SmsDataDownload_ST *DataMsg = (SmsDataDownload_ST *) msg;
454 int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);
455
456 SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, (UINT16)(sizeof(SmsMsgHdr_ST) + sizeof(UINT32) + payload_size));
457
458 DataMsg->MemAddr = mem_address;
459 memcpy(DataMsg->Payload, payload, payload_size);
460
461 if (coredev->device_flags & SMS_ROM_NO_RESPONSE && coredev->mode == DEVICE_MODE_NONE)
462 rc = coredev->sendrequest_handler(coredev->context, DataMsg, DataMsg->xMsgHeader.msgLength);
463 else
464 rc = smscore_sendrequest_and_wait(coredev, DataMsg, DataMsg->xMsgHeader.msgLength, &coredev->data_download_done);
465
466 payload += payload_size;
467 size -= payload_size;
468 mem_address += payload_size;
469 }
470
471 if (rc >= 0)
472 {
473 if (coredev->mode == DEVICE_MODE_NONE)
474 {
475 SmsMsgData_ST* TriggerMsg = (SmsMsgData_ST*) msg;
476
477 SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, sizeof(SmsMsgHdr_ST) + sizeof(UINT32) * 5);
478
479 TriggerMsg->msgData[0] = firmware->StartAddress; // Entry point
480 TriggerMsg->msgData[1] = 5; // Priority
481 TriggerMsg->msgData[2] = 0x200; // Stack size
482 TriggerMsg->msgData[3] = 0; // Parameter
483 TriggerMsg->msgData[4] = 4; // Task ID
484
485 if (coredev->device_flags & SMS_ROM_NO_RESPONSE)
486 {
487 rc = coredev->sendrequest_handler(coredev->context, TriggerMsg, TriggerMsg->xMsgHeader.msgLength);
488 msleep(100);
489 }
490 else
491 rc = smscore_sendrequest_and_wait(coredev, TriggerMsg, TriggerMsg->xMsgHeader.msgLength, &coredev->trigger_done);
492 }
493 else
494 {
495 SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, sizeof(SmsMsgHdr_ST));
496
497 rc = coredev->sendrequest_handler(coredev->context, msg, msg->msgLength);
498 }
499 }
500
501 printk("%s %d \n", __func__, rc);
502
503 kfree(msg);
504
505 return (rc >= 0 && coredev->postload_handler) ?
506 coredev->postload_handler(coredev->context) :
507 rc;
508}
509
510/**
511 * loads specified firmware into a buffer and calls device loadfirmware_handler
512 *
513 * @param coredev pointer to a coredev object returned by smscore_register_device
514 * @param filename null-terminated string specifies firmware file name
515 * @param loadfirmware_handler device handler that loads firmware
516 *
517 * @return 0 on success, <0 on error.
518 */
519int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler)
520{
521 int rc = -ENOENT;
522
523 const struct firmware *fw;
524 u8* fw_buffer;
525
526 if (loadfirmware_handler == NULL && !(coredev->device_flags & SMS_DEVICE_FAMILY2))
527 return -EINVAL;
528
529 rc = request_firmware(&fw, filename, coredev->device);
530 if (rc < 0)
531 {
532 printk(KERN_INFO "%s failed to open \"%s\"\n", __FUNCTION__, filename);
533 return rc;
534 }
535
536 fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA);
537 if (fw_buffer)
538 {
539 memcpy(fw_buffer, fw->data, fw->size);
540
541 rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
542 smscore_load_firmware_family2(coredev, fw_buffer, fw->size) :
543 loadfirmware_handler(coredev->context, fw_buffer, fw->size);
544
545 kfree(fw_buffer);
546 }
547 else
548 {
549 printk(KERN_INFO "%s failed to allocate firmware buffer\n", __FUNCTION__);
550 rc = -ENOMEM;
551 }
552
553 release_firmware(fw);
554
555 return rc;
556}
557
558/**
559 * notifies all clients registered with the device, notifies hotplugs, frees all buffers and coredev object
560 *
561 * @param coredev pointer to a coredev object returned by smscore_register_device
562 *
563 * @return 0 on success, <0 on error.
564 */
565void smscore_unregister_device(smscore_device_t *coredev)
566{
567 smscore_buffer_t *cb;
568 int num_buffers = 0;
569
570 kmutex_lock(&g_smscore_deviceslock);
571
572 smscore_notify_clients(coredev);
573 smscore_notify_callbacks(coredev, NULL, 0);
574
575 // at this point all buffers should be back
576 // onresponse must no longer be called
577
578 while (1)
579 {
580 while ((cb = smscore_getbuffer(coredev)))
581 {
582 kfree(cb);
583 num_buffers ++;
584 }
585
586 if (num_buffers == coredev->num_buffers)
587 break;
588
589 printk(KERN_INFO "%s waiting for %d buffer(s)\n", __FUNCTION__, coredev->num_buffers - num_buffers);
590 msleep(100);
591 }
592
593 printk(KERN_INFO "%s freed %d buffers\n", __FUNCTION__, num_buffers);
594
595 if (coredev->common_buffer)
596 dma_free_coherent(NULL, coredev->common_buffer_size, coredev->common_buffer, coredev->common_buffer_phys);
597
598 list_del(&coredev->entry);
599 kfree(coredev);
600
601 kmutex_unlock(&g_smscore_deviceslock);
602
603 printk(KERN_INFO "%s device %p destroyed\n", __FUNCTION__, coredev);
604}
605
606int smscore_detect_mode(smscore_device_t *coredev)
607{
608 void *buffer = kmalloc(sizeof(SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
609 SmsMsgHdr_ST *msg = (SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);
610 int rc;
611
612 if (!buffer)
613 return -ENOMEM;
614
615 SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, sizeof(SmsMsgHdr_ST));
616
617 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
618 if (rc == -ETIME)
619 {
620 printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed first try\n", __FUNCTION__);
621
622 if (wait_for_completion_timeout(&coredev->resume_done, msecs_to_jiffies(5000)))
623 {
624 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
625 if (rc < 0)
626 {
627 printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n", __FUNCTION__, rc);
628 }
629 }
630 else
631 rc = -ETIME;
632 }
633
634 kfree(buffer);
635
636 return rc;
637}
638
639char *smscore_fw_lkup[] =
640{
641 "dvb_nova_12mhz.inp",
642 "dvb_nova_12mhz.inp",
643 "tdmb_nova.inp",
644 "none",
645 "dvb_nova_12mhz.inp",
646 "isdbt_nova_12mhz.inp",
647 "isdbt_nova_12mhz.inp",
648 "cmmb_nova_12mhz.inp",
649 "none",
650};
651
652/**
653 * calls device handler to change mode of operation
654 * NOTE: stellar/usb may disconnect when changing mode
655 *
656 * @param coredev pointer to a coredev object returned by smscore_register_device
657 * @param mode requested mode of operation
658 *
659 * @return 0 on success, <0 on error.
660 */
661int smscore_set_device_mode(smscore_device_t *coredev, int mode)
662{
663 void *buffer;
664 int rc = 0;
665
666 if (coredev->device_flags & SMS_DEVICE_FAMILY2)
667 {
668 if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER)
669 {
670 printk(KERN_INFO "%s invalid mode specified %d\n", __FUNCTION__, mode);
671 return -EINVAL;
672 }
673
674 if (!(coredev->device_flags & SMS_DEVICE_NOT_READY))
675 {
676 rc = smscore_detect_mode(coredev);
677 if (rc < 0)
678 return rc;
679 }
680
681 if (coredev->mode == mode)
682 {
683 printk(KERN_INFO "%s device mode %d already set\n", __FUNCTION__, mode);
684 return 0;
685 }
686
687 if (!(coredev->modes_supported & (1 << mode)))
688 {
689 rc = smscore_load_firmware(coredev, smscore_fw_lkup[mode], NULL);
690 if (rc < 0)
691 return rc;
692 }
693 else
694 {
695 printk(KERN_INFO "%s mode %d supported by running firmware\n", __FUNCTION__, mode);
696 }
697
698 buffer = kmalloc(sizeof(SmsMsgData_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
699 if (buffer)
700 {
701 SmsMsgData_ST *msg = (SmsMsgData_ST *) SMS_ALIGN_ADDRESS(buffer);
702
703 SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, sizeof(SmsMsgData_ST));
704 msg->msgData[0] = mode;
705
706 rc = smscore_sendrequest_and_wait(coredev, msg, msg->xMsgHeader.msgLength, &coredev->init_device_done);
707
708 kfree(buffer);
709 }
710 else
711 rc = -ENOMEM;
712 }
713 else
714 {
715 if (coredev->detectmode_handler)
716 coredev->detectmode_handler(coredev->context, &coredev->mode);
717
718 if (coredev->mode != mode && coredev->setmode_handler)
719 rc = coredev->setmode_handler(coredev->context, mode);
720 }
721
722 smscore_registry_setmode(coredev->devpath, mode);
723
724 if (rc >= 0)
725 {
726 coredev->mode = mode;
727 coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
728 }
729
730 return rc;
731}
732
733/**
734 * calls device handler to get current mode of operation
735 *
736 * @param coredev pointer to a coredev object returned by smscore_register_device
737 *
738 * @return current mode
739 */
740int smscore_get_device_mode(smscore_device_t *coredev)
741{
742 return coredev->mode;
743}
744
745smscore_client_t* smscore_getclient_by_type(smscore_device_t *coredev, int data_type)
746{
747 smscore_client_t *client = NULL;
748 struct list_head *next, *first;
749 unsigned long flags;
750
751 if (!data_type)
752 return NULL;
753
754 spin_lock_irqsave(&coredev->clientslock, flags);
755
756 first = &coredev->clients;
757
758 for (next = first->next; next != first; next = next->next)
759 {
760 if (((smscore_client_t*) next)->data_type == data_type)
761 {
762 client = (smscore_client_t*) next;
763 break;
764 }
765 }
766
767 spin_unlock_irqrestore(&coredev->clientslock, flags);
768
769 return client;
770}
771
772smscore_client_t* smscore_getclient_by_id(smscore_device_t *coredev, int id)
773{
774 smscore_client_t *client = NULL;
775 struct list_head *next, *first;
776 unsigned long flags;
777
778 spin_lock_irqsave(&coredev->clientslock, flags);
779
780 first = &coredev->subclients;
781
782 for (next = first->next; next != first; next = next->next)
783 {
784 if (((smscore_subclient_t*) next)->id == id)
785 {
786 client = ((smscore_subclient_t*) next)->client;
787 break;
788 }
789 }
790
791 spin_unlock_irqrestore(&coredev->clientslock, flags);
792
793 return client;
794}
795
796/**
797 * find client by response id/type, call clients onresponse handler
798 * return buffer to pool on error
799 *
800 * @param coredev pointer to a coredev object returned by smscore_register_device
801 * @param cb pointer to response buffer descriptor
802 *
803 */
804void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb)
805{
806 SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)((u8*) cb->p + cb->offset);
807 smscore_client_t * client = smscore_getclient_by_type(coredev, phdr->msgType);
808 int rc = -EBUSY;
809
810 static unsigned long last_sample_time = 0;
811 static int data_total = 0;
812 unsigned long time_now = jiffies_to_msecs(jiffies);
813
814 if (!last_sample_time)
815 last_sample_time = time_now;
816
817 if (time_now - last_sample_time > 10000)
818 {
819 printk("\n%s data rate %d bytes/secs\n", __func__, (int)((data_total * 1000) / (time_now - last_sample_time)));
820
821 last_sample_time = time_now;
822 data_total = 0;
823 }
824
825 data_total += cb->size;
826
827 if (!client)
828 client = smscore_getclient_by_id(coredev, phdr->msgDstId);
829
830 if (client)
831 rc = client->onresponse_handler(client->context, cb);
832
833 if (rc < 0)
834 {
835 switch (phdr->msgType)
836 {
837 case MSG_SMS_GET_VERSION_EX_RES:
838 {
839 SmsVersionRes_ST *ver = (SmsVersionRes_ST*) phdr;
840 printk("%s: MSG_SMS_GET_VERSION_EX_RES id %d prots 0x%x ver %d.%d\n", __FUNCTION__, ver->FirmwareId, ver->SupportedProtocols, ver->RomVersionMajor, ver->RomVersionMinor);
841
842 coredev->mode = ver->FirmwareId == 255 ? DEVICE_MODE_NONE : ver->FirmwareId;
843 coredev->modes_supported = ver->SupportedProtocols;
844
845 complete(&coredev->version_ex_done);
846 break;
847 }
848
849 case MSG_SMS_INIT_DEVICE_RES:
850 printk("%s: MSG_SMS_INIT_DEVICE_RES\n", __FUNCTION__);
851 complete(&coredev->init_device_done);
852 break;
853
854 case MSG_SW_RELOAD_START_RES:
855 printk("%s: MSG_SW_RELOAD_START_RES\n", __FUNCTION__);
856 complete(&coredev->reload_start_done);
857 break;
858
859 case MSG_SMS_DATA_DOWNLOAD_RES:
860 complete(&coredev->data_download_done);
861 break;
862
863 case MSG_SW_RELOAD_EXEC_RES:
864 printk("%s: MSG_SW_RELOAD_EXEC_RES\n", __FUNCTION__);
865 break;
866
867 case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
868 printk("%s: MSG_SMS_SWDOWNLOAD_TRIGGER_RES\n", __FUNCTION__);
869 complete(&coredev->trigger_done);
870 break;
871
872 case MSG_SMS_SLEEP_RESUME_COMP_IND:
873 complete(&coredev->resume_done);
874 break;
875
876 default:
877 printk(KERN_INFO "%s no client (%p) or error (%d), type:%d dstid:%d\n", __FUNCTION__, client, rc, phdr->msgType, phdr->msgDstId);
878 }
879
880 smscore_putbuffer(coredev, cb);
881 }
882}
883
884/**
885 * return pointer to next free buffer descriptor from core pool
886 *
887 * @param coredev pointer to a coredev object returned by smscore_register_device
888 *
889 * @return pointer to descriptor on success, NULL on error.
890 */
891smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev)
892{
893 smscore_buffer_t *cb = NULL;
894 unsigned long flags;
895
896 spin_lock_irqsave(&coredev->bufferslock, flags);
897
898 if (!list_empty(&coredev->buffers))
899 {
900 cb = (smscore_buffer_t *) coredev->buffers.next;
901 list_del(&cb->entry);
902 }
903
904 spin_unlock_irqrestore(&coredev->bufferslock, flags);
905
906 return cb;
907}
908
909/**
910 * return buffer descriptor to a pool
911 *
912 * @param coredev pointer to a coredev object returned by smscore_register_device
913 * @param cb pointer buffer descriptor
914 *
915 */
916void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb)
917{
918 list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
919}
920
921int smscore_validate_client(smscore_device_t *coredev, smscore_client_t *client, int id)
922{
923 smscore_client_t *existing_client;
924 smscore_subclient_t *subclient;
925
926 if (!id)
927 return 0;
928
929 existing_client = smscore_getclient_by_id(coredev, id);
930 if (existing_client == client)
931 return 0;
932
933 if (existing_client)
934 return -EBUSY;
935
936 subclient = kzalloc(sizeof(smscore_subclient_t), GFP_KERNEL);
937 if (!subclient)
938 return -ENOMEM;
939
940 subclient->client = client;
941 subclient->id = id;
942
943 list_add_locked(&subclient->entry, &coredev->subclients, &coredev->clientslock);
944
945 return 0;
946}
947
948/**
949 * creates smsclient object, check that id is taken by another client
950 *
951 * @param coredev pointer to a coredev object from clients hotplug
952 * @param initial_id all messages with this id would be sent to this client
953 * @param data_type all messages of this type would be sent to this client
954 * @param onresponse_handler client handler that is called to process incoming messages
955 * @param onremove_handler client handler that is called when device is removed
956 * @param context client-specific context
957 * @param client pointer to a value that receives created smsclient object
958 *
959 * @return 0 on success, <0 on error.
960 */
961int smscore_register_client(smscore_device_t *coredev, smsclient_params_t *params, smscore_client_t **client)
962{
963 smscore_client_t* newclient;
964 int rc;
965
966 // check that no other channel with same data type exists
967 if (params->data_type && smscore_getclient_by_type(coredev, params->data_type))
968 return -EEXIST;
969
970 newclient = kzalloc(sizeof(smscore_client_t), GFP_KERNEL);
971 if (!newclient)
972 return -ENOMEM;
973
974 // check that no other channel with same id exists
975 rc = smscore_validate_client(coredev, newclient, params->initial_id);
976 if (rc < 0)
977 {
978 kfree(newclient);
979 return rc;
980 }
981
982 newclient->coredev = coredev;
983 newclient->data_type = params->data_type;
984 newclient->onresponse_handler = params->onresponse_handler;
985 newclient->onremove_handler = params->onremove_handler;
986 newclient->context = params->context;
987
988 list_add_locked(&newclient->entry, &coredev->clients, &coredev->clientslock);
989
990 *client = newclient;
991
992 printk(KERN_INFO "%s %p %d %d\n", __FUNCTION__, params->context, params->data_type, params->initial_id);
993
994 return 0;
995}
996
997/**
998 * frees smsclient object and all subclients associated with it
999 *
1000 * @param client pointer to smsclient object returned by smscore_register_client
1001 *
1002 */
1003void smscore_unregister_client(smscore_client_t *client)
1004{
1005 smscore_device_t *coredev = client->coredev;
1006 struct list_head *next, *first;
1007 unsigned long flags;
1008
1009 spin_lock_irqsave(&coredev->clientslock, flags);
1010
1011 first = &coredev->subclients;
1012
1013 for (next = first->next; next != first;)
1014 {
1015 smscore_subclient_t *subclient = (smscore_subclient_t *) next;
1016 next = next->next;
1017
1018 if (subclient->client == client)
1019 {
1020 list_del(&subclient->entry);
1021 kfree(subclient);
1022 }
1023 }
1024
1025 printk(KERN_INFO "%s %p %d\n", __FUNCTION__, client->context, client->data_type);
1026
1027 list_del(&client->entry);
1028 kfree(client);
1029
1030 spin_unlock_irqrestore(&coredev->clientslock, flags);
1031}
1032
1033/**
1034 * verifies that source id is not taken by another client,
1035 * calls device handler to send requests to the device
1036 *
1037 * @param client pointer to smsclient object returned by smscore_register_client
1038 * @param buffer pointer to a request buffer
1039 * @param size size (in bytes) of request buffer
1040 *
1041 * @return 0 on success, <0 on error.
1042 */
1043int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size)
1044{
1045 smscore_device_t* coredev = client->coredev;
1046 SmsMsgHdr_ST* phdr = (SmsMsgHdr_ST*) buffer;
1047
1048 // check that no other channel with same id exists
1049 int rc = smscore_validate_client(client->coredev, client, phdr->msgSrcId);
1050 if (rc < 0)
1051 return rc;
1052
1053 return coredev->sendrequest_handler(coredev->context, buffer, size);
1054}
1055
1056/**
1057 * return the size of large (common) buffer
1058 *
1059 * @param coredev pointer to a coredev object from clients hotplug
1060 *
1061 * @return size (in bytes) of the buffer
1062 */
1063int smscore_get_common_buffer_size(smscore_device_t *coredev)
1064{
1065 return coredev->common_buffer_size;
1066}
1067
1068/**
1069 * maps common buffer (if supported by platform)
1070 *
1071 * @param coredev pointer to a coredev object from clients hotplug
1072 * @param vma pointer to vma struct from mmap handler
1073 *
1074 * @return 0 on success, <0 on error.
1075 */
1076int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma)
1077{
1078 unsigned long end = vma->vm_end, start = vma->vm_start, size = PAGE_ALIGN(coredev->common_buffer_size);
1079
1080 if (!(vma->vm_flags & (VM_READ | VM_SHARED)) || (vma->vm_flags & VM_WRITE))
1081 {
1082 printk(KERN_INFO "%s invalid vm flags\n", __FUNCTION__);
1083 return -EINVAL;
1084 }
1085
1086 if ((end - start) != size)
1087 {
1088 printk(KERN_INFO "%s invalid size %d expected %d\n", __FUNCTION__, (int)(end - start), (int) size);
1089 return -EINVAL;
1090 }
1091
1092 if (remap_pfn_range(vma, start, coredev->common_buffer_phys >> PAGE_SHIFT, size, pgprot_noncached(vma->vm_page_prot)))
1093 {
1094 printk(KERN_INFO "%s remap_page_range failed\n", __FUNCTION__);
1095 return -EAGAIN;
1096 }
1097
1098 return 0;
1099}
1100
1101int smscore_module_init(void)
1102{
1103 int rc;
1104
1105 INIT_LIST_HEAD(&g_smscore_notifyees);
1106 INIT_LIST_HEAD(&g_smscore_devices);
1107 kmutex_init(&g_smscore_deviceslock);
1108
1109 INIT_LIST_HEAD(&g_smscore_registry);
1110 kmutex_init(&g_smscore_registrylock);
1111
1112 rc = smschar_initialize();
1113
1114 printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
1115
1116 return rc;
1117}
1118
1119void smscore_module_exit(void)
1120{
1121 smschar_terminate();
1122
1123 kmutex_lock(&g_smscore_deviceslock);
1124 while (!list_empty(&g_smscore_notifyees))
1125 {
1126 smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) g_smscore_notifyees.next;
1127
1128 list_del(&notifyee->entry);
1129 kfree(notifyee);
1130 }
1131 kmutex_unlock(&g_smscore_deviceslock);
1132
1133 kmutex_lock(&g_smscore_registrylock);
1134 while (!list_empty(&g_smscore_registry))
1135 {
1136 smscore_registry_entry_t *entry = (smscore_registry_entry_t *) g_smscore_registry.next;
1137
1138 list_del(&entry->entry);
1139 kfree(entry);
1140 }
1141 kmutex_unlock(&g_smscore_registrylock);
1142
1143 printk(KERN_INFO "%s\n", __FUNCTION__);
1144}
1145
1146module_init(smscore_module_init);
1147module_exit(smscore_module_exit);
1148
1149MODULE_DESCRIPTION("smscore");
1150MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
1151MODULE_LICENSE("GPL");
1152
1153EXPORT_SYMBOL(smscore_registry_setmode);
1154EXPORT_SYMBOL(smscore_registry_getmode);
1155EXPORT_SYMBOL(smscore_register_hotplug);
1156EXPORT_SYMBOL(smscore_unregister_hotplug);
1157EXPORT_SYMBOL(smscore_register_device);
1158EXPORT_SYMBOL(smscore_unregister_device);
1159EXPORT_SYMBOL(smscore_start_device);
1160EXPORT_SYMBOL(smscore_load_firmware);
1161EXPORT_SYMBOL(smscore_set_device_mode);
1162EXPORT_SYMBOL(smscore_get_device_mode);
1163EXPORT_SYMBOL(smscore_register_client);
1164EXPORT_SYMBOL(smscore_unregister_client);
1165EXPORT_SYMBOL(smsclient_sendrequest);
1166EXPORT_SYMBOL(smscore_onresponse);
1167EXPORT_SYMBOL(smscore_get_common_buffer_size);
1168EXPORT_SYMBOL(smscore_map_common_buffer);
1169EXPORT_SYMBOL(smscore_getbuffer);
1170EXPORT_SYMBOL(smscore_putbuffer);
diff --git a/drivers/media/mdtv/smscoreapi.h b/drivers/media/mdtv/smscoreapi.h
new file mode 100644
index 000000000000..a8c025f74eb7
--- /dev/null
+++ b/drivers/media/mdtv/smscoreapi.h
@@ -0,0 +1,101 @@
1#ifndef __smscoreapi_h__
2#define __smscoreapi_h__
3
4#ifndef min
5#define min(a,b) (((a) < (b)) ? (a) : (b))
6#endif
7
8#define SMS_ALLOC_ALIGNMENT 128
9#define SMS_DMA_ALIGNMENT 16
10#define SMS_ALIGN_ADDRESS(addr) ((((u32)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1))
11
12#define SMS_DEVICE_FAMILY2 1
13#define SMS_ROM_NO_RESPONSE 2
14#define SMS_DEVICE_NOT_READY 0x8000000
15
16typedef struct _smscore_device smscore_device_t;
17typedef struct _smscore_client smscore_client_t;
18typedef struct _smscore_buffer smscore_buffer_t;
19
20typedef int (*hotplug_t)(smscore_device_t *coredev, struct device *device, int arrival);
21
22typedef int (*setmode_t)(void *context, int mode);
23typedef void (*detectmode_t)(void *context, int *mode);
24typedef int (*sendrequest_t)(void *context, void *buffer, size_t size);
25typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size);
26typedef int (*preload_t)(void *context);
27typedef int (*postload_t)(void *context);
28
29typedef int (*onresponse_t)(void *context, smscore_buffer_t *cb);
30typedef void (*onremove_t)(void *context);
31
32typedef struct _smscore_buffer
33{
34 // public members, once passed to clients can be changed freely
35 struct list_head entry;
36 int size;
37 int offset;
38
39 // private members, read-only for clients
40 void *p;
41 dma_addr_t phys;
42 unsigned long offset_in_common;
43} *psmscore_buffer_t;
44
45typedef struct _smsdevice_params
46{
47 struct device *device;
48
49 int buffer_size;
50 int num_buffers;
51
52 char devpath[32];
53 unsigned long flags;
54
55 setmode_t setmode_handler;
56 detectmode_t detectmode_handler;
57 sendrequest_t sendrequest_handler;
58 preload_t preload_handler;
59 postload_t postload_handler;
60
61 void *context;
62} smsdevice_params_t;
63
64typedef struct _smsclient_params
65{
66 int initial_id;
67 int data_type;
68 onresponse_t onresponse_handler;
69 onremove_t onremove_handler;
70
71 void *context;
72} smsclient_params_t;
73
74extern void smscore_registry_setmode(char *devpath, int mode);
75extern int smscore_registry_getmode(char *devpath);
76
77extern int smscore_register_hotplug(hotplug_t hotplug);
78extern void smscore_unregister_hotplug(hotplug_t hotplug);
79
80extern int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev);
81extern void smscore_unregister_device(smscore_device_t *coredev);
82
83extern int smscore_start_device(smscore_device_t *coredev);
84extern int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler);
85
86extern int smscore_set_device_mode(smscore_device_t *coredev, int mode);
87extern int smscore_get_device_mode(smscore_device_t *coredev);
88
89extern int smscore_register_client(smscore_device_t *coredev, smsclient_params_t* params, smscore_client_t **client);
90extern void smscore_unregister_client(smscore_client_t *client);
91
92extern int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size);
93extern void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb);
94
95extern int smscore_get_common_buffer_size(smscore_device_t *coredev);
96extern int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma);
97
98extern smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev);
99extern void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb);
100
101#endif // __smscoreapi_h__
diff --git a/drivers/media/mdtv/smsdvb.c b/drivers/media/mdtv/smsdvb.c
new file mode 100644
index 000000000000..f2ed1718d180
--- /dev/null
+++ b/drivers/media/mdtv/smsdvb.c
@@ -0,0 +1,438 @@
1#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");
diff --git a/drivers/media/mdtv/smskdefs.h b/drivers/media/mdtv/smskdefs.h
new file mode 100644
index 000000000000..6874fbcdc7a8
--- /dev/null
+++ b/drivers/media/mdtv/smskdefs.h
@@ -0,0 +1,21 @@
1#ifndef __smskdefs_h__
2#define __smskdefs_h__
3
4#include <linux/version.h>
5#include <linux/device.h>
6#include <linux/list.h>
7#include <linux/mm.h>
8#include <asm/scatterlist.h>
9#include <asm/page.h>
10
11#include <linux/mutex.h>
12
13typedef struct mutex kmutex_t;
14
15#define kmutex_init(_p_) mutex_init(_p_)
16#define kmutex_lock(_p_) mutex_lock(_p_)
17#define kmutex_trylock(_p_) mutex_trylock(_p_)
18#define kmutex_unlock(_p_) mutex_unlock(_p_)
19
20
21#endif // __smskdefs_h__
diff --git a/drivers/media/mdtv/smsnet.c b/drivers/media/mdtv/smsnet.c
new file mode 100644
index 000000000000..5b70d1261e59
--- /dev/null
+++ b/drivers/media/mdtv/smsnet.c
@@ -0,0 +1,447 @@
1#include <linux/module.h>
2#include <linux/init.h>
3#include <linux/netdevice.h> /* struct device, and other headers */
4#include <linux/etherdevice.h> /* eth_type_trans */
5#include <linux/ip.h> /* struct iphdr */
6#include <linux/ipv6.h> /* struct ipv6hdr */
7#include <linux/in.h>
8
9#include "smskdefs.h" // page, scatterlist, kmutex
10#include "smscoreapi.h"
11#include "smstypes.h"
12
13#define IPV4VERSION 0x40
14#define IPV6VERSION 0x60
15#define GETIPVERSION(_x_) ((_x_) & 0xf0)
16
17typedef struct _smsnet_client
18{
19 struct list_head entry;
20
21 smscore_device_t *coredev;
22 smscore_client_t *smsclient;
23
24 int packet_length, splitpacket_length;
25 int header_length, splitheader_length;
26 u8 splitpacket[ETH_DATA_LEN];
27} smsnet_client_t;
28
29struct list_head g_smsnet_clients;
30kmutex_t g_smsnet_clientslock;
31
32struct net_device *g_smsnet_device = NULL;
33struct net_device_stats g_smsnet_stats;
34
35int g_smsnet_inuse = 0;
36
37void smsnet_send_packet(u8* buffer, int length)
38{
39 u8 *eth;
40 struct sk_buff *skb = dev_alloc_skb(length + ETH_HLEN + NET_IP_ALIGN);
41
42 if (!skb)
43 {
44 g_smsnet_stats.rx_dropped++;
45 return;
46 }
47
48 skb_reserve(skb, NET_IP_ALIGN);
49
50 eth = (u8 *) skb_put(skb, length + ETH_HLEN);
51 memcpy(eth + ETH_HLEN, buffer, length);
52
53 eth[6] = 0;
54 eth[7] = 1;
55 eth[8] = 1;
56 eth[9] = 3;
57 eth[10] = 4;
58 eth[11] = 5;
59
60 if (GETIPVERSION(*buffer) == IPV4VERSION)
61 {
62 eth[0] = 1;
63 eth[1] = 0;
64 eth[2] = 0x5e;
65 eth[3] = buffer[17] & 0x7f;
66 eth[4] = buffer[18];
67 eth[5] = buffer[19];
68
69 eth[12] = 0x08;
70 eth[13] = 0x00;
71 }
72 else
73 {
74 // todo: ip6 mcast address
75
76 eth[12] = 0x86;
77 eth[13] = 0xdd;
78 }
79
80 skb->dev = g_smsnet_device;
81 skb->protocol = eth_type_trans(skb, g_smsnet_device);
82 skb->ip_summed = CHECKSUM_UNNECESSARY;
83
84 g_smsnet_stats.rx_packets ++;
85 g_smsnet_stats.rx_bytes += skb->len;
86
87 netif_rx(skb);
88}
89
90int check_header(smsnet_client_t* client, u8* buffer)
91{
92 struct iphdr *ip4_hdr;
93 struct ipv6hdr *ip6_hdr;
94 struct udphdr *udp_hdr;
95 u16 csum;
96
97 // check if packet header is valid and it is a UDP
98 if (GETIPVERSION(*buffer) == IPV4VERSION)
99 {
100 ip4_hdr = (struct iphdr*) buffer;
101 csum = ip4_hdr->check;
102
103 ip4_hdr->check = 0;
104
105 // check header checksum for IPv4 packets
106 if(ip4_hdr->protocol != IPPROTO_UDP || csum != ip_fast_csum(buffer, ip4_hdr->ihl))
107 {
108 ip4_hdr->check = csum;
109 return 0;
110 }
111
112 ip4_hdr->check = csum;
113 client->packet_length = ntohs(ip4_hdr->tot_len);
114 }
115 else
116 {
117 ip6_hdr = (struct ipv6hdr *) buffer;
118 udp_hdr = (struct udphdr *)(ip6_hdr + 1);
119
120 if ((ip6_hdr->nexthdr != IPPROTO_UDP) ||
121 (ip6_hdr->payload_len != udp_hdr->len))
122 {
123 return 0;
124 }
125
126 client->packet_length = ntohs(ip6_hdr->payload_len) + sizeof(struct ipv6hdr);
127 }
128
129 // check for abnormal packet length
130 if (client->packet_length > ETH_DATA_LEN)
131 return 0;
132
133 return 1;
134}
135
136int smsnet_onresponse(void *context, smscore_buffer_t *cb)
137{
138 smsnet_client_t *client = (smsnet_client_t *) context;
139 int length, rest;
140 u8 ip_ver, *buffer;
141
142 buffer = ((u8*) cb->p) + cb->offset + sizeof(SmsMsgHdr_ST);
143 length = cb->size - sizeof(SmsMsgHdr_ST);
144
145 if (client->splitheader_length)
146 {
147 // how much data is missing ?
148 rest = client->header_length - client->splitheader_length;
149
150 // do we have enough in this buffer ?
151 rest = min(rest, length);
152
153 memcpy(&client->splitpacket[client->splitheader_length], buffer, rest);
154
155 client->splitheader_length += rest;
156
157 if (client->splitheader_length != client->header_length)
158 goto exit;
159
160 if (check_header(client, client->splitpacket))
161 {
162 buffer += rest;
163 length -= rest;
164
165 client->splitpacket_length = client->header_length;
166 }
167
168 client->splitheader_length = 0;
169 }
170
171 if (client->splitpacket_length)
172 {
173 // how much data is missing ?
174 rest = client->packet_length - client->splitpacket_length;
175
176 // do we have enough in this buffer ?
177 rest = min(rest, length);
178
179 memcpy(&client->splitpacket[client->splitpacket_length], buffer, rest);
180
181 client->splitpacket_length += rest;
182
183 if (client->splitpacket_length != client->packet_length)
184 goto exit;
185
186 client->splitpacket_length = 0;
187
188 smsnet_send_packet(client->splitpacket, client->packet_length);
189
190 buffer += rest;
191 length -= rest;
192 }
193
194 while (length > 0)
195 {
196 ip_ver = GETIPVERSION(*buffer);
197 while (length && (ip_ver != IPV4VERSION) && (ip_ver != IPV6VERSION))
198 {
199 buffer++;
200 length--;
201 ip_ver = GETIPVERSION(*buffer);
202 }
203
204 // No more data in section
205 if (!length)
206 break;
207
208 // Set the header length at start of packet according to the version
209 // no problem with the IP header cast, since we have at least 1 byte (we use only the first byte)
210 client->header_length = (ip_ver == IPV4VERSION) ? (((struct iphdr *) buffer)->ihl * 4) : (sizeof(struct ipv6hdr) + sizeof(struct udphdr));
211
212 // Check that Header length is at least 20 (min IPv4 length)
213 if (client->header_length < 20)
214 {
215 length--;
216 buffer++;
217 continue;
218 }
219
220 // check split header case
221 if (client->header_length > length)
222 {
223 memcpy(client->splitpacket, buffer, length);
224 client->splitheader_length = length;
225 break;
226 }
227
228 if (check_header(client, buffer))
229 {
230 // check split packet case
231 if (client->packet_length > length)
232 {
233 memcpy(client->splitpacket, buffer, length);
234 client->splitpacket_length = length;
235 break;
236 }
237 }
238 else
239 {
240 length --;
241 buffer ++;
242 continue;
243 }
244
245 smsnet_send_packet(buffer, client->packet_length);
246
247 buffer += client->packet_length;
248 length -= client->packet_length;
249 }
250
251exit:
252 smscore_putbuffer(client->coredev, cb);
253
254 return 0;
255}
256
257void smsnet_unregister_client(smsnet_client_t* client)
258{
259 // must be called under clientslock
260
261 list_del(&client->entry);
262
263 smscore_unregister_client(client->smsclient);
264 kfree(client);
265}
266
267void smsnet_onremove(void *context)
268{
269 kmutex_lock(&g_smsnet_clientslock);
270
271 smsnet_unregister_client((smsnet_client_t*) context);
272
273 kmutex_unlock(&g_smsnet_clientslock);
274}
275
276int smsnet_hotplug(smscore_device_t *coredev, struct device* device, int arrival)
277{
278 smsclient_params_t params;
279 smsnet_client_t* client;
280 int rc;
281
282 // device removal handled by onremove callback
283 if (!arrival)
284 return 0;
285
286 client = kzalloc(sizeof(smsnet_client_t), GFP_KERNEL);
287 if (!client)
288 {
289 printk(KERN_INFO "%s kmalloc() failed\n", __FUNCTION__);
290 return -ENOMEM;
291 }
292
293 params.initial_id = 0;
294 params.data_type = MSG_SMS_DATA_MSG;
295 params.onresponse_handler = smsnet_onresponse;
296 params.onremove_handler = smsnet_onremove;
297 params.context = client;
298
299 rc = smscore_register_client(coredev, &params, &client->smsclient);
300 if (rc < 0)
301 {
302 printk(KERN_INFO "%s smscore_register_client() failed %d\n", __FUNCTION__, rc);
303 kfree(client);
304 return rc;
305 }
306
307 client->coredev = coredev;
308
309 kmutex_lock(&g_smsnet_clientslock);
310
311 list_add(&client->entry, &g_smsnet_clients);
312
313 kmutex_unlock(&g_smsnet_clientslock);
314
315 printk(KERN_INFO "%s success\n", __FUNCTION__);
316
317 return 0;
318}
319
320static int smsnet_open(struct net_device *dev)
321{
322 g_smsnet_inuse ++;
323
324 netif_start_queue(dev);
325
326 printk(KERN_INFO "%s, %d\n", __FUNCTION__, g_smsnet_inuse);
327
328 return 0;
329}
330
331static int smsnet_stop(struct net_device *dev)
332{
333 netif_stop_queue(dev);
334
335 g_smsnet_inuse --;
336
337 printk(KERN_INFO "%s, %d\n", __FUNCTION__, g_smsnet_inuse);
338
339 return 0;
340}
341
342static int smsnet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
343{
344 printk(KERN_INFO "%s\n", __FUNCTION__);
345
346 dev_kfree_skb(skb);
347 return 0;
348}
349
350static struct net_device_stats * smsnet_get_stats(struct net_device *dev)
351{
352 return &g_smsnet_stats;
353}
354
355static void smsnet_set_multicast_list(struct net_device *dev)
356{
357 printk(KERN_INFO "%s %d\n", __FUNCTION__, dev->mc_count);
358 if (dev->mc_count)
359 {
360 struct dev_mc_list *p;
361
362 for (p = dev->mc_list; p; p = p->next)
363 printk(KERN_INFO "%s %d %02x %02x %02x %02x %02x %02x %02x %02x\n", __FUNCTION__, p->dmi_addrlen,
364 p->dmi_addr[0], p->dmi_addr[1], p->dmi_addr[2], p->dmi_addr[3],
365 p->dmi_addr[4], p->dmi_addr[5], p->dmi_addr[6], p->dmi_addr[7]
366 );
367 }
368}
369
370static void smsnet_setup_device(struct net_device *dev)
371{
372 ether_setup(dev);
373
374 dev->open = smsnet_open;
375 dev->stop = smsnet_stop;
376 dev->hard_start_xmit = smsnet_hard_start_xmit;
377 dev->get_stats = smsnet_get_stats;
378 dev->set_multicast_list = smsnet_set_multicast_list;
379
380 dev->mc_count = 0;
381 dev->hard_header_cache = NULL;
382
383 memcpy(dev->dev_addr, "\0SIANO", ETH_ALEN);
384
385 dev->flags |= IFF_NOARP;
386 dev->features |= NETIF_F_NO_CSUM;
387}
388
389int smsnet_module_init(void)
390{
391 int rc;
392
393 INIT_LIST_HEAD(&g_smsnet_clients);
394 kmutex_init(&g_smsnet_clientslock);
395
396 memset(&g_smsnet_stats, 0, sizeof(g_smsnet_stats));
397
398 g_smsnet_device = alloc_netdev(0, "sms", smsnet_setup_device);
399 if (!g_smsnet_device)
400 {
401 printk(KERN_INFO "%s alloc_netdev() failed\n", __FUNCTION__);
402 return -ENOMEM;
403 }
404
405 rc = register_netdev(g_smsnet_device);
406 if (rc < 0)
407 {
408 printk(KERN_INFO "%s register_netdev() failed %d\n", __FUNCTION__, rc);
409 free_netdev(g_smsnet_device);
410 return rc;
411 }
412
413 rc = smscore_register_hotplug(smsnet_hotplug);
414
415 printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
416
417 return rc;
418}
419
420void smsnet_module_exit(void)
421{
422 if (g_smsnet_device)
423 {
424 unregister_netdev(g_smsnet_device);
425 free_netdev(g_smsnet_device);
426
427 g_smsnet_device = NULL;
428 }
429
430 smscore_unregister_hotplug(smsnet_hotplug);
431
432 kmutex_lock(&g_smsnet_clientslock);
433
434 while (!list_empty(&g_smsnet_clients))
435 smsnet_unregister_client((smsnet_client_t*) g_smsnet_clients.next);
436
437 kmutex_unlock(&g_smsnet_clientslock);
438
439 printk(KERN_INFO "%s\n", __FUNCTION__);
440}
441
442module_init(smsnet_module_init);
443module_exit(smsnet_module_exit);
444
445MODULE_DESCRIPTION("smsnet dvb-h ip sink module");
446MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
447MODULE_LICENSE("GPL");
diff --git a/drivers/media/mdtv/smstypes.h b/drivers/media/mdtv/smstypes.h
new file mode 100644
index 000000000000..011cd904d0f1
--- /dev/null
+++ b/drivers/media/mdtv/smstypes.h
@@ -0,0 +1,361 @@
1#ifndef __smstypes_h__
2#define __smstypes_h__
3
4// GPIO definitions for antenna frequency domain control (SMS8021)
5#define SMS_ANTENNA_GPIO_0 1
6#define SMS_ANTENNA_GPIO_1 0
7
8#define BW_8_MHZ 0
9#define BW_7_MHZ 1
10#define BW_6_MHZ 2
11#define BW_5_MHZ 3
12#define BW_ISDBT_1SEG 4
13#define BW_ISDBT_3SEG 5
14
15#define MSG_HDR_FLAG_SPLIT_MSG 4
16
17#define MAX_GPIO_PIN_NUMBER 31
18
19#define HIF_TASK 11
20#define SMS_HOST_LIB 150
21#define DVBT_BDA_CONTROL_MSG_ID 201
22
23#define SMS_MAX_PAYLOAD_SIZE 240
24#define SMS_TUNE_TIMEOUT 500
25
26#define MSG_SMS_GPIO_CONFIG_REQ 507
27#define MSG_SMS_GPIO_CONFIG_RES 508
28#define MSG_SMS_GPIO_SET_LEVEL_REQ 509
29#define MSG_SMS_GPIO_SET_LEVEL_RES 510
30#define MSG_SMS_GPIO_GET_LEVEL_REQ 511
31#define MSG_SMS_GPIO_GET_LEVEL_RES 512
32#define MSG_SMS_RF_TUNE_REQ 561
33#define MSG_SMS_RF_TUNE_RES 562
34#define MSG_SMS_INIT_DEVICE_REQ 578
35#define MSG_SMS_INIT_DEVICE_RES 579
36#define MSG_SMS_ADD_PID_FILTER_REQ 601
37#define MSG_SMS_ADD_PID_FILTER_RES 602
38#define MSG_SMS_REMOVE_PID_FILTER_REQ 603
39#define MSG_SMS_REMOVE_PID_FILTER_RES 604
40#define MSG_SMS_DAB_CHANNEL 607
41#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608
42#define MSG_SMS_GET_PID_FILTER_LIST_RES 609
43#define MSG_SMS_GET_STATISTICS_REQ 615
44#define MSG_SMS_GET_STATISTICS_RES 616
45#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651
46#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652
47#define MSG_SMS_GET_STATISTICS_EX_REQ 653
48#define MSG_SMS_GET_STATISTICS_EX_RES 654
49#define MSG_SMS_SLEEP_RESUME_COMP_IND 655
50#define MSG_SMS_DATA_DOWNLOAD_REQ 660
51#define MSG_SMS_DATA_DOWNLOAD_RES 661
52#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664
53#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665
54#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666
55#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667
56#define MSG_SMS_GET_VERSION_EX_REQ 668
57#define MSG_SMS_GET_VERSION_EX_RES 669
58#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670
59#define MSG_SMS_I2C_SET_FREQ_REQ 685
60#define MSG_SMS_GENERIC_I2C_REQ 687
61#define MSG_SMS_GENERIC_I2C_RES 688
62#define MSG_SMS_DVBT_BDA_DATA 693
63#define MSG_SW_RELOAD_REQ 697
64#define MSG_SMS_DATA_MSG 699
65#define MSG_SW_RELOAD_START_REQ 702
66#define MSG_SW_RELOAD_START_RES 703
67#define MSG_SW_RELOAD_EXEC_REQ 704
68#define MSG_SW_RELOAD_EXEC_RES 705
69#define MSG_SMS_SPI_INT_LINE_SET_REQ 710
70#define MSG_SMS_ISDBT_TUNE_REQ 776
71#define MSG_SMS_ISDBT_TUNE_RES 777
72
73#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \
74 (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \
75 (ptr)->msgLength = len; (ptr)->msgFlags = 0; \
76} while (0)
77#define SMS_INIT_MSG(ptr, type, len) SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len)
78
79typedef enum
80{
81 DEVICE_MODE_NONE = -1,
82 DEVICE_MODE_DVBT = 0,
83 DEVICE_MODE_DVBH,
84 DEVICE_MODE_DAB_TDMB,
85 DEVICE_MODE_DAB_TDMB_DABIP,
86 DEVICE_MODE_DVBT_BDA,
87 DEVICE_MODE_ISDBT,
88 DEVICE_MODE_ISDBT_BDA,
89 DEVICE_MODE_CMMB,
90 DEVICE_MODE_RAW_TUNER,
91 DEVICE_MODE_MAX,
92} SMS_DEVICE_MODE;
93
94typedef unsigned char UINT8;
95typedef unsigned short UINT16;
96typedef unsigned int UINT32;
97typedef int INT32;
98
99typedef struct SmsMsgHdr_S
100{
101 UINT16 msgType;
102 UINT8 msgSrcId;
103 UINT8 msgDstId;
104 UINT16 msgLength; // Length is of the entire message, including header
105 UINT16 msgFlags;
106} SmsMsgHdr_ST;
107
108typedef struct SmsMsgData_S
109{
110 SmsMsgHdr_ST xMsgHeader;
111 UINT32 msgData[1];
112} SmsMsgData_ST;
113
114typedef struct SmsDataDownload_S
115{
116 SmsMsgHdr_ST xMsgHeader;
117 UINT32 MemAddr;
118 UINT8 Payload[SMS_MAX_PAYLOAD_SIZE];
119} SmsDataDownload_ST;
120
121typedef struct SmsVersionRes_S
122{
123 SmsMsgHdr_ST xMsgHeader;
124
125 UINT16 ChipModel; // e.g. 0x1102 for SMS-1102 "Nova"
126 UINT8 Step; // 0 - Step A
127 UINT8 MetalFix; // 0 - Metal 0
128
129 UINT8 FirmwareId; // 0xFF � ROM, otherwise the value indicated by SMSHOSTLIB_DEVICE_MODES_E
130 UINT8 SupportedProtocols; // Bitwise OR combination of supported protocols
131
132 UINT8 VersionMajor;
133 UINT8 VersionMinor;
134 UINT8 VersionPatch;
135 UINT8 VersionFieldPatch;
136
137 UINT8 RomVersionMajor;
138 UINT8 RomVersionMinor;
139 UINT8 RomVersionPatch;
140 UINT8 RomVersionFieldPatch;
141
142 UINT8 TextLabel[34];
143} SmsVersionRes_ST;
144
145typedef struct SmsFirmware_S
146{
147 UINT32 CheckSum;
148 UINT32 Length;
149 UINT32 StartAddress;
150 UINT8 Payload[1];
151} SmsFirmware_ST;
152
153typedef struct SMSHOSTLIB_STATISTICS_S
154{
155 UINT32 Reserved; //!< Reserved
156
157 /// Common parameters
158 UINT32 IsRfLocked; //!< 0 - not locked, 1 - locked
159 UINT32 IsDemodLocked; //!< 0 - not locked, 1 - locked
160 UINT32 IsExternalLNAOn; //!< 0 - external LNA off, 1 - external LNA on
161
162 /// Reception quality
163 INT32 SNR; //!< dB
164 UINT32 BER; //!< Post Viterbi BER [1E-5]
165 UINT32 FIB_CRC; //!< CRC errors percentage, valid only for DAB
166 UINT32 TS_PER; //!< Transport stream PER, 0xFFFFFFFF indicate N/A, valid only for DVB-T/H
167 UINT32 MFER; //!< DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H
168 INT32 RSSI; //!< dBm
169 INT32 InBandPwr; //!< In band power in dBM
170 INT32 CarrierOffset; //!< Carrier Offset in bin/1024
171
172 /// Transmission parameters
173 UINT32 Frequency; //!< Frequency in Hz
174 UINT32 Bandwidth; //!< Bandwidth in MHz, valid only for DVB-T/H
175 UINT32 TransmissionMode; //!< Transmission Mode, for DAB modes 1-4, for DVB-T/H FFT mode carriers in Kilos
176 UINT32 ModemState; //!< from SMS_DvbModemState_ET , valid only for DVB-T/H
177 UINT32 GuardInterval; //!< Guard Interval, 1 divided by value , valid only for DVB-T/H
178 UINT32 CodeRate; //!< Code Rate from SMS_DvbModemState_ET, valid only for DVB-T/H
179 UINT32 LPCodeRate; //!< Low Priority Code Rate from SMS_DvbModemState_ET, valid only for DVB-T/H
180 UINT32 Hierarchy; //!< Hierarchy from SMS_Hierarchy_ET, valid only for DVB-T/H
181 UINT32 Constellation; //!< Constellation from SMS_Constellation_ET, valid only for DVB-T/H
182
183 /// Burst parameters, valid only for DVB-H
184 UINT32 BurstSize; //!< Current burst size in bytes, valid only for DVB-H
185 UINT32 BurstDuration; //!< Current burst duration in mSec, valid only for DVB-H
186 UINT32 BurstCycleTime; //!< Current burst cycle time in mSec, valid only for DVB-H
187 UINT32 CalculatedBurstCycleTime;//!< Current burst cycle time in mSec, as calculated by demodulator, valid only for DVB-H
188 UINT32 NumOfRows; //!< Number of rows in MPE table, valid only for DVB-H
189 UINT32 NumOfPaddCols; //!< Number of padding columns in MPE table, valid only for DVB-H
190 UINT32 NumOfPunctCols; //!< Number of puncturing columns in MPE table, valid only for DVB-H
191 UINT32 ErrorTSPackets; //!< Number of erroneous transport-stream packets
192 UINT32 TotalTSPackets; //!< Total number of transport-stream packets
193 UINT32 NumOfValidMpeTlbs; //!< Number of MPE tables which do not include errors after MPE RS decoding
194 UINT32 NumOfInvalidMpeTlbs; //!< Number of MPE tables which include errors after MPE RS decoding
195 UINT32 NumOfCorrectedMpeTlbs; //!< Number of MPE tables which were corrected by MPE RS decoding
196 /// Common params
197 UINT32 BERErrorCount; //!< Number of errornous SYNC bits.
198 UINT32 BERBitCount; //!< Total number of SYNC bits.
199
200 /// Interface information
201 UINT32 SmsToHostTxErrors; //!< Total number of transmission errors.
202
203 /// DAB/T-DMB
204 UINT32 PreBER; //!< DAB/T-DMB only: Pre Viterbi BER [1E-5]
205
206 /// DVB-H TPS parameters
207 UINT32 CellId; //!< TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered
208
209} SMSHOSTLIB_STATISTICS_ST;
210
211typedef struct
212{
213 UINT32 RequestResult;
214
215 SMSHOSTLIB_STATISTICS_ST Stat;
216
217 // Split the calc of the SNR in DAB
218 UINT32 Signal; //!< dB
219 UINT32 Noise; //!< dB
220
221} SmsMsgStatisticsInfo_ST;
222
223typedef struct SMSHOSTLIB_ISDBT_LAYER_STAT_S
224{
225 // Per-layer information
226 UINT32 CodeRate; //!< Code Rate from SMSHOSTLIB_CODE_RATE_ET, 255 means layer does not exist
227 UINT32 Constellation; //!< Constellation from SMSHOSTLIB_CONSTELLATION_ET, 255 means layer does not exist
228 UINT32 BER; //!< Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A
229 UINT32 BERErrorCount; //!< Post Viterbi Error Bits Count
230 UINT32 BERBitCount; //!< Post Viterbi Total Bits Count
231 UINT32 PreBER; //!< Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A
232 UINT32 TS_PER; //!< Transport stream PER [%], 0xFFFFFFFF indicate N/A
233 UINT32 ErrorTSPackets; //!< Number of erroneous transport-stream packets
234 UINT32 TotalTSPackets; //!< Total number of transport-stream packets
235 UINT32 TILdepthI; //!< Time interleaver depth I parameter, 255 means layer does not exist
236 UINT32 NumberOfSegments; //!< Number of segments in layer A, 255 means layer does not exist
237 UINT32 TMCCErrors; //!< TMCC errors
238} SMSHOSTLIB_ISDBT_LAYER_STAT_ST;
239
240typedef struct SMSHOSTLIB_STATISTICS_ISDBT_S
241{
242 UINT32 StatisticsType; //!< Enumerator identifying the type of the structure. Values are the same as SMSHOSTLIB_DEVICE_MODES_E
243 //!< This fiels MUST always first in any statistics structure
244
245 UINT32 FullSize; //!< Total size of the structure returned by the modem. If the size requested by
246 //!< the host is smaller than FullSize, the struct will be truncated
247
248 // Common parameters
249 UINT32 IsRfLocked; //!< 0 - not locked, 1 - locked
250 UINT32 IsDemodLocked; //!< 0 - not locked, 1 - locked
251 UINT32 IsExternalLNAOn; //!< 0 - external LNA off, 1 - external LNA on
252
253 // Reception quality
254 INT32 SNR; //!< dB
255 INT32 RSSI; //!< dBm
256 INT32 InBandPwr; //!< In band power in dBM
257 INT32 CarrierOffset; //!< Carrier Offset in Hz
258
259 // Transmission parameters
260 UINT32 Frequency; //!< Frequency in Hz
261 UINT32 Bandwidth; //!< Bandwidth in MHz
262 UINT32 TransmissionMode; //!< ISDB-T transmission mode
263 UINT32 ModemState; //!< 0 - Acquisition, 1 - Locked
264 UINT32 GuardInterval; //!< Guard Interval, 1 divided by value
265 UINT32 SystemType; //!< ISDB-T system type (ISDB-T / ISDB-Tsb)
266 UINT32 PartialReception; //!< TRUE - partial reception, FALSE otherwise
267 UINT32 NumOfLayers; //!< Number of ISDB-T layers in the network
268
269 // Per-layer information
270 // Layers A, B and C
271 SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; //!< Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST
272
273 // Interface information
274 UINT32 SmsToHostTxErrors; //!< Total number of transmission errors.
275
276} SMSHOSTLIB_STATISTICS_ISDBT_ST;
277
278typedef struct SMSHOSTLIB_STATISTICS_DVB_S
279{
280 UINT32 StatisticsType; //!< Enumerator identifying the type of the structure. Values are the same as SMSHOSTLIB_DEVICE_MODES_E
281 //!< This fiels MUST always first in any statistics structure
282
283 UINT32 FullSize; //!< Total size of the structure returned by the modem. If the size requested by
284 //!< the host is smaller than FullSize, the struct will be truncated
285 // Common parameters
286 UINT32 IsRfLocked; //!< 0 - not locked, 1 - locked
287 UINT32 IsDemodLocked; //!< 0 - not locked, 1 - locked
288 UINT32 IsExternalLNAOn; //!< 0 - external LNA off, 1 - external LNA on
289
290 // Reception quality
291 INT32 SNR; //!< dB
292 UINT32 BER; //!< Post Viterbi BER [1E-5]
293 UINT32 BERErrorCount; //!< Number of errornous SYNC bits.
294 UINT32 BERBitCount; //!< Total number of SYNC bits.
295 UINT32 TS_PER; //!< Transport stream PER, 0xFFFFFFFF indicate N/A
296 UINT32 MFER; //!< DVB-H frame error rate in percentage, 0xFFFFFFFF indicate N/A, valid only for DVB-H
297 INT32 RSSI; //!< dBm
298 INT32 InBandPwr; //!< In band power in dBM
299 INT32 CarrierOffset; //!< Carrier Offset in bin/1024
300
301 // Transmission parameters
302 UINT32 Frequency; //!< Frequency in Hz
303 UINT32 Bandwidth; //!< Bandwidth in MHz
304 UINT32 ModemState; //!< from SMSHOSTLIB_DVB_MODEM_STATE_ET
305 UINT32 TransmissionMode; //!< FFT mode carriers in Kilos
306 UINT32 GuardInterval; //!< Guard Interval, 1 divided by value
307 UINT32 CodeRate; //!< Code Rate from SMSHOSTLIB_CODE_RATE_ET
308 UINT32 LPCodeRate; //!< Low Priority Code Rate from SMSHOSTLIB_CODE_RATE_ET
309 UINT32 Hierarchy; //!< Hierarchy from SMSHOSTLIB_HIERARCHY_ET
310 UINT32 Constellation; //!< Constellation from SMSHOSTLIB_CONSTELLATION_ET
311
312 // Burst parameters, valid only for DVB-H
313 UINT32 BurstSize; //!< Current burst size in bytes, valid only for DVB-H
314 UINT32 BurstDuration; //!< Current burst duration in mSec, valid only for DVB-H
315 UINT32 BurstCycleTime; //!< Current burst cycle time in mSec, valid only for DVB-H
316 UINT32 CalculatedBurstCycleTime;//!< Current burst cycle time in mSec, as calculated by demodulator, valid only for DVB-H
317 UINT32 NumOfRows; //!< Number of rows in MPE table, valid only for DVB-H
318 UINT32 NumOfPaddCols; //!< Number of padding columns in MPE table, valid only for DVB-H
319 UINT32 NumOfPunctCols; //!< Number of puncturing columns in MPE table, valid only for DVB-H
320 UINT32 ErrorTSPackets; //!< Number of erroneous transport-stream packets
321 UINT32 TotalTSPackets; //!< Total number of transport-stream packets
322 UINT32 NumOfValidMpeTlbs; //!< Number of MPE tables which do not include errors after MPE RS decoding, valid only for DVB-H
323 UINT32 NumOfInvalidMpeTlbs; //!< Number of MPE tables which include errors after MPE RS decoding, valid only for DVB-H
324 UINT32 NumOfCorrectedMpeTlbs; //!< Number of MPE tables which were corrected by MPE RS decoding, valid only for DVB-H
325 UINT32 NumMPEReceived; //!< DVB-H, Num MPE section received
326
327 // DVB-H TPS parameters
328 UINT32 CellId; //!< TPS Cell ID in bits 15..0, bits 31..16 zero; if set to 0xFFFFFFFF cell_id not yet recovered
329 UINT32 DvbhSrvIndHP; //!< DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator
330 UINT32 DvbhSrvIndLP; //!< DVB-H service indication info, bit 1 - Time Slicing indicator, bit 0 - MPE-FEC indicator
331
332 // Interface information
333 UINT32 SmsToHostTxErrors; //!< Total number of transmission errors.
334
335} SMSHOSTLIB_STATISTICS_DVB_ST;
336
337typedef struct SMSHOSTLIB_GPIO_CONFIG_S
338{
339 UINT8 Direction; //!< GPIO direction: Input - 0, Output - 1
340 UINT8 PullUpDown; //!< PullUp/PullDown: None - 0, PullDown - 1, PullUp - 2, Keeper - 3
341 UINT8 InputCharacteristics; //!< Input Characteristics: Normal - 0, Schmitt trigger - 1
342 UINT8 OutputSlewRate; //!< Output Slew Rate: Fast slew rate - 0, Slow slew rate - 1
343 UINT8 OutputDriving; //!< Output driving capability: 4mA - 0, 8mA - 1, 12mA - 2, 16mA - 3
344} SMSHOSTLIB_GPIO_CONFIG_ST;
345
346typedef struct SMSHOSTLIB_I2C_REQ_S
347{
348 UINT32 DeviceAddress; // I2c device address
349 UINT32 WriteCount; // number of bytes to write
350 UINT32 ReadCount; // number of bytes to read
351 UINT8 Data[1];
352} SMSHOSTLIB_I2C_REQ_ST;
353
354typedef struct SMSHOSTLIB_I2C_RES_S
355{
356 UINT32 Status; // non-zero value in case of failure
357 UINT32 ReadCount; // number of bytes read
358 UINT8 Data[1];
359} SMSHOSTLIB_I2C_RES_ST;
360
361#endif // __smstypes_h__
diff --git a/drivers/media/mdtv/smsusb.c b/drivers/media/mdtv/smsusb.c
new file mode 100644
index 000000000000..43af826b3832
--- /dev/null
+++ b/drivers/media/mdtv/smsusb.c
@@ -0,0 +1,428 @@
1#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/module.h>
4#include <linux/usb.h>
5#include <linux/firmware.h>
6
7#include "smskdefs.h" // page, scatterlist, kmutex
8#include "smscoreapi.h"
9#include "smstypes.h"
10
11#define USB_VID_SIANO 0x187f
12#define USB_PID_0010 0x0010
13#define USB_PID_0100 0x0100
14#define USB_PID_0200 0x0200
15
16#define USB1_BUFFER_SIZE 0x1000
17#define USB2_BUFFER_SIZE 0x4000
18
19#define MAX_BUFFERS 50
20#define MAX_URBS 10
21
22typedef struct _smsusb_device smsusb_device_t;
23
24typedef struct _smsusb_urb
25{
26 smscore_buffer_t *cb;
27 smsusb_device_t *dev;
28
29 struct urb urb;
30} smsusb_urb_t;
31
32typedef struct _smsusb_device
33{
34 struct usb_device* udev;
35 smscore_device_t *coredev;
36
37 smsusb_urb_t surbs[MAX_URBS];
38
39 int response_alignment;
40 int buffer_size;
41} *psmsusb_device_t;
42
43static struct usb_device_id smsusb_id_table [] = {
44 { USB_DEVICE(USB_VID_SIANO, USB_PID_0010) },
45 { USB_DEVICE(USB_VID_SIANO, USB_PID_0100) },
46 { USB_DEVICE(USB_VID_SIANO, USB_PID_0200) },
47 { } /* Terminating entry */
48};
49MODULE_DEVICE_TABLE (usb, smsusb_id_table);
50
51int smsusb_submit_urb(smsusb_device_t* dev, smsusb_urb_t* surb);
52
53void smsusb_onresponse(struct urb *urb)
54{
55 smsusb_urb_t *surb = (smsusb_urb_t *) urb->context;
56 smsusb_device_t *dev = surb->dev;
57
58 if (urb->status < 0)
59 {
60 printk(KERN_INFO "%s error, urb status %d, %d bytes\n", __FUNCTION__, urb->status, urb->actual_length);
61 return;
62 }
63
64 if (urb->actual_length > 0)
65 {
66 SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *) surb->cb->p;
67
68 if (urb->actual_length >= phdr->msgLength)
69 {
70 surb->cb->size = phdr->msgLength;
71
72 if (dev->response_alignment && (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG))
73 {
74 surb->cb->offset = dev->response_alignment + ((phdr->msgFlags >> 8) & 3);
75
76 // sanity check
77 if (((int) phdr->msgLength + surb->cb->offset) > urb->actual_length)
78 {
79 printk("%s: invalid response msglen %d offset %d size %d\n", __FUNCTION__, phdr->msgLength, surb->cb->offset, urb->actual_length);
80 goto exit_and_resubmit;
81 }
82
83 // move buffer pointer and copy header to its new location
84 memcpy((char*) phdr + surb->cb->offset, phdr, sizeof(SmsMsgHdr_ST));
85 }
86 else
87 surb->cb->offset = 0;
88
89 smscore_onresponse(dev->coredev, surb->cb);
90 surb->cb = NULL;
91 }
92 else
93 {
94 printk("%s invalid response msglen %d actual %d\n", __FUNCTION__, phdr->msgLength, urb->actual_length);
95 }
96 }
97
98exit_and_resubmit:
99 smsusb_submit_urb(dev, surb);
100}
101
102int smsusb_submit_urb(smsusb_device_t* dev, smsusb_urb_t* surb)
103{
104 if (!surb->cb)
105 {
106 surb->cb = smscore_getbuffer(dev->coredev);
107 if (!surb->cb)
108 {
109 printk(KERN_INFO "%s smscore_getbuffer(...) returned NULL\n", __FUNCTION__);
110 return -ENOMEM;
111 }
112 }
113
114 usb_fill_bulk_urb(
115 &surb->urb,
116 dev->udev,
117 usb_rcvbulkpipe(dev->udev, 0x81),
118 surb->cb->p,
119 dev->buffer_size,
120 smsusb_onresponse,
121 surb
122 );
123 surb->urb.transfer_dma = surb->cb->phys;
124 surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
125
126 return usb_submit_urb(&surb->urb, GFP_ATOMIC);
127}
128
129void smsusb_stop_streaming(smsusb_device_t* dev)
130{
131 int i;
132
133 for (i = 0; i < MAX_URBS; i ++)
134 {
135 usb_kill_urb(&dev->surbs[i].urb);
136
137 if (dev->surbs[i].cb)
138 {
139 smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
140 dev->surbs[i].cb = NULL;
141 }
142 }
143}
144
145int smsusb_start_streaming(smsusb_device_t* dev)
146{
147 int i, rc;
148
149 for (i = 0; i < MAX_URBS; i ++)
150 {
151 rc = smsusb_submit_urb(dev, &dev->surbs[i]);
152 if (rc < 0)
153 {
154 printk(KERN_INFO "%s smsusb_submit_urb(...) failed\n", __FUNCTION__);
155 smsusb_stop_streaming(dev);
156 break;
157 }
158 }
159
160 return rc;
161}
162
163int smsusb_sendrequest(void *context, void *buffer, size_t size)
164{
165 smsusb_device_t* dev = (smsusb_device_t*) context;
166 int dummy;
167
168 return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), buffer, size, &dummy, 1000);
169}
170
171char *smsusb1_fw_lkup[] =
172{
173 "dvbt_stellar_usb.inp",
174 "dvbh_stellar_usb.inp",
175 "tdmb_stellar_usb.inp",
176 "none",
177 "dvbt_bda_stellar_usb.inp",
178};
179
180int smsusb1_load_firmware(struct usb_device *udev, int id)
181{
182 const struct firmware *fw;
183 u8* fw_buffer;
184 int rc, dummy;
185
186 if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA)
187 {
188 printk(KERN_INFO "%s invalid firmware id specified %d\n", __FUNCTION__, id);
189 return -EINVAL;
190 }
191
192 rc = request_firmware(&fw, smsusb1_fw_lkup[id], &udev->dev);
193 if (rc < 0)
194 {
195 printk(KERN_INFO "%s failed to open \"%s\" mode %d\n", __FUNCTION__, smsusb1_fw_lkup[id], id);
196 return rc;
197 }
198
199 fw_buffer = kmalloc(fw->size, GFP_KERNEL);
200 if (fw_buffer)
201 {
202 memcpy(fw_buffer, fw->data, fw->size);
203
204 rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), fw_buffer, fw->size, &dummy, 1000);
205
206 printk(KERN_INFO "%s: sent %d(%d) bytes, rc %d\n", __FUNCTION__, fw->size, dummy, rc);
207
208 kfree(fw_buffer);
209 }
210 else
211 {
212 printk(KERN_INFO "failed to allocate firmware buffer\n");
213 rc = -ENOMEM;
214 }
215
216 release_firmware(fw);
217
218 return rc;
219}
220
221void smsusb1_detectmode(void *context, int *mode)
222{
223 char *product_string = ((smsusb_device_t *) context)->udev->product;
224
225 *mode = DEVICE_MODE_NONE;
226
227 if (!product_string)
228 {
229 product_string = "none";
230 printk("%s product string not found\n", __FUNCTION__);
231 }
232 else
233 {
234 if (strstr(product_string, "DVBH"))
235 *mode = 1;
236 else if (strstr(product_string, "BDA"))
237 *mode = 4;
238 else if (strstr(product_string, "DVBT"))
239 *mode = 0;
240 else if (strstr(product_string, "TDMB"))
241 *mode = 2;
242 }
243
244 printk("%s: %d \"%s\"\n", __FUNCTION__, *mode, product_string);
245}
246
247int smsusb1_setmode(void *context, int mode)
248{
249 SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, sizeof(SmsMsgHdr_ST), 0 };
250
251 if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA)
252 {
253 printk(KERN_INFO "%s invalid firmware id specified %d\n", __FUNCTION__, mode);
254 return -EINVAL;
255 }
256
257 return smsusb_sendrequest(context, &Msg, sizeof(Msg));
258}
259
260void smsusb_term_device(struct usb_interface *intf)
261{
262 smsusb_device_t *dev = (smsusb_device_t*) usb_get_intfdata(intf);
263
264 if (dev)
265 {
266 smsusb_stop_streaming(dev);
267
268 // unregister from smscore
269 if (dev->coredev)
270 smscore_unregister_device(dev->coredev);
271
272 kfree(dev);
273
274 printk(KERN_INFO "%s device %p destroyed\n", __FUNCTION__, dev);
275 }
276
277 usb_set_intfdata(intf, NULL);
278}
279
280int smsusb_init_device(struct usb_interface *intf)
281{
282 smsdevice_params_t params;
283 smsusb_device_t* dev;
284 int i, rc;
285
286 // create device object
287 dev = kzalloc(sizeof(smsusb_device_t), GFP_KERNEL);
288 if (!dev)
289 {
290 printk(KERN_INFO "%s kzalloc(sizeof(smsusb_device_t) failed\n", __FUNCTION__);
291 return -ENOMEM;
292 }
293
294 memset(&params, 0, sizeof(params));
295 usb_set_intfdata(intf, dev);
296 dev->udev = interface_to_usbdev(intf);
297
298 switch (dev->udev->descriptor.idProduct)
299 {
300 case USB_PID_0100:
301 dev->buffer_size = USB1_BUFFER_SIZE;
302
303 params.setmode_handler = smsusb1_setmode;
304 params.detectmode_handler = smsusb1_detectmode;
305 break;
306
307 default:
308 dev->buffer_size = USB2_BUFFER_SIZE;
309 dev->response_alignment = dev->udev->ep_in[1]->desc.wMaxPacketSize - sizeof(SmsMsgHdr_ST);
310
311 params.flags |= SMS_DEVICE_FAMILY2;
312 break;
313 }
314
315 params.device = &dev->udev->dev;
316 params.buffer_size = dev->buffer_size;
317 params.num_buffers = MAX_BUFFERS;
318 params.sendrequest_handler = smsusb_sendrequest;
319 params.context = dev;
320 snprintf(params.devpath, sizeof(params.devpath), "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath);
321
322 // register in smscore
323 rc = smscore_register_device(&params, &dev->coredev);
324 if (rc < 0)
325 {
326 printk(KERN_INFO "%s smscore_register_device(...) failed, rc %d\n", __FUNCTION__, rc);
327 smsusb_term_device(intf);
328 return rc;
329 }
330
331 // initialize urbs
332 for (i = 0; i < MAX_URBS; i ++)
333 {
334 dev->surbs[i].dev = dev;
335 usb_init_urb(&dev->surbs[i].urb);
336 }
337
338 rc = smsusb_start_streaming(dev);
339 if (rc < 0)
340 {
341 printk(KERN_INFO "%s smsusb_start_streaming(...) failed\n", __FUNCTION__);
342 smsusb_term_device(intf);
343 return rc;
344 }
345
346 rc = smscore_start_device(dev->coredev);
347 if (rc < 0)
348 {
349 printk(KERN_INFO "%s smscore_start_device(...) failed\n", __FUNCTION__);
350 smsusb_term_device(intf);
351 return rc;
352 }
353
354 printk(KERN_INFO "%s device %p created\n", __FUNCTION__, dev);
355
356 return rc;
357}
358
359int smsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
360{
361 struct usb_device *udev = interface_to_usbdev(intf);
362 char devpath[32];
363 int i, rc;
364
365 if (intf->num_altsetting > 0)
366 {
367 rc = usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
368 if (rc < 0)
369 {
370 printk(KERN_INFO "%s usb_set_interface failed, rc %d\n", __FUNCTION__, rc);
371 return rc;
372 }
373 }
374
375 printk(KERN_INFO "smsusb_probe %d\n", intf->cur_altsetting->desc.bInterfaceNumber);
376 for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i ++)
377 printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, intf->cur_altsetting->endpoint[i].desc.bmAttributes, intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
378
379 if (udev->actconfig->desc.bNumInterfaces == 2 && intf->cur_altsetting->desc.bInterfaceNumber == 0)
380 {
381 printk(KERN_INFO "rom interface 0 is not used\n");
382 return -ENODEV;
383 }
384
385 if (intf->cur_altsetting->desc.bInterfaceNumber == 1)
386 {
387 snprintf(devpath, 32, "%d:%s", udev->bus->busnum, udev->devpath);
388 return smsusb1_load_firmware(udev, smscore_registry_getmode(devpath));
389 }
390
391 return smsusb_init_device(intf);
392}
393
394void smsusb_disconnect(struct usb_interface *intf)
395{
396 smsusb_term_device(intf);
397}
398
399static struct usb_driver smsusb_driver = {
400 .name = "smsusb",
401 .probe = smsusb_probe,
402 .disconnect = smsusb_disconnect,
403 .id_table = smsusb_id_table,
404};
405
406int smsusb_module_init(void)
407{
408 int rc = usb_register(&smsusb_driver);
409 if (rc)
410 printk(KERN_INFO "usb_register failed. Error number %d\n", rc);
411
412 printk(KERN_INFO "%s\n", __FUNCTION__);
413
414 return rc;
415}
416
417void smsusb_module_exit(void)
418{
419 usb_deregister(&smsusb_driver);
420 printk(KERN_INFO "%s\n", __FUNCTION__);
421}
422
423module_init(smsusb_module_init);
424module_exit(smsusb_module_exit);
425
426MODULE_DESCRIPTION("smsusb");
427MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
428MODULE_LICENSE("GPL");