diff options
Diffstat (limited to 'drivers/media/dvb/siano/smscoreapi.c')
-rw-r--r-- | drivers/media/dvb/siano/smscoreapi.c | 1644 |
1 files changed, 1644 insertions, 0 deletions
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c new file mode 100644 index 00000000000..7331e8450d1 --- /dev/null +++ b/drivers/media/dvb/siano/smscoreapi.c | |||
@@ -0,0 +1,1644 @@ | |||
1 | /* | ||
2 | * Siano core API module | ||
3 | * | ||
4 | * This file contains implementation for the interface to sms core component | ||
5 | * | ||
6 | * author: Uri Shkolnik | ||
7 | * | ||
8 | * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation; | ||
13 | * | ||
14 | * Software distributed under the License is distributed on an "AS IS" | ||
15 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. | ||
16 | * | ||
17 | * See the GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/dma-mapping.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/slab.h> | ||
32 | |||
33 | #include <linux/firmware.h> | ||
34 | #include <linux/wait.h> | ||
35 | #include <asm/byteorder.h> | ||
36 | |||
37 | #include "smscoreapi.h" | ||
38 | #include "sms-cards.h" | ||
39 | #include "smsir.h" | ||
40 | #include "smsendian.h" | ||
41 | |||
42 | static int sms_dbg; | ||
43 | module_param_named(debug, sms_dbg, int, 0644); | ||
44 | MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); | ||
45 | |||
46 | struct smscore_device_notifyee_t { | ||
47 | struct list_head entry; | ||
48 | hotplug_t hotplug; | ||
49 | }; | ||
50 | |||
51 | struct smscore_idlist_t { | ||
52 | struct list_head entry; | ||
53 | int id; | ||
54 | int data_type; | ||
55 | }; | ||
56 | |||
57 | struct smscore_client_t { | ||
58 | struct list_head entry; | ||
59 | struct smscore_device_t *coredev; | ||
60 | void *context; | ||
61 | struct list_head idlist; | ||
62 | onresponse_t onresponse_handler; | ||
63 | onremove_t onremove_handler; | ||
64 | }; | ||
65 | |||
66 | void smscore_set_board_id(struct smscore_device_t *core, int id) | ||
67 | { | ||
68 | core->board_id = id; | ||
69 | } | ||
70 | |||
71 | int smscore_led_state(struct smscore_device_t *core, int led) | ||
72 | { | ||
73 | if (led >= 0) | ||
74 | core->led_state = led; | ||
75 | return core->led_state; | ||
76 | } | ||
77 | EXPORT_SYMBOL_GPL(smscore_set_board_id); | ||
78 | |||
79 | int smscore_get_board_id(struct smscore_device_t *core) | ||
80 | { | ||
81 | return core->board_id; | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(smscore_get_board_id); | ||
84 | |||
85 | struct smscore_registry_entry_t { | ||
86 | struct list_head entry; | ||
87 | char devpath[32]; | ||
88 | int mode; | ||
89 | enum sms_device_type_st type; | ||
90 | }; | ||
91 | |||
92 | static struct list_head g_smscore_notifyees; | ||
93 | static struct list_head g_smscore_devices; | ||
94 | static struct mutex g_smscore_deviceslock; | ||
95 | |||
96 | static struct list_head g_smscore_registry; | ||
97 | static struct mutex g_smscore_registrylock; | ||
98 | |||
99 | static int default_mode = 4; | ||
100 | |||
101 | module_param(default_mode, int, 0644); | ||
102 | MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); | ||
103 | |||
104 | static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) | ||
105 | { | ||
106 | struct smscore_registry_entry_t *entry; | ||
107 | struct list_head *next; | ||
108 | |||
109 | kmutex_lock(&g_smscore_registrylock); | ||
110 | for (next = g_smscore_registry.next; | ||
111 | next != &g_smscore_registry; | ||
112 | next = next->next) { | ||
113 | entry = (struct smscore_registry_entry_t *) next; | ||
114 | if (!strcmp(entry->devpath, devpath)) { | ||
115 | kmutex_unlock(&g_smscore_registrylock); | ||
116 | return entry; | ||
117 | } | ||
118 | } | ||
119 | entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); | ||
120 | if (entry) { | ||
121 | entry->mode = default_mode; | ||
122 | strcpy(entry->devpath, devpath); | ||
123 | list_add(&entry->entry, &g_smscore_registry); | ||
124 | } else | ||
125 | sms_err("failed to create smscore_registry."); | ||
126 | kmutex_unlock(&g_smscore_registrylock); | ||
127 | return entry; | ||
128 | } | ||
129 | |||
130 | int smscore_registry_getmode(char *devpath) | ||
131 | { | ||
132 | struct smscore_registry_entry_t *entry; | ||
133 | |||
134 | entry = smscore_find_registry(devpath); | ||
135 | if (entry) | ||
136 | return entry->mode; | ||
137 | else | ||
138 | sms_err("No registry found."); | ||
139 | |||
140 | return default_mode; | ||
141 | } | ||
142 | EXPORT_SYMBOL_GPL(smscore_registry_getmode); | ||
143 | |||
144 | static enum sms_device_type_st smscore_registry_gettype(char *devpath) | ||
145 | { | ||
146 | struct smscore_registry_entry_t *entry; | ||
147 | |||
148 | entry = smscore_find_registry(devpath); | ||
149 | if (entry) | ||
150 | return entry->type; | ||
151 | else | ||
152 | sms_err("No registry found."); | ||
153 | |||
154 | return -1; | ||
155 | } | ||
156 | |||
157 | void smscore_registry_setmode(char *devpath, int mode) | ||
158 | { | ||
159 | struct smscore_registry_entry_t *entry; | ||
160 | |||
161 | entry = smscore_find_registry(devpath); | ||
162 | if (entry) | ||
163 | entry->mode = mode; | ||
164 | else | ||
165 | sms_err("No registry found."); | ||
166 | } | ||
167 | |||
168 | static void smscore_registry_settype(char *devpath, | ||
169 | enum sms_device_type_st type) | ||
170 | { | ||
171 | struct smscore_registry_entry_t *entry; | ||
172 | |||
173 | entry = smscore_find_registry(devpath); | ||
174 | if (entry) | ||
175 | entry->type = type; | ||
176 | else | ||
177 | sms_err("No registry found."); | ||
178 | } | ||
179 | |||
180 | |||
181 | static void list_add_locked(struct list_head *new, struct list_head *head, | ||
182 | spinlock_t *lock) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | |||
186 | spin_lock_irqsave(lock, flags); | ||
187 | |||
188 | list_add(new, head); | ||
189 | |||
190 | spin_unlock_irqrestore(lock, flags); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * register a client callback that called when device plugged in/unplugged | ||
195 | * NOTE: if devices exist callback is called immediately for each device | ||
196 | * | ||
197 | * @param hotplug callback | ||
198 | * | ||
199 | * @return 0 on success, <0 on error. | ||
200 | */ | ||
201 | int smscore_register_hotplug(hotplug_t hotplug) | ||
202 | { | ||
203 | struct smscore_device_notifyee_t *notifyee; | ||
204 | struct list_head *next, *first; | ||
205 | int rc = 0; | ||
206 | |||
207 | kmutex_lock(&g_smscore_deviceslock); | ||
208 | |||
209 | notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), | ||
210 | GFP_KERNEL); | ||
211 | if (notifyee) { | ||
212 | /* now notify callback about existing devices */ | ||
213 | first = &g_smscore_devices; | ||
214 | for (next = first->next; | ||
215 | next != first && !rc; | ||
216 | next = next->next) { | ||
217 | struct smscore_device_t *coredev = | ||
218 | (struct smscore_device_t *) next; | ||
219 | rc = hotplug(coredev, coredev->device, 1); | ||
220 | } | ||
221 | |||
222 | if (rc >= 0) { | ||
223 | notifyee->hotplug = hotplug; | ||
224 | list_add(¬ifyee->entry, &g_smscore_notifyees); | ||
225 | } else | ||
226 | kfree(notifyee); | ||
227 | } else | ||
228 | rc = -ENOMEM; | ||
229 | |||
230 | kmutex_unlock(&g_smscore_deviceslock); | ||
231 | |||
232 | return rc; | ||
233 | } | ||
234 | EXPORT_SYMBOL_GPL(smscore_register_hotplug); | ||
235 | |||
236 | /** | ||
237 | * unregister a client callback that called when device plugged in/unplugged | ||
238 | * | ||
239 | * @param hotplug callback | ||
240 | * | ||
241 | */ | ||
242 | void smscore_unregister_hotplug(hotplug_t hotplug) | ||
243 | { | ||
244 | struct list_head *next, *first; | ||
245 | |||
246 | kmutex_lock(&g_smscore_deviceslock); | ||
247 | |||
248 | first = &g_smscore_notifyees; | ||
249 | |||
250 | for (next = first->next; next != first;) { | ||
251 | struct smscore_device_notifyee_t *notifyee = | ||
252 | (struct smscore_device_notifyee_t *) next; | ||
253 | next = next->next; | ||
254 | |||
255 | if (notifyee->hotplug == hotplug) { | ||
256 | list_del(¬ifyee->entry); | ||
257 | kfree(notifyee); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | kmutex_unlock(&g_smscore_deviceslock); | ||
262 | } | ||
263 | EXPORT_SYMBOL_GPL(smscore_unregister_hotplug); | ||
264 | |||
265 | static void smscore_notify_clients(struct smscore_device_t *coredev) | ||
266 | { | ||
267 | struct smscore_client_t *client; | ||
268 | |||
269 | /* the client must call smscore_unregister_client from remove handler */ | ||
270 | while (!list_empty(&coredev->clients)) { | ||
271 | client = (struct smscore_client_t *) coredev->clients.next; | ||
272 | client->onremove_handler(client->context); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | static int smscore_notify_callbacks(struct smscore_device_t *coredev, | ||
277 | struct device *device, int arrival) | ||
278 | { | ||
279 | struct list_head *next, *first; | ||
280 | int rc = 0; | ||
281 | |||
282 | /* note: must be called under g_deviceslock */ | ||
283 | |||
284 | first = &g_smscore_notifyees; | ||
285 | |||
286 | for (next = first->next; next != first; next = next->next) { | ||
287 | rc = ((struct smscore_device_notifyee_t *) next)-> | ||
288 | hotplug(coredev, device, arrival); | ||
289 | if (rc < 0) | ||
290 | break; | ||
291 | } | ||
292 | |||
293 | return rc; | ||
294 | } | ||
295 | |||
296 | static struct | ||
297 | smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, | ||
298 | dma_addr_t common_buffer_phys) | ||
299 | { | ||
300 | struct smscore_buffer_t *cb = | ||
301 | kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); | ||
302 | if (!cb) { | ||
303 | sms_info("kmalloc(...) failed"); | ||
304 | return NULL; | ||
305 | } | ||
306 | |||
307 | cb->p = buffer; | ||
308 | cb->offset_in_common = buffer - (u8 *) common_buffer; | ||
309 | cb->phys = common_buffer_phys + cb->offset_in_common; | ||
310 | |||
311 | return cb; | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * creates coredev object for a device, prepares buffers, | ||
316 | * creates buffer mappings, notifies registered hotplugs about new device. | ||
317 | * | ||
318 | * @param params device pointer to struct with device specific parameters | ||
319 | * and handlers | ||
320 | * @param coredev pointer to a value that receives created coredev object | ||
321 | * | ||
322 | * @return 0 on success, <0 on error. | ||
323 | */ | ||
324 | int smscore_register_device(struct smsdevice_params_t *params, | ||
325 | struct smscore_device_t **coredev) | ||
326 | { | ||
327 | struct smscore_device_t *dev; | ||
328 | u8 *buffer; | ||
329 | |||
330 | dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); | ||
331 | if (!dev) { | ||
332 | sms_info("kzalloc(...) failed"); | ||
333 | return -ENOMEM; | ||
334 | } | ||
335 | |||
336 | /* init list entry so it could be safe in smscore_unregister_device */ | ||
337 | INIT_LIST_HEAD(&dev->entry); | ||
338 | |||
339 | /* init queues */ | ||
340 | INIT_LIST_HEAD(&dev->clients); | ||
341 | INIT_LIST_HEAD(&dev->buffers); | ||
342 | |||
343 | /* init locks */ | ||
344 | spin_lock_init(&dev->clientslock); | ||
345 | spin_lock_init(&dev->bufferslock); | ||
346 | |||
347 | /* init completion events */ | ||
348 | init_completion(&dev->version_ex_done); | ||
349 | init_completion(&dev->data_download_done); | ||
350 | init_completion(&dev->trigger_done); | ||
351 | init_completion(&dev->init_device_done); | ||
352 | init_completion(&dev->reload_start_done); | ||
353 | init_completion(&dev->resume_done); | ||
354 | init_completion(&dev->gpio_configuration_done); | ||
355 | init_completion(&dev->gpio_set_level_done); | ||
356 | init_completion(&dev->gpio_get_level_done); | ||
357 | init_completion(&dev->ir_init_done); | ||
358 | |||
359 | /* Buffer management */ | ||
360 | init_waitqueue_head(&dev->buffer_mng_waitq); | ||
361 | |||
362 | /* alloc common buffer */ | ||
363 | dev->common_buffer_size = params->buffer_size * params->num_buffers; | ||
364 | dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, | ||
365 | &dev->common_buffer_phys, | ||
366 | GFP_KERNEL | GFP_DMA); | ||
367 | if (!dev->common_buffer) { | ||
368 | smscore_unregister_device(dev); | ||
369 | return -ENOMEM; | ||
370 | } | ||
371 | |||
372 | /* prepare dma buffers */ | ||
373 | for (buffer = dev->common_buffer; | ||
374 | dev->num_buffers < params->num_buffers; | ||
375 | dev->num_buffers++, buffer += params->buffer_size) { | ||
376 | struct smscore_buffer_t *cb = | ||
377 | smscore_createbuffer(buffer, dev->common_buffer, | ||
378 | dev->common_buffer_phys); | ||
379 | if (!cb) { | ||
380 | smscore_unregister_device(dev); | ||
381 | return -ENOMEM; | ||
382 | } | ||
383 | |||
384 | smscore_putbuffer(dev, cb); | ||
385 | } | ||
386 | |||
387 | sms_info("allocated %d buffers", dev->num_buffers); | ||
388 | |||
389 | dev->mode = DEVICE_MODE_NONE; | ||
390 | dev->context = params->context; | ||
391 | dev->device = params->device; | ||
392 | dev->setmode_handler = params->setmode_handler; | ||
393 | dev->detectmode_handler = params->detectmode_handler; | ||
394 | dev->sendrequest_handler = params->sendrequest_handler; | ||
395 | dev->preload_handler = params->preload_handler; | ||
396 | dev->postload_handler = params->postload_handler; | ||
397 | |||
398 | dev->device_flags = params->flags; | ||
399 | strcpy(dev->devpath, params->devpath); | ||
400 | |||
401 | smscore_registry_settype(dev->devpath, params->device_type); | ||
402 | |||
403 | /* add device to devices list */ | ||
404 | kmutex_lock(&g_smscore_deviceslock); | ||
405 | list_add(&dev->entry, &g_smscore_devices); | ||
406 | kmutex_unlock(&g_smscore_deviceslock); | ||
407 | |||
408 | *coredev = dev; | ||
409 | |||
410 | sms_info("device %p created", dev); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | EXPORT_SYMBOL_GPL(smscore_register_device); | ||
415 | |||
416 | |||
417 | static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev, | ||
418 | void *buffer, size_t size, struct completion *completion) { | ||
419 | int rc = coredev->sendrequest_handler(coredev->context, buffer, size); | ||
420 | if (rc < 0) { | ||
421 | sms_info("sendrequest returned error %d", rc); | ||
422 | return rc; | ||
423 | } | ||
424 | |||
425 | return wait_for_completion_timeout(completion, | ||
426 | msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ? | ||
427 | 0 : -ETIME; | ||
428 | } | ||
429 | |||
430 | /** | ||
431 | * Starts & enables IR operations | ||
432 | * | ||
433 | * @return 0 on success, < 0 on error. | ||
434 | */ | ||
435 | static int smscore_init_ir(struct smscore_device_t *coredev) | ||
436 | { | ||
437 | int ir_io; | ||
438 | int rc; | ||
439 | void *buffer; | ||
440 | |||
441 | coredev->ir.dev = NULL; | ||
442 | ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir; | ||
443 | if (ir_io) {/* only if IR port exist we use IR sub-module */ | ||
444 | sms_info("IR loading"); | ||
445 | rc = sms_ir_init(coredev); | ||
446 | |||
447 | if (rc != 0) | ||
448 | sms_err("Error initialization DTV IR sub-module"); | ||
449 | else { | ||
450 | buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + | ||
451 | SMS_DMA_ALIGNMENT, | ||
452 | GFP_KERNEL | GFP_DMA); | ||
453 | if (buffer) { | ||
454 | struct SmsMsgData_ST2 *msg = | ||
455 | (struct SmsMsgData_ST2 *) | ||
456 | SMS_ALIGN_ADDRESS(buffer); | ||
457 | |||
458 | SMS_INIT_MSG(&msg->xMsgHeader, | ||
459 | MSG_SMS_START_IR_REQ, | ||
460 | sizeof(struct SmsMsgData_ST2)); | ||
461 | msg->msgData[0] = coredev->ir.controller; | ||
462 | msg->msgData[1] = coredev->ir.timeout; | ||
463 | |||
464 | smsendian_handle_tx_message( | ||
465 | (struct SmsMsgHdr_ST2 *)msg); | ||
466 | rc = smscore_sendrequest_and_wait(coredev, msg, | ||
467 | msg->xMsgHeader. msgLength, | ||
468 | &coredev->ir_init_done); | ||
469 | |||
470 | kfree(buffer); | ||
471 | } else | ||
472 | sms_err | ||
473 | ("Sending IR initialization message failed"); | ||
474 | } | ||
475 | } else | ||
476 | sms_info("IR port has not been detected"); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | /** | ||
482 | * sets initial device mode and notifies client hotplugs that device is ready | ||
483 | * | ||
484 | * @param coredev pointer to a coredev object returned by | ||
485 | * smscore_register_device | ||
486 | * | ||
487 | * @return 0 on success, <0 on error. | ||
488 | */ | ||
489 | int smscore_start_device(struct smscore_device_t *coredev) | ||
490 | { | ||
491 | int rc = smscore_set_device_mode( | ||
492 | coredev, smscore_registry_getmode(coredev->devpath)); | ||
493 | if (rc < 0) { | ||
494 | sms_info("set device mode faile , rc %d", rc); | ||
495 | return rc; | ||
496 | } | ||
497 | |||
498 | kmutex_lock(&g_smscore_deviceslock); | ||
499 | |||
500 | rc = smscore_notify_callbacks(coredev, coredev->device, 1); | ||
501 | smscore_init_ir(coredev); | ||
502 | |||
503 | sms_info("device %p started, rc %d", coredev, rc); | ||
504 | |||
505 | kmutex_unlock(&g_smscore_deviceslock); | ||
506 | |||
507 | return rc; | ||
508 | } | ||
509 | EXPORT_SYMBOL_GPL(smscore_start_device); | ||
510 | |||
511 | |||
512 | static int smscore_load_firmware_family2(struct smscore_device_t *coredev, | ||
513 | void *buffer, size_t size) | ||
514 | { | ||
515 | struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; | ||
516 | struct SmsMsgHdr_ST *msg; | ||
517 | u32 mem_address; | ||
518 | u8 *payload = firmware->Payload; | ||
519 | int rc = 0; | ||
520 | firmware->StartAddress = le32_to_cpu(firmware->StartAddress); | ||
521 | firmware->Length = le32_to_cpu(firmware->Length); | ||
522 | |||
523 | mem_address = firmware->StartAddress; | ||
524 | |||
525 | sms_info("loading FW to addr 0x%x size %d", | ||
526 | mem_address, firmware->Length); | ||
527 | if (coredev->preload_handler) { | ||
528 | rc = coredev->preload_handler(coredev->context); | ||
529 | if (rc < 0) | ||
530 | return rc; | ||
531 | } | ||
532 | |||
533 | /* PAGE_SIZE buffer shall be enough and dma aligned */ | ||
534 | msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); | ||
535 | if (!msg) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | if (coredev->mode != DEVICE_MODE_NONE) { | ||
539 | sms_debug("sending reload command."); | ||
540 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, | ||
541 | sizeof(struct SmsMsgHdr_ST)); | ||
542 | rc = smscore_sendrequest_and_wait(coredev, msg, | ||
543 | msg->msgLength, | ||
544 | &coredev->reload_start_done); | ||
545 | mem_address = *(u32 *) &payload[20]; | ||
546 | } | ||
547 | |||
548 | while (size && rc >= 0) { | ||
549 | struct SmsDataDownload_ST *DataMsg = | ||
550 | (struct SmsDataDownload_ST *) msg; | ||
551 | int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); | ||
552 | |||
553 | SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, | ||
554 | (u16)(sizeof(struct SmsMsgHdr_ST) + | ||
555 | sizeof(u32) + payload_size)); | ||
556 | |||
557 | DataMsg->MemAddr = mem_address; | ||
558 | memcpy(DataMsg->Payload, payload, payload_size); | ||
559 | |||
560 | if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && | ||
561 | (coredev->mode == DEVICE_MODE_NONE)) | ||
562 | rc = coredev->sendrequest_handler( | ||
563 | coredev->context, DataMsg, | ||
564 | DataMsg->xMsgHeader.msgLength); | ||
565 | else | ||
566 | rc = smscore_sendrequest_and_wait( | ||
567 | coredev, DataMsg, | ||
568 | DataMsg->xMsgHeader.msgLength, | ||
569 | &coredev->data_download_done); | ||
570 | |||
571 | payload += payload_size; | ||
572 | size -= payload_size; | ||
573 | mem_address += payload_size; | ||
574 | } | ||
575 | |||
576 | if (rc >= 0) { | ||
577 | if (coredev->mode == DEVICE_MODE_NONE) { | ||
578 | struct SmsMsgData_ST *TriggerMsg = | ||
579 | (struct SmsMsgData_ST *) msg; | ||
580 | |||
581 | SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, | ||
582 | sizeof(struct SmsMsgHdr_ST) + | ||
583 | sizeof(u32) * 5); | ||
584 | |||
585 | TriggerMsg->msgData[0] = firmware->StartAddress; | ||
586 | /* Entry point */ | ||
587 | TriggerMsg->msgData[1] = 5; /* Priority */ | ||
588 | TriggerMsg->msgData[2] = 0x200; /* Stack size */ | ||
589 | TriggerMsg->msgData[3] = 0; /* Parameter */ | ||
590 | TriggerMsg->msgData[4] = 4; /* Task ID */ | ||
591 | |||
592 | if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { | ||
593 | rc = coredev->sendrequest_handler( | ||
594 | coredev->context, TriggerMsg, | ||
595 | TriggerMsg->xMsgHeader.msgLength); | ||
596 | msleep(100); | ||
597 | } else | ||
598 | rc = smscore_sendrequest_and_wait( | ||
599 | coredev, TriggerMsg, | ||
600 | TriggerMsg->xMsgHeader.msgLength, | ||
601 | &coredev->trigger_done); | ||
602 | } else { | ||
603 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, | ||
604 | sizeof(struct SmsMsgHdr_ST)); | ||
605 | |||
606 | rc = coredev->sendrequest_handler(coredev->context, | ||
607 | msg, msg->msgLength); | ||
608 | } | ||
609 | msleep(500); | ||
610 | } | ||
611 | |||
612 | sms_debug("rc=%d, postload=%p ", rc, | ||
613 | coredev->postload_handler); | ||
614 | |||
615 | kfree(msg); | ||
616 | |||
617 | return ((rc >= 0) && coredev->postload_handler) ? | ||
618 | coredev->postload_handler(coredev->context) : | ||
619 | rc; | ||
620 | } | ||
621 | |||
622 | /** | ||
623 | * loads specified firmware into a buffer and calls device loadfirmware_handler | ||
624 | * | ||
625 | * @param coredev pointer to a coredev object returned by | ||
626 | * smscore_register_device | ||
627 | * @param filename null-terminated string specifies firmware file name | ||
628 | * @param loadfirmware_handler device handler that loads firmware | ||
629 | * | ||
630 | * @return 0 on success, <0 on error. | ||
631 | */ | ||
632 | static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, | ||
633 | char *filename, | ||
634 | loadfirmware_t loadfirmware_handler) | ||
635 | { | ||
636 | int rc = -ENOENT; | ||
637 | const struct firmware *fw; | ||
638 | u8 *fw_buffer; | ||
639 | |||
640 | if (loadfirmware_handler == NULL && !(coredev->device_flags & | ||
641 | SMS_DEVICE_FAMILY2)) | ||
642 | return -EINVAL; | ||
643 | |||
644 | rc = request_firmware(&fw, filename, coredev->device); | ||
645 | if (rc < 0) { | ||
646 | sms_info("failed to open \"%s\"", filename); | ||
647 | return rc; | ||
648 | } | ||
649 | sms_info("read FW %s, size=%zd", filename, fw->size); | ||
650 | fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), | ||
651 | GFP_KERNEL | GFP_DMA); | ||
652 | if (fw_buffer) { | ||
653 | memcpy(fw_buffer, fw->data, fw->size); | ||
654 | |||
655 | rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? | ||
656 | smscore_load_firmware_family2(coredev, | ||
657 | fw_buffer, | ||
658 | fw->size) : | ||
659 | loadfirmware_handler(coredev->context, | ||
660 | fw_buffer, fw->size); | ||
661 | |||
662 | kfree(fw_buffer); | ||
663 | } else { | ||
664 | sms_info("failed to allocate firmware buffer"); | ||
665 | rc = -ENOMEM; | ||
666 | } | ||
667 | |||
668 | release_firmware(fw); | ||
669 | |||
670 | return rc; | ||
671 | } | ||
672 | |||
673 | /** | ||
674 | * notifies all clients registered with the device, notifies hotplugs, | ||
675 | * frees all buffers and coredev object | ||
676 | * | ||
677 | * @param coredev pointer to a coredev object returned by | ||
678 | * smscore_register_device | ||
679 | * | ||
680 | * @return 0 on success, <0 on error. | ||
681 | */ | ||
682 | void smscore_unregister_device(struct smscore_device_t *coredev) | ||
683 | { | ||
684 | struct smscore_buffer_t *cb; | ||
685 | int num_buffers = 0; | ||
686 | int retry = 0; | ||
687 | |||
688 | kmutex_lock(&g_smscore_deviceslock); | ||
689 | |||
690 | /* Release input device (IR) resources */ | ||
691 | sms_ir_exit(coredev); | ||
692 | |||
693 | smscore_notify_clients(coredev); | ||
694 | smscore_notify_callbacks(coredev, NULL, 0); | ||
695 | |||
696 | /* at this point all buffers should be back | ||
697 | * onresponse must no longer be called */ | ||
698 | |||
699 | while (1) { | ||
700 | while (!list_empty(&coredev->buffers)) { | ||
701 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | ||
702 | list_del(&cb->entry); | ||
703 | kfree(cb); | ||
704 | num_buffers++; | ||
705 | } | ||
706 | if (num_buffers == coredev->num_buffers) | ||
707 | break; | ||
708 | if (++retry > 10) { | ||
709 | sms_info("exiting although " | ||
710 | "not all buffers released."); | ||
711 | break; | ||
712 | } | ||
713 | |||
714 | sms_info("waiting for %d buffer(s)", | ||
715 | coredev->num_buffers - num_buffers); | ||
716 | msleep(100); | ||
717 | } | ||
718 | |||
719 | sms_info("freed %d buffers", num_buffers); | ||
720 | |||
721 | if (coredev->common_buffer) | ||
722 | dma_free_coherent(NULL, coredev->common_buffer_size, | ||
723 | coredev->common_buffer, coredev->common_buffer_phys); | ||
724 | |||
725 | if (coredev->fw_buf != NULL) | ||
726 | kfree(coredev->fw_buf); | ||
727 | |||
728 | list_del(&coredev->entry); | ||
729 | kfree(coredev); | ||
730 | |||
731 | kmutex_unlock(&g_smscore_deviceslock); | ||
732 | |||
733 | sms_info("device %p destroyed", coredev); | ||
734 | } | ||
735 | EXPORT_SYMBOL_GPL(smscore_unregister_device); | ||
736 | |||
737 | static int smscore_detect_mode(struct smscore_device_t *coredev) | ||
738 | { | ||
739 | void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, | ||
740 | GFP_KERNEL | GFP_DMA); | ||
741 | struct SmsMsgHdr_ST *msg = | ||
742 | (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); | ||
743 | int rc; | ||
744 | |||
745 | if (!buffer) | ||
746 | return -ENOMEM; | ||
747 | |||
748 | SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, | ||
749 | sizeof(struct SmsMsgHdr_ST)); | ||
750 | |||
751 | rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, | ||
752 | &coredev->version_ex_done); | ||
753 | if (rc == -ETIME) { | ||
754 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); | ||
755 | |||
756 | if (wait_for_completion_timeout(&coredev->resume_done, | ||
757 | msecs_to_jiffies(5000))) { | ||
758 | rc = smscore_sendrequest_and_wait( | ||
759 | coredev, msg, msg->msgLength, | ||
760 | &coredev->version_ex_done); | ||
761 | if (rc < 0) | ||
762 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " | ||
763 | "second try, rc %d", rc); | ||
764 | } else | ||
765 | rc = -ETIME; | ||
766 | } | ||
767 | |||
768 | kfree(buffer); | ||
769 | |||
770 | return rc; | ||
771 | } | ||
772 | |||
773 | static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { | ||
774 | /*Stellar NOVA A0 Nova B0 VEGA*/ | ||
775 | /*DVBT*/ | ||
776 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
777 | /*DVBH*/ | ||
778 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
779 | /*TDMB*/ | ||
780 | {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, | ||
781 | /*DABIP*/ | ||
782 | {"none", "none", "none", "none"}, | ||
783 | /*BDA*/ | ||
784 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
785 | /*ISDBT*/ | ||
786 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, | ||
787 | /*ISDBTBDA*/ | ||
788 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, | ||
789 | /*CMMB*/ | ||
790 | {"none", "none", "none", "cmmb_vega_12mhz.inp"} | ||
791 | }; | ||
792 | |||
793 | static inline char *sms_get_fw_name(struct smscore_device_t *coredev, | ||
794 | int mode, enum sms_device_type_st type) | ||
795 | { | ||
796 | char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; | ||
797 | return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * calls device handler to change mode of operation | ||
802 | * NOTE: stellar/usb may disconnect when changing mode | ||
803 | * | ||
804 | * @param coredev pointer to a coredev object returned by | ||
805 | * smscore_register_device | ||
806 | * @param mode requested mode of operation | ||
807 | * | ||
808 | * @return 0 on success, <0 on error. | ||
809 | */ | ||
810 | int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) | ||
811 | { | ||
812 | void *buffer; | ||
813 | int rc = 0; | ||
814 | enum sms_device_type_st type; | ||
815 | |||
816 | sms_debug("set device mode to %d", mode); | ||
817 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { | ||
818 | if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { | ||
819 | sms_err("invalid mode specified %d", mode); | ||
820 | return -EINVAL; | ||
821 | } | ||
822 | |||
823 | smscore_registry_setmode(coredev->devpath, mode); | ||
824 | |||
825 | if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) { | ||
826 | rc = smscore_detect_mode(coredev); | ||
827 | if (rc < 0) { | ||
828 | sms_err("mode detect failed %d", rc); | ||
829 | return rc; | ||
830 | } | ||
831 | } | ||
832 | |||
833 | if (coredev->mode == mode) { | ||
834 | sms_info("device mode %d already set", mode); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | if (!(coredev->modes_supported & (1 << mode))) { | ||
839 | char *fw_filename; | ||
840 | |||
841 | type = smscore_registry_gettype(coredev->devpath); | ||
842 | fw_filename = sms_get_fw_name(coredev, mode, type); | ||
843 | |||
844 | rc = smscore_load_firmware_from_file(coredev, | ||
845 | fw_filename, NULL); | ||
846 | if (rc < 0) { | ||
847 | sms_warn("error %d loading firmware: %s, " | ||
848 | "trying again with default firmware", | ||
849 | rc, fw_filename); | ||
850 | |||
851 | /* try again with the default firmware */ | ||
852 | fw_filename = smscore_fw_lkup[mode][type]; | ||
853 | rc = smscore_load_firmware_from_file(coredev, | ||
854 | fw_filename, NULL); | ||
855 | |||
856 | if (rc < 0) { | ||
857 | sms_warn("error %d loading " | ||
858 | "firmware: %s", rc, | ||
859 | fw_filename); | ||
860 | return rc; | ||
861 | } | ||
862 | } | ||
863 | sms_log("firmware download success: %s", fw_filename); | ||
864 | } else | ||
865 | sms_info("mode %d supported by running " | ||
866 | "firmware", mode); | ||
867 | |||
868 | buffer = kmalloc(sizeof(struct SmsMsgData_ST) + | ||
869 | SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); | ||
870 | if (buffer) { | ||
871 | struct SmsMsgData_ST *msg = | ||
872 | (struct SmsMsgData_ST *) | ||
873 | SMS_ALIGN_ADDRESS(buffer); | ||
874 | |||
875 | SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, | ||
876 | sizeof(struct SmsMsgData_ST)); | ||
877 | msg->msgData[0] = mode; | ||
878 | |||
879 | rc = smscore_sendrequest_and_wait( | ||
880 | coredev, msg, msg->xMsgHeader.msgLength, | ||
881 | &coredev->init_device_done); | ||
882 | |||
883 | kfree(buffer); | ||
884 | } else { | ||
885 | sms_err("Could not allocate buffer for " | ||
886 | "init device message."); | ||
887 | rc = -ENOMEM; | ||
888 | } | ||
889 | } else { | ||
890 | if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { | ||
891 | sms_err("invalid mode specified %d", mode); | ||
892 | return -EINVAL; | ||
893 | } | ||
894 | |||
895 | smscore_registry_setmode(coredev->devpath, mode); | ||
896 | |||
897 | if (coredev->detectmode_handler) | ||
898 | coredev->detectmode_handler(coredev->context, | ||
899 | &coredev->mode); | ||
900 | |||
901 | if (coredev->mode != mode && coredev->setmode_handler) | ||
902 | rc = coredev->setmode_handler(coredev->context, mode); | ||
903 | } | ||
904 | |||
905 | if (rc >= 0) { | ||
906 | coredev->mode = mode; | ||
907 | coredev->device_flags &= ~SMS_DEVICE_NOT_READY; | ||
908 | } | ||
909 | |||
910 | if (rc < 0) | ||
911 | sms_err("return error code %d.", rc); | ||
912 | return rc; | ||
913 | } | ||
914 | |||
915 | /** | ||
916 | * calls device handler to get current mode of operation | ||
917 | * | ||
918 | * @param coredev pointer to a coredev object returned by | ||
919 | * smscore_register_device | ||
920 | * | ||
921 | * @return current mode | ||
922 | */ | ||
923 | int smscore_get_device_mode(struct smscore_device_t *coredev) | ||
924 | { | ||
925 | return coredev->mode; | ||
926 | } | ||
927 | EXPORT_SYMBOL_GPL(smscore_get_device_mode); | ||
928 | |||
929 | /** | ||
930 | * find client by response id & type within the clients list. | ||
931 | * return client handle or NULL. | ||
932 | * | ||
933 | * @param coredev pointer to a coredev object returned by | ||
934 | * smscore_register_device | ||
935 | * @param data_type client data type (SMS_DONT_CARE for all types) | ||
936 | * @param id client id (SMS_DONT_CARE for all id) | ||
937 | * | ||
938 | */ | ||
939 | static struct | ||
940 | smscore_client_t *smscore_find_client(struct smscore_device_t *coredev, | ||
941 | int data_type, int id) | ||
942 | { | ||
943 | struct smscore_client_t *client = NULL; | ||
944 | struct list_head *next, *first; | ||
945 | unsigned long flags; | ||
946 | struct list_head *firstid, *nextid; | ||
947 | |||
948 | |||
949 | spin_lock_irqsave(&coredev->clientslock, flags); | ||
950 | first = &coredev->clients; | ||
951 | for (next = first->next; | ||
952 | (next != first) && !client; | ||
953 | next = next->next) { | ||
954 | firstid = &((struct smscore_client_t *)next)->idlist; | ||
955 | for (nextid = firstid->next; | ||
956 | nextid != firstid; | ||
957 | nextid = nextid->next) { | ||
958 | if ((((struct smscore_idlist_t *)nextid)->id == id) && | ||
959 | (((struct smscore_idlist_t *)nextid)->data_type == data_type || | ||
960 | (((struct smscore_idlist_t *)nextid)->data_type == 0))) { | ||
961 | client = (struct smscore_client_t *) next; | ||
962 | break; | ||
963 | } | ||
964 | } | ||
965 | } | ||
966 | spin_unlock_irqrestore(&coredev->clientslock, flags); | ||
967 | return client; | ||
968 | } | ||
969 | |||
970 | /** | ||
971 | * find client by response id/type, call clients onresponse handler | ||
972 | * return buffer to pool on error | ||
973 | * | ||
974 | * @param coredev pointer to a coredev object returned by | ||
975 | * smscore_register_device | ||
976 | * @param cb pointer to response buffer descriptor | ||
977 | * | ||
978 | */ | ||
979 | void smscore_onresponse(struct smscore_device_t *coredev, | ||
980 | struct smscore_buffer_t *cb) { | ||
981 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p | ||
982 | + cb->offset); | ||
983 | struct smscore_client_t *client; | ||
984 | int rc = -EBUSY; | ||
985 | static unsigned long last_sample_time; /* = 0; */ | ||
986 | static int data_total; /* = 0; */ | ||
987 | unsigned long time_now = jiffies_to_msecs(jiffies); | ||
988 | |||
989 | if (!last_sample_time) | ||
990 | last_sample_time = time_now; | ||
991 | |||
992 | if (time_now - last_sample_time > 10000) { | ||
993 | sms_debug("\ndata rate %d bytes/secs", | ||
994 | (int)((data_total * 1000) / | ||
995 | (time_now - last_sample_time))); | ||
996 | |||
997 | last_sample_time = time_now; | ||
998 | data_total = 0; | ||
999 | } | ||
1000 | |||
1001 | data_total += cb->size; | ||
1002 | /* Do we need to re-route? */ | ||
1003 | if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) || | ||
1004 | (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { | ||
1005 | if (coredev->mode == DEVICE_MODE_DVBT_BDA) | ||
1006 | phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; | ||
1007 | } | ||
1008 | |||
1009 | |||
1010 | client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); | ||
1011 | |||
1012 | /* If no client registered for type & id, | ||
1013 | * check for control client where type is not registered */ | ||
1014 | if (client) | ||
1015 | rc = client->onresponse_handler(client->context, cb); | ||
1016 | |||
1017 | if (rc < 0) { | ||
1018 | switch (phdr->msgType) { | ||
1019 | case MSG_SMS_GET_VERSION_EX_RES: | ||
1020 | { | ||
1021 | struct SmsVersionRes_ST *ver = | ||
1022 | (struct SmsVersionRes_ST *) phdr; | ||
1023 | sms_debug("MSG_SMS_GET_VERSION_EX_RES " | ||
1024 | "id %d prots 0x%x ver %d.%d", | ||
1025 | ver->FirmwareId, ver->SupportedProtocols, | ||
1026 | ver->RomVersionMajor, ver->RomVersionMinor); | ||
1027 | |||
1028 | coredev->mode = ver->FirmwareId == 255 ? | ||
1029 | DEVICE_MODE_NONE : ver->FirmwareId; | ||
1030 | coredev->modes_supported = ver->SupportedProtocols; | ||
1031 | |||
1032 | complete(&coredev->version_ex_done); | ||
1033 | break; | ||
1034 | } | ||
1035 | case MSG_SMS_INIT_DEVICE_RES: | ||
1036 | sms_debug("MSG_SMS_INIT_DEVICE_RES"); | ||
1037 | complete(&coredev->init_device_done); | ||
1038 | break; | ||
1039 | case MSG_SW_RELOAD_START_RES: | ||
1040 | sms_debug("MSG_SW_RELOAD_START_RES"); | ||
1041 | complete(&coredev->reload_start_done); | ||
1042 | break; | ||
1043 | case MSG_SMS_DATA_DOWNLOAD_RES: | ||
1044 | complete(&coredev->data_download_done); | ||
1045 | break; | ||
1046 | case MSG_SW_RELOAD_EXEC_RES: | ||
1047 | sms_debug("MSG_SW_RELOAD_EXEC_RES"); | ||
1048 | break; | ||
1049 | case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: | ||
1050 | sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES"); | ||
1051 | complete(&coredev->trigger_done); | ||
1052 | break; | ||
1053 | case MSG_SMS_SLEEP_RESUME_COMP_IND: | ||
1054 | complete(&coredev->resume_done); | ||
1055 | break; | ||
1056 | case MSG_SMS_GPIO_CONFIG_EX_RES: | ||
1057 | sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); | ||
1058 | complete(&coredev->gpio_configuration_done); | ||
1059 | break; | ||
1060 | case MSG_SMS_GPIO_SET_LEVEL_RES: | ||
1061 | sms_debug("MSG_SMS_GPIO_SET_LEVEL_RES"); | ||
1062 | complete(&coredev->gpio_set_level_done); | ||
1063 | break; | ||
1064 | case MSG_SMS_GPIO_GET_LEVEL_RES: | ||
1065 | { | ||
1066 | u32 *msgdata = (u32 *) phdr; | ||
1067 | coredev->gpio_get_res = msgdata[1]; | ||
1068 | sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", | ||
1069 | coredev->gpio_get_res); | ||
1070 | complete(&coredev->gpio_get_level_done); | ||
1071 | break; | ||
1072 | } | ||
1073 | case MSG_SMS_START_IR_RES: | ||
1074 | complete(&coredev->ir_init_done); | ||
1075 | break; | ||
1076 | case MSG_SMS_IR_SAMPLES_IND: | ||
1077 | sms_ir_event(coredev, | ||
1078 | (const char *) | ||
1079 | ((char *)phdr | ||
1080 | + sizeof(struct SmsMsgHdr_ST)), | ||
1081 | (int)phdr->msgLength | ||
1082 | - sizeof(struct SmsMsgHdr_ST)); | ||
1083 | break; | ||
1084 | |||
1085 | default: | ||
1086 | break; | ||
1087 | } | ||
1088 | smscore_putbuffer(coredev, cb); | ||
1089 | } | ||
1090 | } | ||
1091 | EXPORT_SYMBOL_GPL(smscore_onresponse); | ||
1092 | |||
1093 | /** | ||
1094 | * return pointer to next free buffer descriptor from core pool | ||
1095 | * | ||
1096 | * @param coredev pointer to a coredev object returned by | ||
1097 | * smscore_register_device | ||
1098 | * | ||
1099 | * @return pointer to descriptor on success, NULL on error. | ||
1100 | */ | ||
1101 | |||
1102 | struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev) | ||
1103 | { | ||
1104 | struct smscore_buffer_t *cb = NULL; | ||
1105 | unsigned long flags; | ||
1106 | |||
1107 | spin_lock_irqsave(&coredev->bufferslock, flags); | ||
1108 | if (!list_empty(&coredev->buffers)) { | ||
1109 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | ||
1110 | list_del(&cb->entry); | ||
1111 | } | ||
1112 | spin_unlock_irqrestore(&coredev->bufferslock, flags); | ||
1113 | return cb; | ||
1114 | } | ||
1115 | |||
1116 | struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev) | ||
1117 | { | ||
1118 | struct smscore_buffer_t *cb = NULL; | ||
1119 | |||
1120 | wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev))); | ||
1121 | |||
1122 | return cb; | ||
1123 | } | ||
1124 | EXPORT_SYMBOL_GPL(smscore_getbuffer); | ||
1125 | |||
1126 | /** | ||
1127 | * return buffer descriptor to a pool | ||
1128 | * | ||
1129 | * @param coredev pointer to a coredev object returned by | ||
1130 | * smscore_register_device | ||
1131 | * @param cb pointer buffer descriptor | ||
1132 | * | ||
1133 | */ | ||
1134 | void smscore_putbuffer(struct smscore_device_t *coredev, | ||
1135 | struct smscore_buffer_t *cb) { | ||
1136 | wake_up_interruptible(&coredev->buffer_mng_waitq); | ||
1137 | list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock); | ||
1138 | } | ||
1139 | EXPORT_SYMBOL_GPL(smscore_putbuffer); | ||
1140 | |||
1141 | static int smscore_validate_client(struct smscore_device_t *coredev, | ||
1142 | struct smscore_client_t *client, | ||
1143 | int data_type, int id) | ||
1144 | { | ||
1145 | struct smscore_idlist_t *listentry; | ||
1146 | struct smscore_client_t *registered_client; | ||
1147 | |||
1148 | if (!client) { | ||
1149 | sms_err("bad parameter."); | ||
1150 | return -EINVAL; | ||
1151 | } | ||
1152 | registered_client = smscore_find_client(coredev, data_type, id); | ||
1153 | if (registered_client == client) | ||
1154 | return 0; | ||
1155 | |||
1156 | if (registered_client) { | ||
1157 | sms_err("The msg ID already registered to another client."); | ||
1158 | return -EEXIST; | ||
1159 | } | ||
1160 | listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); | ||
1161 | if (!listentry) { | ||
1162 | sms_err("Can't allocate memory for client id."); | ||
1163 | return -ENOMEM; | ||
1164 | } | ||
1165 | listentry->id = id; | ||
1166 | listentry->data_type = data_type; | ||
1167 | list_add_locked(&listentry->entry, &client->idlist, | ||
1168 | &coredev->clientslock); | ||
1169 | return 0; | ||
1170 | } | ||
1171 | |||
1172 | /** | ||
1173 | * creates smsclient object, check that id is taken by another client | ||
1174 | * | ||
1175 | * @param coredev pointer to a coredev object from clients hotplug | ||
1176 | * @param initial_id all messages with this id would be sent to this client | ||
1177 | * @param data_type all messages of this type would be sent to this client | ||
1178 | * @param onresponse_handler client handler that is called to | ||
1179 | * process incoming messages | ||
1180 | * @param onremove_handler client handler that is called when device is removed | ||
1181 | * @param context client-specific context | ||
1182 | * @param client pointer to a value that receives created smsclient object | ||
1183 | * | ||
1184 | * @return 0 on success, <0 on error. | ||
1185 | */ | ||
1186 | int smscore_register_client(struct smscore_device_t *coredev, | ||
1187 | struct smsclient_params_t *params, | ||
1188 | struct smscore_client_t **client) | ||
1189 | { | ||
1190 | struct smscore_client_t *newclient; | ||
1191 | /* check that no other channel with same parameters exists */ | ||
1192 | if (smscore_find_client(coredev, params->data_type, | ||
1193 | params->initial_id)) { | ||
1194 | sms_err("Client already exist."); | ||
1195 | return -EEXIST; | ||
1196 | } | ||
1197 | |||
1198 | newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); | ||
1199 | if (!newclient) { | ||
1200 | sms_err("Failed to allocate memory for client."); | ||
1201 | return -ENOMEM; | ||
1202 | } | ||
1203 | |||
1204 | INIT_LIST_HEAD(&newclient->idlist); | ||
1205 | newclient->coredev = coredev; | ||
1206 | newclient->onresponse_handler = params->onresponse_handler; | ||
1207 | newclient->onremove_handler = params->onremove_handler; | ||
1208 | newclient->context = params->context; | ||
1209 | list_add_locked(&newclient->entry, &coredev->clients, | ||
1210 | &coredev->clientslock); | ||
1211 | smscore_validate_client(coredev, newclient, params->data_type, | ||
1212 | params->initial_id); | ||
1213 | *client = newclient; | ||
1214 | sms_debug("%p %d %d", params->context, params->data_type, | ||
1215 | params->initial_id); | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | EXPORT_SYMBOL_GPL(smscore_register_client); | ||
1220 | |||
1221 | /** | ||
1222 | * frees smsclient object and all subclients associated with it | ||
1223 | * | ||
1224 | * @param client pointer to smsclient object returned by | ||
1225 | * smscore_register_client | ||
1226 | * | ||
1227 | */ | ||
1228 | void smscore_unregister_client(struct smscore_client_t *client) | ||
1229 | { | ||
1230 | struct smscore_device_t *coredev = client->coredev; | ||
1231 | unsigned long flags; | ||
1232 | |||
1233 | spin_lock_irqsave(&coredev->clientslock, flags); | ||
1234 | |||
1235 | |||
1236 | while (!list_empty(&client->idlist)) { | ||
1237 | struct smscore_idlist_t *identry = | ||
1238 | (struct smscore_idlist_t *) client->idlist.next; | ||
1239 | list_del(&identry->entry); | ||
1240 | kfree(identry); | ||
1241 | } | ||
1242 | |||
1243 | sms_info("%p", client->context); | ||
1244 | |||
1245 | list_del(&client->entry); | ||
1246 | kfree(client); | ||
1247 | |||
1248 | spin_unlock_irqrestore(&coredev->clientslock, flags); | ||
1249 | } | ||
1250 | EXPORT_SYMBOL_GPL(smscore_unregister_client); | ||
1251 | |||
1252 | /** | ||
1253 | * verifies that source id is not taken by another client, | ||
1254 | * calls device handler to send requests to the device | ||
1255 | * | ||
1256 | * @param client pointer to smsclient object returned by | ||
1257 | * smscore_register_client | ||
1258 | * @param buffer pointer to a request buffer | ||
1259 | * @param size size (in bytes) of request buffer | ||
1260 | * | ||
1261 | * @return 0 on success, <0 on error. | ||
1262 | */ | ||
1263 | int smsclient_sendrequest(struct smscore_client_t *client, | ||
1264 | void *buffer, size_t size) | ||
1265 | { | ||
1266 | struct smscore_device_t *coredev; | ||
1267 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; | ||
1268 | int rc; | ||
1269 | |||
1270 | if (client == NULL) { | ||
1271 | sms_err("Got NULL client"); | ||
1272 | return -EINVAL; | ||
1273 | } | ||
1274 | |||
1275 | coredev = client->coredev; | ||
1276 | |||
1277 | /* check that no other channel with same id exists */ | ||
1278 | if (coredev == NULL) { | ||
1279 | sms_err("Got NULL coredev"); | ||
1280 | return -EINVAL; | ||
1281 | } | ||
1282 | |||
1283 | rc = smscore_validate_client(client->coredev, client, 0, | ||
1284 | phdr->msgSrcId); | ||
1285 | if (rc < 0) | ||
1286 | return rc; | ||
1287 | |||
1288 | return coredev->sendrequest_handler(coredev->context, buffer, size); | ||
1289 | } | ||
1290 | EXPORT_SYMBOL_GPL(smsclient_sendrequest); | ||
1291 | |||
1292 | |||
1293 | /* old GPIO managements implementation */ | ||
1294 | int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, | ||
1295 | struct smscore_config_gpio *pinconfig) | ||
1296 | { | ||
1297 | struct { | ||
1298 | struct SmsMsgHdr_ST hdr; | ||
1299 | u32 data[6]; | ||
1300 | } msg; | ||
1301 | |||
1302 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { | ||
1303 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1304 | msg.hdr.msgDstId = HIF_TASK; | ||
1305 | msg.hdr.msgFlags = 0; | ||
1306 | msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | ||
1307 | msg.hdr.msgLength = sizeof(msg); | ||
1308 | |||
1309 | msg.data[0] = pin; | ||
1310 | msg.data[1] = pinconfig->pullupdown; | ||
1311 | |||
1312 | /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */ | ||
1313 | msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; | ||
1314 | |||
1315 | switch (pinconfig->outputdriving) { | ||
1316 | case SMS_GPIO_OUTPUTDRIVING_16mA: | ||
1317 | msg.data[3] = 7; /* Nova - 16mA */ | ||
1318 | break; | ||
1319 | case SMS_GPIO_OUTPUTDRIVING_12mA: | ||
1320 | msg.data[3] = 5; /* Nova - 11mA */ | ||
1321 | break; | ||
1322 | case SMS_GPIO_OUTPUTDRIVING_8mA: | ||
1323 | msg.data[3] = 3; /* Nova - 7mA */ | ||
1324 | break; | ||
1325 | case SMS_GPIO_OUTPUTDRIVING_4mA: | ||
1326 | default: | ||
1327 | msg.data[3] = 2; /* Nova - 4mA */ | ||
1328 | break; | ||
1329 | } | ||
1330 | |||
1331 | msg.data[4] = pinconfig->direction; | ||
1332 | msg.data[5] = 0; | ||
1333 | } else /* TODO: SMS_DEVICE_FAMILY1 */ | ||
1334 | return -EINVAL; | ||
1335 | |||
1336 | return coredev->sendrequest_handler(coredev->context, | ||
1337 | &msg, sizeof(msg)); | ||
1338 | } | ||
1339 | |||
1340 | int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) | ||
1341 | { | ||
1342 | struct { | ||
1343 | struct SmsMsgHdr_ST hdr; | ||
1344 | u32 data[3]; | ||
1345 | } msg; | ||
1346 | |||
1347 | if (pin > MAX_GPIO_PIN_NUMBER) | ||
1348 | return -EINVAL; | ||
1349 | |||
1350 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1351 | msg.hdr.msgDstId = HIF_TASK; | ||
1352 | msg.hdr.msgFlags = 0; | ||
1353 | msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | ||
1354 | msg.hdr.msgLength = sizeof(msg); | ||
1355 | |||
1356 | msg.data[0] = pin; | ||
1357 | msg.data[1] = level ? 1 : 0; | ||
1358 | msg.data[2] = 0; | ||
1359 | |||
1360 | return coredev->sendrequest_handler(coredev->context, | ||
1361 | &msg, sizeof(msg)); | ||
1362 | } | ||
1363 | |||
1364 | /* new GPIO management implementation */ | ||
1365 | static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, | ||
1366 | u32 *pGroupNum, u32 *pGroupCfg) { | ||
1367 | |||
1368 | *pGroupCfg = 1; | ||
1369 | |||
1370 | if (PinNum <= 1) { | ||
1371 | *pTranslatedPinNum = 0; | ||
1372 | *pGroupNum = 9; | ||
1373 | *pGroupCfg = 2; | ||
1374 | } else if (PinNum >= 2 && PinNum <= 6) { | ||
1375 | *pTranslatedPinNum = 2; | ||
1376 | *pGroupNum = 0; | ||
1377 | *pGroupCfg = 2; | ||
1378 | } else if (PinNum >= 7 && PinNum <= 11) { | ||
1379 | *pTranslatedPinNum = 7; | ||
1380 | *pGroupNum = 1; | ||
1381 | } else if (PinNum >= 12 && PinNum <= 15) { | ||
1382 | *pTranslatedPinNum = 12; | ||
1383 | *pGroupNum = 2; | ||
1384 | *pGroupCfg = 3; | ||
1385 | } else if (PinNum == 16) { | ||
1386 | *pTranslatedPinNum = 16; | ||
1387 | *pGroupNum = 23; | ||
1388 | } else if (PinNum >= 17 && PinNum <= 24) { | ||
1389 | *pTranslatedPinNum = 17; | ||
1390 | *pGroupNum = 3; | ||
1391 | } else if (PinNum == 25) { | ||
1392 | *pTranslatedPinNum = 25; | ||
1393 | *pGroupNum = 6; | ||
1394 | } else if (PinNum >= 26 && PinNum <= 28) { | ||
1395 | *pTranslatedPinNum = 26; | ||
1396 | *pGroupNum = 4; | ||
1397 | } else if (PinNum == 29) { | ||
1398 | *pTranslatedPinNum = 29; | ||
1399 | *pGroupNum = 5; | ||
1400 | *pGroupCfg = 2; | ||
1401 | } else if (PinNum == 30) { | ||
1402 | *pTranslatedPinNum = 30; | ||
1403 | *pGroupNum = 8; | ||
1404 | } else if (PinNum == 31) { | ||
1405 | *pTranslatedPinNum = 31; | ||
1406 | *pGroupNum = 17; | ||
1407 | } else | ||
1408 | return -1; | ||
1409 | |||
1410 | *pGroupCfg <<= 24; | ||
1411 | |||
1412 | return 0; | ||
1413 | } | ||
1414 | |||
1415 | int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, | ||
1416 | struct smscore_gpio_config *pGpioConfig) { | ||
1417 | |||
1418 | u32 totalLen; | ||
1419 | u32 TranslatedPinNum = 0; | ||
1420 | u32 GroupNum = 0; | ||
1421 | u32 ElectricChar; | ||
1422 | u32 groupCfg; | ||
1423 | void *buffer; | ||
1424 | int rc; | ||
1425 | |||
1426 | struct SetGpioMsg { | ||
1427 | struct SmsMsgHdr_ST xMsgHeader; | ||
1428 | u32 msgData[6]; | ||
1429 | } *pMsg; | ||
1430 | |||
1431 | |||
1432 | if (PinNum > MAX_GPIO_PIN_NUMBER) | ||
1433 | return -EINVAL; | ||
1434 | |||
1435 | if (pGpioConfig == NULL) | ||
1436 | return -EINVAL; | ||
1437 | |||
1438 | totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); | ||
1439 | |||
1440 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1441 | GFP_KERNEL | GFP_DMA); | ||
1442 | if (!buffer) | ||
1443 | return -ENOMEM; | ||
1444 | |||
1445 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1446 | |||
1447 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1448 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1449 | pMsg->xMsgHeader.msgFlags = 0; | ||
1450 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1451 | pMsg->msgData[0] = PinNum; | ||
1452 | |||
1453 | if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { | ||
1454 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; | ||
1455 | if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, | ||
1456 | &groupCfg) != 0) { | ||
1457 | rc = -EINVAL; | ||
1458 | goto free; | ||
1459 | } | ||
1460 | |||
1461 | pMsg->msgData[1] = TranslatedPinNum; | ||
1462 | pMsg->msgData[2] = GroupNum; | ||
1463 | ElectricChar = (pGpioConfig->PullUpDown) | ||
1464 | | (pGpioConfig->InputCharacteristics << 2) | ||
1465 | | (pGpioConfig->OutputSlewRate << 3) | ||
1466 | | (pGpioConfig->OutputDriving << 4); | ||
1467 | pMsg->msgData[3] = ElectricChar; | ||
1468 | pMsg->msgData[4] = pGpioConfig->Direction; | ||
1469 | pMsg->msgData[5] = groupCfg; | ||
1470 | } else { | ||
1471 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | ||
1472 | pMsg->msgData[1] = pGpioConfig->PullUpDown; | ||
1473 | pMsg->msgData[2] = pGpioConfig->OutputSlewRate; | ||
1474 | pMsg->msgData[3] = pGpioConfig->OutputDriving; | ||
1475 | pMsg->msgData[4] = pGpioConfig->Direction; | ||
1476 | pMsg->msgData[5] = 0; | ||
1477 | } | ||
1478 | |||
1479 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1480 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1481 | &coredev->gpio_configuration_done); | ||
1482 | |||
1483 | if (rc != 0) { | ||
1484 | if (rc == -ETIME) | ||
1485 | sms_err("smscore_gpio_configure timeout"); | ||
1486 | else | ||
1487 | sms_err("smscore_gpio_configure error"); | ||
1488 | } | ||
1489 | free: | ||
1490 | kfree(buffer); | ||
1491 | |||
1492 | return rc; | ||
1493 | } | ||
1494 | |||
1495 | int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, | ||
1496 | u8 NewLevel) { | ||
1497 | |||
1498 | u32 totalLen; | ||
1499 | int rc; | ||
1500 | void *buffer; | ||
1501 | |||
1502 | struct SetGpioMsg { | ||
1503 | struct SmsMsgHdr_ST xMsgHeader; | ||
1504 | u32 msgData[3]; /* keep it 3 ! */ | ||
1505 | } *pMsg; | ||
1506 | |||
1507 | if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) | ||
1508 | return -EINVAL; | ||
1509 | |||
1510 | totalLen = sizeof(struct SmsMsgHdr_ST) + | ||
1511 | (3 * sizeof(u32)); /* keep it 3 ! */ | ||
1512 | |||
1513 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1514 | GFP_KERNEL | GFP_DMA); | ||
1515 | if (!buffer) | ||
1516 | return -ENOMEM; | ||
1517 | |||
1518 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1519 | |||
1520 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1521 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1522 | pMsg->xMsgHeader.msgFlags = 0; | ||
1523 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | ||
1524 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1525 | pMsg->msgData[0] = PinNum; | ||
1526 | pMsg->msgData[1] = NewLevel; | ||
1527 | |||
1528 | /* Send message to SMS */ | ||
1529 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1530 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1531 | &coredev->gpio_set_level_done); | ||
1532 | |||
1533 | if (rc != 0) { | ||
1534 | if (rc == -ETIME) | ||
1535 | sms_err("smscore_gpio_set_level timeout"); | ||
1536 | else | ||
1537 | sms_err("smscore_gpio_set_level error"); | ||
1538 | } | ||
1539 | kfree(buffer); | ||
1540 | |||
1541 | return rc; | ||
1542 | } | ||
1543 | |||
1544 | int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, | ||
1545 | u8 *level) { | ||
1546 | |||
1547 | u32 totalLen; | ||
1548 | int rc; | ||
1549 | void *buffer; | ||
1550 | |||
1551 | struct SetGpioMsg { | ||
1552 | struct SmsMsgHdr_ST xMsgHeader; | ||
1553 | u32 msgData[2]; | ||
1554 | } *pMsg; | ||
1555 | |||
1556 | |||
1557 | if (PinNum > MAX_GPIO_PIN_NUMBER) | ||
1558 | return -EINVAL; | ||
1559 | |||
1560 | totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); | ||
1561 | |||
1562 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1563 | GFP_KERNEL | GFP_DMA); | ||
1564 | if (!buffer) | ||
1565 | return -ENOMEM; | ||
1566 | |||
1567 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1568 | |||
1569 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1570 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1571 | pMsg->xMsgHeader.msgFlags = 0; | ||
1572 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; | ||
1573 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1574 | pMsg->msgData[0] = PinNum; | ||
1575 | pMsg->msgData[1] = 0; | ||
1576 | |||
1577 | /* Send message to SMS */ | ||
1578 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1579 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1580 | &coredev->gpio_get_level_done); | ||
1581 | |||
1582 | if (rc != 0) { | ||
1583 | if (rc == -ETIME) | ||
1584 | sms_err("smscore_gpio_get_level timeout"); | ||
1585 | else | ||
1586 | sms_err("smscore_gpio_get_level error"); | ||
1587 | } | ||
1588 | kfree(buffer); | ||
1589 | |||
1590 | /* Its a race between other gpio_get_level() and the copy of the single | ||
1591 | * global 'coredev->gpio_get_res' to the function's variable 'level' | ||
1592 | */ | ||
1593 | *level = coredev->gpio_get_res; | ||
1594 | |||
1595 | return rc; | ||
1596 | } | ||
1597 | |||
1598 | static int __init smscore_module_init(void) | ||
1599 | { | ||
1600 | int rc = 0; | ||
1601 | |||
1602 | INIT_LIST_HEAD(&g_smscore_notifyees); | ||
1603 | INIT_LIST_HEAD(&g_smscore_devices); | ||
1604 | kmutex_init(&g_smscore_deviceslock); | ||
1605 | |||
1606 | INIT_LIST_HEAD(&g_smscore_registry); | ||
1607 | kmutex_init(&g_smscore_registrylock); | ||
1608 | |||
1609 | return rc; | ||
1610 | } | ||
1611 | |||
1612 | static void __exit smscore_module_exit(void) | ||
1613 | { | ||
1614 | kmutex_lock(&g_smscore_deviceslock); | ||
1615 | while (!list_empty(&g_smscore_notifyees)) { | ||
1616 | struct smscore_device_notifyee_t *notifyee = | ||
1617 | (struct smscore_device_notifyee_t *) | ||
1618 | g_smscore_notifyees.next; | ||
1619 | |||
1620 | list_del(¬ifyee->entry); | ||
1621 | kfree(notifyee); | ||
1622 | } | ||
1623 | kmutex_unlock(&g_smscore_deviceslock); | ||
1624 | |||
1625 | kmutex_lock(&g_smscore_registrylock); | ||
1626 | while (!list_empty(&g_smscore_registry)) { | ||
1627 | struct smscore_registry_entry_t *entry = | ||
1628 | (struct smscore_registry_entry_t *) | ||
1629 | g_smscore_registry.next; | ||
1630 | |||
1631 | list_del(&entry->entry); | ||
1632 | kfree(entry); | ||
1633 | } | ||
1634 | kmutex_unlock(&g_smscore_registrylock); | ||
1635 | |||
1636 | sms_debug(""); | ||
1637 | } | ||
1638 | |||
1639 | module_init(smscore_module_init); | ||
1640 | module_exit(smscore_module_exit); | ||
1641 | |||
1642 | MODULE_DESCRIPTION("Siano MDTV Core module"); | ||
1643 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); | ||
1644 | MODULE_LICENSE("GPL"); | ||