V4L/DVB (8268): sms1xxx: usb cleanup
[linux-2.6.git] / drivers / media / mdtv / smscoreapi.c
blob6d21ff39122713e8edcd1a456e80c502d899d104
1 /*!
3 \file smscoreapi.c
5 \brief Siano core API module
6 This file contains implementation for the interface to sms core component
8 \par Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
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;
14 Software distributed under the License is distributed on an "AS
15 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
16 implied.
18 \author Anatoly Greenblat
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>
30 #include <linux/firmware.h>
32 #include "smscoreapi.h"
34 typedef struct _smscore_device_notifyee
36 struct list_head entry;
37 hotplug_t hotplug;
38 } smscore_device_notifyee_t;
40 typedef struct _smscore_client
42 struct list_head entry;
43 smscore_device_t *coredev;
45 void *context;
47 int data_type;
49 onresponse_t onresponse_handler;
50 onremove_t onremove_handler;
51 } *psmscore_client_t;
53 typedef struct _smscore_subclient
55 struct list_head entry;
56 smscore_client_t *client;
58 int id;
59 } smscore_subclient_t;
61 typedef struct _smscore_device
63 struct list_head entry;
65 struct list_head clients;
66 struct list_head subclients;
67 spinlock_t clientslock;
69 struct list_head buffers;
70 spinlock_t bufferslock;
71 int num_buffers;
73 void *common_buffer;
74 int common_buffer_size;
75 dma_addr_t common_buffer_phys;
77 void *context;
78 struct device *device;
80 char devpath[32];
81 unsigned long device_flags;
83 setmode_t setmode_handler;
84 detectmode_t detectmode_handler;
85 sendrequest_t sendrequest_handler;
86 preload_t preload_handler;
87 postload_t postload_handler;
89 int mode, modes_supported;
91 struct completion version_ex_done, data_download_done, trigger_done;
92 struct completion init_device_done, reload_start_done, resume_done;
93 } *psmscore_device_t;
95 typedef struct _smscore_registry_entry
97 struct list_head entry;
98 char devpath[32];
99 int mode;
100 } smscore_registry_entry_t;
102 struct list_head g_smscore_notifyees;
103 struct list_head g_smscore_devices;
104 kmutex_t g_smscore_deviceslock;
106 struct list_head g_smscore_registry;
107 kmutex_t g_smscore_registrylock;
109 static int default_mode = 1;
110 module_param(default_mode, int, 0644);
111 MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");
113 int smscore_registry_getmode(char* devpath)
115 smscore_registry_entry_t *entry;
116 struct list_head *next;
118 kmutex_lock(&g_smscore_registrylock);
120 for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
122 entry = (smscore_registry_entry_t *) next;
124 if (!strcmp(entry->devpath, devpath))
126 kmutex_unlock(&g_smscore_registrylock);
127 return entry->mode;
131 entry = (smscore_registry_entry_t *) kmalloc(sizeof(smscore_registry_entry_t), GFP_KERNEL);
132 if (entry)
134 entry->mode = default_mode;
135 strcpy(entry->devpath, devpath);
137 list_add(&entry->entry, &g_smscore_registry);
140 kmutex_unlock(&g_smscore_registrylock);
142 return default_mode;
145 void smscore_registry_setmode(char* devpath, int mode)
147 smscore_registry_entry_t *entry;
148 struct list_head *next;
150 kmutex_lock(&g_smscore_registrylock);
152 for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next)
154 entry = (smscore_registry_entry_t *) next;
156 if (!strcmp(entry->devpath, devpath))
158 entry->mode = mode;
159 break;
163 kmutex_unlock(&g_smscore_registrylock);
167 void list_add_locked(struct list_head *new, struct list_head *head, spinlock_t* lock)
169 unsigned long flags;
171 spin_lock_irqsave(lock, flags);
173 list_add(new, head);
175 spin_unlock_irqrestore(lock, flags);
179 * register a client callback that called when device plugged in/unplugged
180 * NOTE: if devices exist callback is called immediately for each device
182 * @param hotplug callback
184 * @return 0 on success, <0 on error.
186 int smscore_register_hotplug(hotplug_t hotplug)
188 smscore_device_notifyee_t *notifyee;
189 struct list_head *next, *first;
190 int rc = 0;
192 kmutex_lock(&g_smscore_deviceslock);
194 notifyee = kmalloc(sizeof(smscore_device_notifyee_t), GFP_KERNEL);
195 if (notifyee)
197 // now notify callback about existing devices
198 first = &g_smscore_devices;
199 for (next = first->next; next != first && !rc; next = next->next)
201 smscore_device_t *coredev = (smscore_device_t *) next;
202 rc = hotplug(coredev, coredev->device, 1);
205 if (rc >= 0)
207 notifyee->hotplug = hotplug;
208 list_add(&notifyee->entry, &g_smscore_notifyees);
210 else
211 kfree(notifyee);
213 else
214 rc = -ENOMEM;
216 kmutex_unlock(&g_smscore_deviceslock);
218 return rc;
222 * unregister a client callback that called when device plugged in/unplugged
224 * @param hotplug callback
227 void smscore_unregister_hotplug(hotplug_t hotplug)
229 struct list_head *next, *first;
231 kmutex_lock(&g_smscore_deviceslock);
233 first = &g_smscore_notifyees;
235 for (next = first->next; next != first;)
237 smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) next;
238 next = next->next;
240 if (notifyee->hotplug == hotplug)
242 list_del(&notifyee->entry);
243 kfree(notifyee);
247 kmutex_unlock(&g_smscore_deviceslock);
250 void smscore_notify_clients(smscore_device_t *coredev)
252 smscore_client_t* client;
254 // the client must call smscore_unregister_client from remove handler
255 while (!list_empty(&coredev->clients))
257 client = (smscore_client_t *) coredev->clients.next;
258 client->onremove_handler(client->context);
262 int smscore_notify_callbacks(smscore_device_t *coredev, struct device *device, int arrival)
264 struct list_head *next, *first;
265 int rc = 0;
267 // note: must be called under g_deviceslock
269 first = &g_smscore_notifyees;
271 for (next = first->next; next != first; next = next->next)
273 rc = ((smscore_device_notifyee_t *) next)->hotplug(coredev, device, arrival);
274 if (rc < 0)
275 break;
278 return rc;
281 smscore_buffer_t *smscore_createbuffer(u8* buffer, void* common_buffer, dma_addr_t common_buffer_phys)
283 smscore_buffer_t *cb = kmalloc(sizeof(smscore_buffer_t), GFP_KERNEL);
284 if (!cb)
286 printk(KERN_INFO "%s kmalloc(...) failed\n", __FUNCTION__);
287 return NULL;
290 cb->p = buffer;
291 cb->offset_in_common = buffer - (u8*) common_buffer;
292 cb->phys = common_buffer_phys + cb->offset_in_common;
294 return cb;
298 * creates coredev object for a device, prepares buffers, creates buffer mappings, notifies
299 * registered hotplugs about new device.
301 * @param params device pointer to struct with device specific parameters and handlers
302 * @param coredev pointer to a value that receives created coredev object
304 * @return 0 on success, <0 on error.
306 int smscore_register_device(smsdevice_params_t *params, smscore_device_t **coredev)
308 smscore_device_t* dev;
309 u8 *buffer;
311 dev = kzalloc(sizeof(smscore_device_t), GFP_KERNEL);
312 if (!dev)
314 printk(KERN_INFO "%s kzalloc(...) failed\n", __FUNCTION__);
315 return -ENOMEM;
318 // init list entry so it could be safe in smscore_unregister_device
319 INIT_LIST_HEAD(&dev->entry);
321 // init queues
322 INIT_LIST_HEAD(&dev->clients);
323 INIT_LIST_HEAD(&dev->subclients);
324 INIT_LIST_HEAD(&dev->buffers);
326 // init locks
327 spin_lock_init(&dev->clientslock);
328 spin_lock_init(&dev->bufferslock);
330 // init completion events
331 init_completion(&dev->version_ex_done);
332 init_completion(&dev->data_download_done);
333 init_completion(&dev->trigger_done);
334 init_completion(&dev->init_device_done);
335 init_completion(&dev->reload_start_done);
336 init_completion(&dev->resume_done);
338 // alloc common buffer
339 dev->common_buffer_size = params->buffer_size * params->num_buffers;
340 dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, &dev->common_buffer_phys, GFP_KERNEL | GFP_DMA);
341 if (!dev->common_buffer)
343 smscore_unregister_device(dev);
344 return -ENOMEM;
347 // prepare dma buffers
348 for (buffer = dev->common_buffer; dev->num_buffers < params->num_buffers; dev->num_buffers ++, buffer += params->buffer_size)
350 smscore_buffer_t *cb = smscore_createbuffer(buffer, dev->common_buffer, dev->common_buffer_phys);
351 if (!cb)
353 smscore_unregister_device(dev);
354 return -ENOMEM;
357 smscore_putbuffer(dev, cb);
360 printk(KERN_INFO "%s allocated %d buffers\n", __FUNCTION__, dev->num_buffers);
362 dev->mode = DEVICE_MODE_NONE;
363 dev->context = params->context;
364 dev->device = params->device;
365 dev->setmode_handler = params->setmode_handler;
366 dev->detectmode_handler = params->detectmode_handler;
367 dev->sendrequest_handler = params->sendrequest_handler;
368 dev->preload_handler = params->preload_handler;
369 dev->postload_handler = params->postload_handler;
371 dev->device_flags = params->flags;
372 strcpy(dev->devpath, params->devpath);
374 // add device to devices list
375 kmutex_lock(&g_smscore_deviceslock);
376 list_add(&dev->entry, &g_smscore_devices);
377 kmutex_unlock(&g_smscore_deviceslock);
379 *coredev = dev;
381 printk(KERN_INFO "%s device %p created\n", __FUNCTION__, dev);
383 return 0;
387 * sets initial device mode and notifies client hotplugs that device is ready
389 * @param coredev pointer to a coredev object returned by smscore_register_device
391 * @return 0 on success, <0 on error.
393 int smscore_start_device(smscore_device_t *coredev)
395 int rc = smscore_set_device_mode(coredev, smscore_registry_getmode(coredev->devpath));
396 if (rc < 0)
397 return rc;
399 kmutex_lock(&g_smscore_deviceslock);
401 rc = smscore_notify_callbacks(coredev, coredev->device, 1);
403 printk(KERN_INFO "%s device %p started, rc %d\n", __FUNCTION__, coredev, rc);
405 kmutex_unlock(&g_smscore_deviceslock);
407 return rc;
410 int smscore_sendrequest_and_wait(smscore_device_t *coredev, void* buffer, size_t size, struct completion *completion)
412 int rc = coredev->sendrequest_handler(coredev->context, buffer, size);
413 if (rc < 0)
414 return rc;
416 return wait_for_completion_timeout(completion, msecs_to_jiffies(1000)) ? 0 : -ETIME;
419 int smscore_load_firmware_family2(smscore_device_t *coredev, void *buffer, size_t size)
421 SmsFirmware_ST* firmware = (SmsFirmware_ST*) buffer;
422 SmsMsgHdr_ST *msg;
423 UINT32 mem_address = firmware->StartAddress;
424 u8* payload = firmware->Payload;
425 int rc = 0;
427 if (coredev->preload_handler)
429 rc = coredev->preload_handler(coredev->context);
430 if (rc < 0)
431 return rc;
434 // PAGE_SIZE buffer shall be enough and dma aligned
435 msg = (SmsMsgHdr_ST *) kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
436 if (!msg)
437 return -ENOMEM;
439 if (coredev->mode != DEVICE_MODE_NONE)
441 SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, sizeof(SmsMsgHdr_ST));
442 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->reload_start_done);
443 mem_address = *(UINT32*) &payload[20];
446 while (size && rc >= 0)
448 SmsDataDownload_ST *DataMsg = (SmsDataDownload_ST *) msg;
449 int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);
451 SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, (UINT16)(sizeof(SmsMsgHdr_ST) + sizeof(UINT32) + payload_size));
453 DataMsg->MemAddr = mem_address;
454 memcpy(DataMsg->Payload, payload, payload_size);
456 if (coredev->device_flags & SMS_ROM_NO_RESPONSE && coredev->mode == DEVICE_MODE_NONE)
457 rc = coredev->sendrequest_handler(coredev->context, DataMsg, DataMsg->xMsgHeader.msgLength);
458 else
459 rc = smscore_sendrequest_and_wait(coredev, DataMsg, DataMsg->xMsgHeader.msgLength, &coredev->data_download_done);
461 payload += payload_size;
462 size -= payload_size;
463 mem_address += payload_size;
466 if (rc >= 0)
468 if (coredev->mode == DEVICE_MODE_NONE)
470 SmsMsgData_ST* TriggerMsg = (SmsMsgData_ST*) msg;
472 SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, sizeof(SmsMsgHdr_ST) + sizeof(UINT32) * 5);
474 TriggerMsg->msgData[0] = firmware->StartAddress; // Entry point
475 TriggerMsg->msgData[1] = 5; // Priority
476 TriggerMsg->msgData[2] = 0x200; // Stack size
477 TriggerMsg->msgData[3] = 0; // Parameter
478 TriggerMsg->msgData[4] = 4; // Task ID
480 if (coredev->device_flags & SMS_ROM_NO_RESPONSE)
482 rc = coredev->sendrequest_handler(coredev->context, TriggerMsg, TriggerMsg->xMsgHeader.msgLength);
483 msleep(100);
485 else
486 rc = smscore_sendrequest_and_wait(coredev, TriggerMsg, TriggerMsg->xMsgHeader.msgLength, &coredev->trigger_done);
488 else
490 SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, sizeof(SmsMsgHdr_ST));
492 rc = coredev->sendrequest_handler(coredev->context, msg, msg->msgLength);
496 printk("%s %d \n", __func__, rc);
498 kfree(msg);
500 return (rc >= 0 && coredev->postload_handler) ?
501 coredev->postload_handler(coredev->context) :
506 * loads specified firmware into a buffer and calls device loadfirmware_handler
508 * @param coredev pointer to a coredev object returned by smscore_register_device
509 * @param filename null-terminated string specifies firmware file name
510 * @param loadfirmware_handler device handler that loads firmware
512 * @return 0 on success, <0 on error.
514 int smscore_load_firmware(smscore_device_t *coredev, char* filename, loadfirmware_t loadfirmware_handler)
516 int rc = -ENOENT;
518 const struct firmware *fw;
519 u8* fw_buffer;
521 if (loadfirmware_handler == NULL && !(coredev->device_flags & SMS_DEVICE_FAMILY2))
522 return -EINVAL;
524 rc = request_firmware(&fw, filename, coredev->device);
525 if (rc < 0)
527 printk(KERN_INFO "%s failed to open \"%s\"\n", __FUNCTION__, filename);
528 return rc;
531 fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA);
532 if (fw_buffer)
534 memcpy(fw_buffer, fw->data, fw->size);
536 rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
537 smscore_load_firmware_family2(coredev, fw_buffer, fw->size) :
538 loadfirmware_handler(coredev->context, fw_buffer, fw->size);
540 kfree(fw_buffer);
542 else
544 printk(KERN_INFO "%s failed to allocate firmware buffer\n", __FUNCTION__);
545 rc = -ENOMEM;
548 release_firmware(fw);
550 return rc;
554 * notifies all clients registered with the device, notifies hotplugs, frees all buffers and coredev object
556 * @param coredev pointer to a coredev object returned by smscore_register_device
558 * @return 0 on success, <0 on error.
560 void smscore_unregister_device(smscore_device_t *coredev)
562 smscore_buffer_t *cb;
563 int num_buffers = 0;
565 kmutex_lock(&g_smscore_deviceslock);
567 smscore_notify_clients(coredev);
568 smscore_notify_callbacks(coredev, NULL, 0);
570 // at this point all buffers should be back
571 // onresponse must no longer be called
573 while (1)
575 while ((cb = smscore_getbuffer(coredev)))
577 kfree(cb);
578 num_buffers ++;
581 if (num_buffers == coredev->num_buffers)
582 break;
584 printk(KERN_INFO "%s waiting for %d buffer(s)\n", __FUNCTION__, coredev->num_buffers - num_buffers);
585 msleep(100);
588 printk(KERN_INFO "%s freed %d buffers\n", __FUNCTION__, num_buffers);
590 if (coredev->common_buffer)
591 dma_free_coherent(NULL, coredev->common_buffer_size, coredev->common_buffer, coredev->common_buffer_phys);
593 list_del(&coredev->entry);
594 kfree(coredev);
596 kmutex_unlock(&g_smscore_deviceslock);
598 printk(KERN_INFO "%s device %p destroyed\n", __FUNCTION__, coredev);
601 int smscore_detect_mode(smscore_device_t *coredev)
603 void *buffer = kmalloc(sizeof(SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
604 SmsMsgHdr_ST *msg = (SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);
605 int rc;
607 if (!buffer)
608 return -ENOMEM;
610 SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, sizeof(SmsMsgHdr_ST));
612 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
613 if (rc == -ETIME)
615 printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed first try\n", __FUNCTION__);
617 if (wait_for_completion_timeout(&coredev->resume_done, msecs_to_jiffies(5000)))
619 rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, &coredev->version_ex_done);
620 if (rc < 0)
622 printk("%s: MSG_SMS_GET_VERSION_EX_REQ failed second try, rc %d\n", __FUNCTION__, rc);
625 else
626 rc = -ETIME;
629 kfree(buffer);
631 return rc;
634 char *smscore_fw_lkup[] =
636 "dvb_nova_12mhz.inp",
637 "dvb_nova_12mhz.inp",
638 "tdmb_nova.inp",
639 "none",
640 "dvb_nova_12mhz.inp",
641 "isdbt_nova_12mhz.inp",
642 "isdbt_nova_12mhz.inp",
643 "cmmb_nova_12mhz.inp",
644 "none",
648 * calls device handler to change mode of operation
649 * NOTE: stellar/usb may disconnect when changing mode
651 * @param coredev pointer to a coredev object returned by smscore_register_device
652 * @param mode requested mode of operation
654 * @return 0 on success, <0 on error.
656 int smscore_set_device_mode(smscore_device_t *coredev, int mode)
658 void *buffer;
659 int rc = 0;
661 if (coredev->device_flags & SMS_DEVICE_FAMILY2)
663 if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER)
665 printk(KERN_INFO "%s invalid mode specified %d\n", __FUNCTION__, mode);
666 return -EINVAL;
669 if (!(coredev->device_flags & SMS_DEVICE_NOT_READY))
671 rc = smscore_detect_mode(coredev);
672 if (rc < 0)
673 return rc;
676 if (coredev->mode == mode)
678 printk(KERN_INFO "%s device mode %d already set\n", __FUNCTION__, mode);
679 return 0;
682 if (!(coredev->modes_supported & (1 << mode)))
684 rc = smscore_load_firmware(coredev, smscore_fw_lkup[mode], NULL);
685 if (rc < 0)
686 return rc;
688 else
690 printk(KERN_INFO "%s mode %d supported by running firmware\n", __FUNCTION__, mode);
693 buffer = kmalloc(sizeof(SmsMsgData_ST) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
694 if (buffer)
696 SmsMsgData_ST *msg = (SmsMsgData_ST *) SMS_ALIGN_ADDRESS(buffer);
698 SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, sizeof(SmsMsgData_ST));
699 msg->msgData[0] = mode;
701 rc = smscore_sendrequest_and_wait(coredev, msg, msg->xMsgHeader.msgLength, &coredev->init_device_done);
703 kfree(buffer);
705 else
706 rc = -ENOMEM;
708 else
710 if (coredev->detectmode_handler)
711 coredev->detectmode_handler(coredev->context, &coredev->mode);
713 if (coredev->mode != mode && coredev->setmode_handler)
714 rc = coredev->setmode_handler(coredev->context, mode);
717 smscore_registry_setmode(coredev->devpath, mode);
719 if (rc >= 0)
721 coredev->mode = mode;
722 coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
725 return rc;
729 * calls device handler to get current mode of operation
731 * @param coredev pointer to a coredev object returned by smscore_register_device
733 * @return current mode
735 int smscore_get_device_mode(smscore_device_t *coredev)
737 return coredev->mode;
740 smscore_client_t* smscore_getclient_by_type(smscore_device_t *coredev, int data_type)
742 smscore_client_t *client = NULL;
743 struct list_head *next, *first;
744 unsigned long flags;
746 if (!data_type)
747 return NULL;
749 spin_lock_irqsave(&coredev->clientslock, flags);
751 first = &coredev->clients;
753 for (next = first->next; next != first; next = next->next)
755 if (((smscore_client_t*) next)->data_type == data_type)
757 client = (smscore_client_t*) next;
758 break;
762 spin_unlock_irqrestore(&coredev->clientslock, flags);
764 return client;
767 smscore_client_t* smscore_getclient_by_id(smscore_device_t *coredev, int id)
769 smscore_client_t *client = NULL;
770 struct list_head *next, *first;
771 unsigned long flags;
773 spin_lock_irqsave(&coredev->clientslock, flags);
775 first = &coredev->subclients;
777 for (next = first->next; next != first; next = next->next)
779 if (((smscore_subclient_t*) next)->id == id)
781 client = ((smscore_subclient_t*) next)->client;
782 break;
786 spin_unlock_irqrestore(&coredev->clientslock, flags);
788 return client;
792 * find client by response id/type, call clients onresponse handler
793 * return buffer to pool on error
795 * @param coredev pointer to a coredev object returned by smscore_register_device
796 * @param cb pointer to response buffer descriptor
799 void smscore_onresponse(smscore_device_t *coredev, smscore_buffer_t *cb)
801 SmsMsgHdr_ST *phdr = (SmsMsgHdr_ST *)((u8*) cb->p + cb->offset);
802 smscore_client_t * client = smscore_getclient_by_type(coredev, phdr->msgType);
803 int rc = -EBUSY;
805 static unsigned long last_sample_time = 0;
806 static int data_total = 0;
807 unsigned long time_now = jiffies_to_msecs(jiffies);
809 if (!last_sample_time)
810 last_sample_time = time_now;
812 if (time_now - last_sample_time > 10000)
814 printk("\n%s data rate %d bytes/secs\n", __func__, (int)((data_total * 1000) / (time_now - last_sample_time)));
816 last_sample_time = time_now;
817 data_total = 0;
820 data_total += cb->size;
822 if (!client)
823 client = smscore_getclient_by_id(coredev, phdr->msgDstId);
825 if (client)
826 rc = client->onresponse_handler(client->context, cb);
828 if (rc < 0)
830 switch (phdr->msgType)
832 case MSG_SMS_GET_VERSION_EX_RES:
834 SmsVersionRes_ST *ver = (SmsVersionRes_ST*) phdr;
835 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);
837 coredev->mode = ver->FirmwareId == 255 ? DEVICE_MODE_NONE : ver->FirmwareId;
838 coredev->modes_supported = ver->SupportedProtocols;
840 complete(&coredev->version_ex_done);
841 break;
844 case MSG_SMS_INIT_DEVICE_RES:
845 printk("%s: MSG_SMS_INIT_DEVICE_RES\n", __FUNCTION__);
846 complete(&coredev->init_device_done);
847 break;
849 case MSG_SW_RELOAD_START_RES:
850 printk("%s: MSG_SW_RELOAD_START_RES\n", __FUNCTION__);
851 complete(&coredev->reload_start_done);
852 break;
854 case MSG_SMS_DATA_DOWNLOAD_RES:
855 complete(&coredev->data_download_done);
856 break;
858 case MSG_SW_RELOAD_EXEC_RES:
859 printk("%s: MSG_SW_RELOAD_EXEC_RES\n", __FUNCTION__);
860 break;
862 case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
863 printk("%s: MSG_SMS_SWDOWNLOAD_TRIGGER_RES\n", __FUNCTION__);
864 complete(&coredev->trigger_done);
865 break;
867 case MSG_SMS_SLEEP_RESUME_COMP_IND:
868 complete(&coredev->resume_done);
869 break;
871 default:
872 printk(KERN_INFO "%s no client (%p) or error (%d), type:%d dstid:%d\n", __FUNCTION__, client, rc, phdr->msgType, phdr->msgDstId);
875 smscore_putbuffer(coredev, cb);
880 * return pointer to next free buffer descriptor from core pool
882 * @param coredev pointer to a coredev object returned by smscore_register_device
884 * @return pointer to descriptor on success, NULL on error.
886 smscore_buffer_t *smscore_getbuffer(smscore_device_t *coredev)
888 smscore_buffer_t *cb = NULL;
889 unsigned long flags;
891 spin_lock_irqsave(&coredev->bufferslock, flags);
893 if (!list_empty(&coredev->buffers))
895 cb = (smscore_buffer_t *) coredev->buffers.next;
896 list_del(&cb->entry);
899 spin_unlock_irqrestore(&coredev->bufferslock, flags);
901 return cb;
905 * return buffer descriptor to a pool
907 * @param coredev pointer to a coredev object returned by smscore_register_device
908 * @param cb pointer buffer descriptor
911 void smscore_putbuffer(smscore_device_t *coredev, smscore_buffer_t *cb)
913 list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
916 int smscore_validate_client(smscore_device_t *coredev, smscore_client_t *client, int id)
918 smscore_client_t *existing_client;
919 smscore_subclient_t *subclient;
921 if (!id)
922 return 0;
924 existing_client = smscore_getclient_by_id(coredev, id);
925 if (existing_client == client)
926 return 0;
928 if (existing_client)
929 return -EBUSY;
931 subclient = kzalloc(sizeof(smscore_subclient_t), GFP_KERNEL);
932 if (!subclient)
933 return -ENOMEM;
935 subclient->client = client;
936 subclient->id = id;
938 list_add_locked(&subclient->entry, &coredev->subclients, &coredev->clientslock);
940 return 0;
944 * creates smsclient object, check that id is taken by another client
946 * @param coredev pointer to a coredev object from clients hotplug
947 * @param initial_id all messages with this id would be sent to this client
948 * @param data_type all messages of this type would be sent to this client
949 * @param onresponse_handler client handler that is called to process incoming messages
950 * @param onremove_handler client handler that is called when device is removed
951 * @param context client-specific context
952 * @param client pointer to a value that receives created smsclient object
954 * @return 0 on success, <0 on error.
956 int smscore_register_client(smscore_device_t *coredev, smsclient_params_t *params, smscore_client_t **client)
958 smscore_client_t* newclient;
959 int rc;
961 // check that no other channel with same data type exists
962 if (params->data_type && smscore_getclient_by_type(coredev, params->data_type))
963 return -EEXIST;
965 newclient = kzalloc(sizeof(smscore_client_t), GFP_KERNEL);
966 if (!newclient)
967 return -ENOMEM;
969 // check that no other channel with same id exists
970 rc = smscore_validate_client(coredev, newclient, params->initial_id);
971 if (rc < 0)
973 kfree(newclient);
974 return rc;
977 newclient->coredev = coredev;
978 newclient->data_type = params->data_type;
979 newclient->onresponse_handler = params->onresponse_handler;
980 newclient->onremove_handler = params->onremove_handler;
981 newclient->context = params->context;
983 list_add_locked(&newclient->entry, &coredev->clients, &coredev->clientslock);
985 *client = newclient;
987 printk(KERN_INFO "%s %p %d %d\n", __FUNCTION__, params->context, params->data_type, params->initial_id);
989 return 0;
993 * frees smsclient object and all subclients associated with it
995 * @param client pointer to smsclient object returned by smscore_register_client
998 void smscore_unregister_client(smscore_client_t *client)
1000 smscore_device_t *coredev = client->coredev;
1001 struct list_head *next, *first;
1002 unsigned long flags;
1004 spin_lock_irqsave(&coredev->clientslock, flags);
1006 first = &coredev->subclients;
1008 for (next = first->next; next != first;)
1010 smscore_subclient_t *subclient = (smscore_subclient_t *) next;
1011 next = next->next;
1013 if (subclient->client == client)
1015 list_del(&subclient->entry);
1016 kfree(subclient);
1020 printk(KERN_INFO "%s %p %d\n", __FUNCTION__, client->context, client->data_type);
1022 list_del(&client->entry);
1023 kfree(client);
1025 spin_unlock_irqrestore(&coredev->clientslock, flags);
1029 * verifies that source id is not taken by another client,
1030 * calls device handler to send requests to the device
1032 * @param client pointer to smsclient object returned by smscore_register_client
1033 * @param buffer pointer to a request buffer
1034 * @param size size (in bytes) of request buffer
1036 * @return 0 on success, <0 on error.
1038 int smsclient_sendrequest(smscore_client_t *client, void *buffer, size_t size)
1040 smscore_device_t* coredev = client->coredev;
1041 SmsMsgHdr_ST* phdr = (SmsMsgHdr_ST*) buffer;
1043 // check that no other channel with same id exists
1044 int rc = smscore_validate_client(client->coredev, client, phdr->msgSrcId);
1045 if (rc < 0)
1046 return rc;
1048 return coredev->sendrequest_handler(coredev->context, buffer, size);
1052 * return the size of large (common) buffer
1054 * @param coredev pointer to a coredev object from clients hotplug
1056 * @return size (in bytes) of the buffer
1058 int smscore_get_common_buffer_size(smscore_device_t *coredev)
1060 return coredev->common_buffer_size;
1064 * maps common buffer (if supported by platform)
1066 * @param coredev pointer to a coredev object from clients hotplug
1067 * @param vma pointer to vma struct from mmap handler
1069 * @return 0 on success, <0 on error.
1071 int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct * vma)
1073 unsigned long end = vma->vm_end, start = vma->vm_start, size = PAGE_ALIGN(coredev->common_buffer_size);
1075 if (!(vma->vm_flags & (VM_READ | VM_SHARED)) || (vma->vm_flags & VM_WRITE))
1077 printk(KERN_INFO "%s invalid vm flags\n", __FUNCTION__);
1078 return -EINVAL;
1081 if ((end - start) != size)
1083 printk(KERN_INFO "%s invalid size %d expected %d\n", __FUNCTION__, (int)(end - start), (int) size);
1084 return -EINVAL;
1087 if (remap_pfn_range(vma, start, coredev->common_buffer_phys >> PAGE_SHIFT, size, pgprot_noncached(vma->vm_page_prot)))
1089 printk(KERN_INFO "%s remap_page_range failed\n", __FUNCTION__);
1090 return -EAGAIN;
1093 return 0;
1096 int smscore_module_init(void)
1098 int rc = 0;
1100 INIT_LIST_HEAD(&g_smscore_notifyees);
1101 INIT_LIST_HEAD(&g_smscore_devices);
1102 kmutex_init(&g_smscore_deviceslock);
1104 INIT_LIST_HEAD(&g_smscore_registry);
1105 kmutex_init(&g_smscore_registrylock);
1107 /* USB Register */
1108 rc = smsusb_register();
1110 /* DVB Register */
1111 rc = smsdvb_register();
1113 printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
1115 return rc;
1118 void smscore_module_exit(void)
1121 kmutex_lock(&g_smscore_deviceslock);
1122 while (!list_empty(&g_smscore_notifyees))
1124 smscore_device_notifyee_t *notifyee = (smscore_device_notifyee_t *) g_smscore_notifyees.next;
1126 list_del(&notifyee->entry);
1127 kfree(notifyee);
1129 kmutex_unlock(&g_smscore_deviceslock);
1131 kmutex_lock(&g_smscore_registrylock);
1132 while (!list_empty(&g_smscore_registry))
1134 smscore_registry_entry_t *entry = (smscore_registry_entry_t *) g_smscore_registry.next;
1136 list_del(&entry->entry);
1137 kfree(entry);
1139 kmutex_unlock(&g_smscore_registrylock);
1141 /* DVB UnRegister */
1142 smsdvb_unregister();
1144 /* Unregister USB */
1145 smsusb_unregister();
1147 printk(KERN_INFO "%s\n", __FUNCTION__);
1150 module_init(smscore_module_init);
1151 module_exit(smscore_module_exit);
1153 MODULE_DESCRIPTION("smscore");
1154 MODULE_AUTHOR("Anatoly Greenblatt,,, (anatolyg@siano-ms.com)");
1155 MODULE_LICENSE("GPL");
1157 EXPORT_SYMBOL(smscore_registry_setmode);
1158 EXPORT_SYMBOL(smscore_registry_getmode);
1159 EXPORT_SYMBOL(smscore_register_hotplug);
1160 EXPORT_SYMBOL(smscore_unregister_hotplug);
1161 EXPORT_SYMBOL(smscore_register_device);
1162 EXPORT_SYMBOL(smscore_unregister_device);
1163 EXPORT_SYMBOL(smscore_start_device);
1164 EXPORT_SYMBOL(smscore_load_firmware);
1165 EXPORT_SYMBOL(smscore_set_device_mode);
1166 EXPORT_SYMBOL(smscore_get_device_mode);
1167 EXPORT_SYMBOL(smscore_register_client);
1168 EXPORT_SYMBOL(smscore_unregister_client);
1169 EXPORT_SYMBOL(smsclient_sendrequest);
1170 EXPORT_SYMBOL(smscore_onresponse);
1171 EXPORT_SYMBOL(smscore_get_common_buffer_size);
1172 EXPORT_SYMBOL(smscore_map_common_buffer);
1173 EXPORT_SYMBOL(smscore_getbuffer);
1174 EXPORT_SYMBOL(smscore_putbuffer);