diff options
author | Michael Krufky <mkrufky@linuxtv.org> | 2008-05-19 17:56:13 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-07-20 06:19:35 -0400 |
commit | 2e5c1ec8865abd81e24a394918c7ba315e0b7b70 (patch) | |
tree | cd241de9b5eeddc27455b07e3530f69bc3e7d95d | |
parent | a9e285856112e5e721b6a341d15437a164128b30 (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/Kconfig | 14 | ||||
-rw-r--r-- | drivers/media/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/mdtv/Kconfig | 36 | ||||
-rw-r--r-- | drivers/media/mdtv/Makefile | 8 | ||||
-rw-r--r-- | drivers/media/mdtv/smschar.c | 575 | ||||
-rw-r--r-- | drivers/media/mdtv/smschar.h | 7 | ||||
-rw-r--r-- | drivers/media/mdtv/smscharioctl.h | 17 | ||||
-rw-r--r-- | drivers/media/mdtv/smscoreapi.c | 1170 | ||||
-rw-r--r-- | drivers/media/mdtv/smscoreapi.h | 101 | ||||
-rw-r--r-- | drivers/media/mdtv/smsdvb.c | 438 | ||||
-rw-r--r-- | drivers/media/mdtv/smskdefs.h | 21 | ||||
-rw-r--r-- | drivers/media/mdtv/smsnet.c | 447 | ||||
-rw-r--r-- | drivers/media/mdtv/smstypes.h | 361 | ||||
-rw-r--r-- | drivers/media/mdtv/smsusb.c | 428 |
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 | ||
116 | source "drivers/media/dvb/Kconfig" | 116 | source "drivers/media/dvb/Kconfig" |
117 | 117 | ||
118 | # | ||
119 | # Mobile Digital TV devices (DVB-H, T-DMB, etc.) | ||
120 | # | ||
121 | menuconfig MDTV_ADAPTERS | ||
122 | bool "Mobile Digital TV adapter" | ||
123 | default y | ||
124 | |||
125 | if MDTV_ADAPTERS | ||
126 | |||
127 | source "drivers/media/mdtv/Kconfig" | ||
128 | |||
129 | endif # MDTV_ADAPTERS | ||
130 | |||
131 | |||
118 | config DAB | 132 | config 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 | ||
7 | obj-$(CONFIG_VIDEO_DEV) += radio/ | 7 | obj-$(CONFIG_VIDEO_DEV) += radio/ |
8 | obj-$(CONFIG_DVB_CORE) += dvb/ | 8 | obj-$(CONFIG_DVB_CORE) += dvb/ |
9 | obj-$(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 | |||
5 | config 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 | |||
19 | config 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 | |||
5 | obj-$(CONFIG_MDTV_SIANO_STELLAR_COMMON) += smschar.o smsnet.o | ||
6 | obj-$(CONFIG_MDTV_SIANO_STELLAR_USB) += smsusb.o | ||
7 | |||
8 | EXTRA_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 | |||
42 | typedef 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. | ||
60 | int smschar_major = 251; | ||
61 | |||
62 | //! Holds the first minor number of the device node. may be changed at load time. | ||
63 | int smschar_minor = 0; | ||
64 | |||
65 | // macros that allow the load time parameters change | ||
66 | module_param ( smschar_major, int, S_IRUGO ); | ||
67 | module_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 | |||
86 | smschar_device_t smschar_devices[SMSCHAR_NR_DEVS]; | ||
87 | static 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 | */ | ||
95 | void 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 | */ | ||
137 | int 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 | */ | ||
174 | void 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 | */ | ||
190 | int 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, ¶ms, &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 | */ | ||
226 | int 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 | */ | ||
245 | ssize_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 | */ | ||
309 | ssize_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 | |||
336 | int 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 | */ | ||
351 | int 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 | |||
404 | int 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 | |||
458 | struct 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 | |||
469 | static 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 | */ | ||
497 | int 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 | |||
545 | int 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 | |||
569 | void 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 | |||
4 | extern int smschar_initialize(void); | ||
5 | extern 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 | |||
6 | typedef 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 | |||
39 | typedef struct _smscore_device_notifyee | ||
40 | { | ||
41 | struct list_head entry; | ||
42 | hotplug_t hotplug; | ||
43 | } smscore_device_notifyee_t; | ||
44 | |||
45 | typedef 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 | |||
58 | typedef struct _smscore_subclient | ||
59 | { | ||
60 | struct list_head entry; | ||
61 | smscore_client_t *client; | ||
62 | |||
63 | int id; | ||
64 | } smscore_subclient_t; | ||
65 | |||
66 | typedef 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 | |||
100 | typedef struct _smscore_registry_entry | ||
101 | { | ||
102 | struct list_head entry; | ||
103 | char devpath[32]; | ||
104 | int mode; | ||
105 | } smscore_registry_entry_t; | ||
106 | |||
107 | struct list_head g_smscore_notifyees; | ||
108 | struct list_head g_smscore_devices; | ||
109 | kmutex_t g_smscore_deviceslock; | ||
110 | |||
111 | struct list_head g_smscore_registry; | ||
112 | kmutex_t g_smscore_registrylock; | ||
113 | |||
114 | static int default_mode = 1; | ||
115 | module_param(default_mode, int, 0644); | ||
116 | MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); | ||
117 | |||
118 | int 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 | |||
150 | void 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 | |||
172 | void 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 | */ | ||
191 | int 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(¬ifyee->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 | */ | ||
232 | void 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(¬ifyee->entry); | ||
248 | kfree(notifyee); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | kmutex_unlock(&g_smscore_deviceslock); | ||
253 | } | ||
254 | |||
255 | void 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 | |||
267 | int 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 | |||
286 | smscore_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 | */ | ||
311 | int 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 | */ | ||
398 | int 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 | |||
415 | int 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 | |||
424 | int 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 | */ | ||
519 | int 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 | */ | ||
565 | void 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 | |||
606 | int 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 | |||
639 | char *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 | */ | ||
661 | int 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 | */ | ||
740 | int smscore_get_device_mode(smscore_device_t *coredev) | ||
741 | { | ||
742 | return coredev->mode; | ||
743 | } | ||
744 | |||
745 | smscore_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 | |||
772 | smscore_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 | */ | ||
804 | void 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 | */ | ||
891 | smscore_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 | */ | ||
916 | void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb) | ||
917 | { | ||
918 | list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock); | ||
919 | } | ||
920 | |||
921 | int 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 | */ | ||
961 | int 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 | */ | ||
1003 | void 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 | */ | ||
1043 | int 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 | */ | ||
1063 | int 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 | */ | ||
1076 | int 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 | |||
1101 | int 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 | |||
1119 | void 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(¬ifyee->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 | |||
1146 | module_init(smscore_module_init); | ||
1147 | module_exit(smscore_module_exit); | ||
1148 | |||
1149 | MODULE_DESCRIPTION("smscore"); | ||
1150 | MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)"); | ||
1151 | MODULE_LICENSE("GPL"); | ||
1152 | |||
1153 | EXPORT_SYMBOL(smscore_registry_setmode); | ||
1154 | EXPORT_SYMBOL(smscore_registry_getmode); | ||
1155 | EXPORT_SYMBOL(smscore_register_hotplug); | ||
1156 | EXPORT_SYMBOL(smscore_unregister_hotplug); | ||
1157 | EXPORT_SYMBOL(smscore_register_device); | ||
1158 | EXPORT_SYMBOL(smscore_unregister_device); | ||
1159 | EXPORT_SYMBOL(smscore_start_device); | ||
1160 | EXPORT_SYMBOL(smscore_load_firmware); | ||
1161 | EXPORT_SYMBOL(smscore_set_device_mode); | ||
1162 | EXPORT_SYMBOL(smscore_get_device_mode); | ||
1163 | EXPORT_SYMBOL(smscore_register_client); | ||
1164 | EXPORT_SYMBOL(smscore_unregister_client); | ||
1165 | EXPORT_SYMBOL(smsclient_sendrequest); | ||
1166 | EXPORT_SYMBOL(smscore_onresponse); | ||
1167 | EXPORT_SYMBOL(smscore_get_common_buffer_size); | ||
1168 | EXPORT_SYMBOL(smscore_map_common_buffer); | ||
1169 | EXPORT_SYMBOL(smscore_getbuffer); | ||
1170 | EXPORT_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 | |||
16 | typedef struct _smscore_device smscore_device_t; | ||
17 | typedef struct _smscore_client smscore_client_t; | ||
18 | typedef struct _smscore_buffer smscore_buffer_t; | ||
19 | |||
20 | typedef int (*hotplug_t)(smscore_device_t *coredev, struct device *device, int arrival); | ||
21 | |||
22 | typedef int (*setmode_t)(void *context, int mode); | ||
23 | typedef void (*detectmode_t)(void *context, int *mode); | ||
24 | typedef int (*sendrequest_t)(void *context, void *buffer, size_t size); | ||
25 | typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size); | ||
26 | typedef int (*preload_t)(void *context); | ||
27 | typedef int (*postload_t)(void *context); | ||
28 | |||
29 | typedef int (*onresponse_t)(void *context, smscore_buffer_t *cb); | ||
30 | typedef void (*onremove_t)(void *context); | ||
31 | |||
32 | typedef 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 | |||
45 | typedef 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 | |||
64 | typedef 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 | |||
74 | extern void smscore_registry_setmode(char *devpath, int mode); | ||
75 | extern int smscore_registry_getmode(char *devpath); | ||
76 | |||
77 | extern int smscore_register_hotplug(hotplug_t hotplug); | ||
78 | extern void smscore_unregister_hotplug(hotplug_t hotplug); | ||
79 | |||
80 | extern int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev); | ||
81 | extern void smscore_unregister_device(smscore_device_t *coredev); | ||
82 | |||
83 | extern int smscore_start_device(smscore_device_t *coredev); | ||
84 | extern int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler); | ||
85 | |||
86 | extern int smscore_set_device_mode(smscore_device_t *coredev, int mode); | ||
87 | extern int smscore_get_device_mode(smscore_device_t *coredev); | ||
88 | |||
89 | extern int smscore_register_client(smscore_device_t *coredev, smsclient_params_t* params, smscore_client_t **client); | ||
90 | extern void smscore_unregister_client(smscore_client_t *client); | ||
91 | |||
92 | extern int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size); | ||
93 | extern void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb); | ||
94 | |||
95 | extern int smscore_get_common_buffer_size(smscore_device_t *coredev); | ||
96 | extern int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma); | ||
97 | |||
98 | extern smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev); | ||
99 | extern 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 | |||
13 | typedef 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 | |||
35 | struct list_head g_smsdvb_clients; | ||
36 | kmutex_t g_smsdvb_clientslock; | ||
37 | |||
38 | int 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 | |||
88 | void 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 | |||
102 | void 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 | |||
111 | static 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 | |||
128 | static 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 | |||
145 | static 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 | |||
154 | static 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 | |||
160 | static 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 | |||
171 | static 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 | |||
182 | static 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 | |||
193 | static 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 | |||
204 | static 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 | |||
214 | static 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 | |||
247 | static 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 | |||
258 | static void smsdvb_release(struct dvb_frontend *fe) | ||
259 | { | ||
260 | // do nothing | ||
261 | } | ||
262 | |||
263 | static 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 | |||
292 | int 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, ¶ms, &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 | |||
388 | client_error: | ||
389 | dvb_unregister_frontend(&client->frontend); | ||
390 | |||
391 | frontend_error: | ||
392 | dvb_dmxdev_release(&client->dmxdev); | ||
393 | |||
394 | dmxdev_error: | ||
395 | dvb_dmx_release(&client->demux); | ||
396 | |||
397 | dvbdmx_error: | ||
398 | dvb_unregister_adapter(&client->adapter); | ||
399 | |||
400 | adapter_error: | ||
401 | kfree(client); | ||
402 | return rc; | ||
403 | } | ||
404 | |||
405 | int 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 | |||
419 | void 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 | |||
433 | module_init(smsdvb_module_init); | ||
434 | module_exit(smsdvb_module_exit); | ||
435 | |||
436 | MODULE_DESCRIPTION("smsdvb dvb-api module"); | ||
437 | MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)"); | ||
438 | MODULE_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 | |||
13 | typedef 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 | |||
17 | typedef 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 | |||
29 | struct list_head g_smsnet_clients; | ||
30 | kmutex_t g_smsnet_clientslock; | ||
31 | |||
32 | struct net_device *g_smsnet_device = NULL; | ||
33 | struct net_device_stats g_smsnet_stats; | ||
34 | |||
35 | int g_smsnet_inuse = 0; | ||
36 | |||
37 | void 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 | |||
90 | int 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 | |||
136 | int 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 | |||
251 | exit: | ||
252 | smscore_putbuffer(client->coredev, cb); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | void 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 | |||
267 | void 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 | |||
276 | int 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, ¶ms, &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 | |||
320 | static 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 | |||
331 | static 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 | |||
342 | static 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 | |||
350 | static struct net_device_stats * smsnet_get_stats(struct net_device *dev) | ||
351 | { | ||
352 | return &g_smsnet_stats; | ||
353 | } | ||
354 | |||
355 | static 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 | |||
370 | static 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 | |||
389 | int 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 | |||
420 | void 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 | |||
442 | module_init(smsnet_module_init); | ||
443 | module_exit(smsnet_module_exit); | ||
444 | |||
445 | MODULE_DESCRIPTION("smsnet dvb-h ip sink module"); | ||
446 | MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)"); | ||
447 | MODULE_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 | |||
79 | typedef 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 | |||
94 | typedef unsigned char UINT8; | ||
95 | typedef unsigned short UINT16; | ||
96 | typedef unsigned int UINT32; | ||
97 | typedef int INT32; | ||
98 | |||
99 | typedef 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 | |||
108 | typedef struct SmsMsgData_S | ||
109 | { | ||
110 | SmsMsgHdr_ST xMsgHeader; | ||
111 | UINT32 msgData[1]; | ||
112 | } SmsMsgData_ST; | ||
113 | |||
114 | typedef struct SmsDataDownload_S | ||
115 | { | ||
116 | SmsMsgHdr_ST xMsgHeader; | ||
117 | UINT32 MemAddr; | ||
118 | UINT8 Payload[SMS_MAX_PAYLOAD_SIZE]; | ||
119 | } SmsDataDownload_ST; | ||
120 | |||
121 | typedef 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 | |||
145 | typedef struct SmsFirmware_S | ||
146 | { | ||
147 | UINT32 CheckSum; | ||
148 | UINT32 Length; | ||
149 | UINT32 StartAddress; | ||
150 | UINT8 Payload[1]; | ||
151 | } SmsFirmware_ST; | ||
152 | |||
153 | typedef 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 | |||
211 | typedef 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 | |||
223 | typedef 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 | |||
240 | typedef 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 | |||
278 | typedef 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 | |||
337 | typedef 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 | |||
346 | typedef 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 | |||
354 | typedef 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 | |||
22 | typedef struct _smsusb_device smsusb_device_t; | ||
23 | |||
24 | typedef struct _smsusb_urb | ||
25 | { | ||
26 | smscore_buffer_t *cb; | ||
27 | smsusb_device_t *dev; | ||
28 | |||
29 | struct urb urb; | ||
30 | } smsusb_urb_t; | ||
31 | |||
32 | typedef 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 | |||
43 | static 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 | }; | ||
49 | MODULE_DEVICE_TABLE (usb, smsusb_id_table); | ||
50 | |||
51 | int smsusb_submit_urb(smsusb_device_t* dev, smsusb_urb_t* surb); | ||
52 | |||
53 | void 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 | |||
98 | exit_and_resubmit: | ||
99 | smsusb_submit_urb(dev, surb); | ||
100 | } | ||
101 | |||
102 | int 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 | |||
129 | void 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 | |||
145 | int 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 | |||
163 | int 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 | |||
171 | char *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 | |||
180 | int 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 | |||
221 | void 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 | |||
247 | int 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 | |||
260 | void 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 | |||
280 | int 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(¶ms, 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(¶ms, &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 | |||
359 | int 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 | |||
394 | void smsusb_disconnect(struct usb_interface *intf) | ||
395 | { | ||
396 | smsusb_term_device(intf); | ||
397 | } | ||
398 | |||
399 | static struct usb_driver smsusb_driver = { | ||
400 | .name = "smsusb", | ||
401 | .probe = smsusb_probe, | ||
402 | .disconnect = smsusb_disconnect, | ||
403 | .id_table = smsusb_id_table, | ||
404 | }; | ||
405 | |||
406 | int 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 | |||
417 | void smsusb_module_exit(void) | ||
418 | { | ||
419 | usb_deregister(&smsusb_driver); | ||
420 | printk(KERN_INFO "%s\n", __FUNCTION__); | ||
421 | } | ||
422 | |||
423 | module_init(smsusb_module_init); | ||
424 | module_exit(smsusb_module_exit); | ||
425 | |||
426 | MODULE_DESCRIPTION("smsusb"); | ||
427 | MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)"); | ||
428 | MODULE_LICENSE("GPL"); | ||