smscoreapi.c 35.1 KB
Newer Older
1
/*
2 3 4 5
 *  Siano core API module
 *
 *  This file contains implementation for the interface to sms core component
 *
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 15
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
16
 *
17
 *  See the GNU General Public License for more details.
18 19 20 21 22 23
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

24 25 26 27 28 29
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
30
#include <linux/io.h>
31 32 33 34

#include <linux/firmware.h>

#include "smscoreapi.h"
35
#include "sms-cards.h"
36 37
#include "smsir.h"
#include "smsendian.h"
38

39
static int sms_dbg;
40
module_param_named(debug, sms_dbg, int, 0644);
41 42
MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");

43
struct smscore_device_notifyee_t {
44 45
	struct list_head entry;
	hotplug_t hotplug;
46
};
47

48
struct smscore_idlist_t {
49 50 51
	struct list_head entry;
	int		id;
	int		data_type;
52
};
53

54
struct smscore_client_t {
55
	struct list_head entry;
56
	struct smscore_device_t *coredev;
57
	void			*context;
58
	struct list_head 	idlist;
59 60
	onresponse_t	onresponse_handler;
	onremove_t		onremove_handler;
61
};
62

63 64 65 66 67
void smscore_set_board_id(struct smscore_device_t *core, int id)
{
	core->board_id = id;
}

68 69 70 71 72 73
int smscore_led_state(struct smscore_device_t *core, int led)
{
	if (led >= 0)
		core->led_state = led;
	return core->led_state;
}
74
EXPORT_SYMBOL_GPL(smscore_set_board_id);
75

76 77 78 79
int smscore_get_board_id(struct smscore_device_t *core)
{
	return core->board_id;
}
80
EXPORT_SYMBOL_GPL(smscore_get_board_id);
81

82
struct smscore_registry_entry_t {
83 84 85
	struct list_head entry;
	char			devpath[32];
	int				mode;
86 87
	enum sms_device_type_st	type;
};
88

89 90 91
static struct list_head g_smscore_notifyees;
static struct list_head g_smscore_devices;
static struct mutex g_smscore_deviceslock;
92

93 94
static struct list_head g_smscore_registry;
static struct mutex g_smscore_registrylock;
95

96
static int default_mode = 4;
97

98 99 100
module_param(default_mode, int, 0644);
MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");

101
static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
102
{
103
	struct smscore_registry_entry_t *entry;
104 105 106
	struct list_head *next;

	kmutex_lock(&g_smscore_registrylock);
107 108 109
	for (next = g_smscore_registry.next;
	     next != &g_smscore_registry;
	     next = next->next) {
110
		entry = (struct smscore_registry_entry_t *) next;
111
		if (!strcmp(entry->devpath, devpath)) {
112
			kmutex_unlock(&g_smscore_registrylock);
113
			return entry;
114 115
		}
	}
116 117 118
	entry = (struct smscore_registry_entry_t *)
			kmalloc(sizeof(struct smscore_registry_entry_t),
				GFP_KERNEL);
119
	if (entry) {
120 121 122
		entry->mode = default_mode;
		strcpy(entry->devpath, devpath);
		list_add(&entry->entry, &g_smscore_registry);
123
	} else
124
		sms_err("failed to create smscore_registry.");
125
	kmutex_unlock(&g_smscore_registrylock);
126 127
	return entry;
}
128

129
int smscore_registry_getmode(char *devpath)
130
{
131
	struct smscore_registry_entry_t *entry;
132

133 134
	entry = smscore_find_registry(devpath);
	if (entry)
135 136
		return entry->mode;
	else
137
		sms_err("No registry found.");
138

139 140
	return default_mode;
}
141
EXPORT_SYMBOL_GPL(smscore_registry_getmode);
142

143
static enum sms_device_type_st smscore_registry_gettype(char *devpath)
144
{
145
	struct smscore_registry_entry_t *entry;
146

147 148
	entry = smscore_find_registry(devpath);
	if (entry)
149 150
		return entry->type;
	else
151
		sms_err("No registry found.");
152

153 154
	return -1;
}
155

156 157
void smscore_registry_setmode(char *devpath, int mode)
{
158
	struct smscore_registry_entry_t *entry;
159

160 161 162
	entry = smscore_find_registry(devpath);
	if (entry)
		entry->mode = mode;
163
	else
164
		sms_err("No registry found.");
165
}
166

167 168
static void smscore_registry_settype(char *devpath,
				     enum sms_device_type_st type)
169
{
170
	struct smscore_registry_entry_t *entry;
171

172 173
	entry = smscore_find_registry(devpath);
	if (entry)
174 175
		entry->type = type;
	else
176
		sms_err("No registry found.");
177 178 179
}


180 181
static void list_add_locked(struct list_head *new, struct list_head *head,
			    spinlock_t *lock)
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
{
	unsigned long flags;

	spin_lock_irqsave(lock, flags);

	list_add(new, head);

	spin_unlock_irqrestore(lock, flags);
}

/**
 * register a client callback that called when device plugged in/unplugged
 * NOTE: if devices exist callback is called immediately for each device
 *
 * @param hotplug callback
 *
 * @return 0 on success, <0 on error.
 */
int smscore_register_hotplug(hotplug_t hotplug)
{
202
	struct smscore_device_notifyee_t *notifyee;
203 204 205 206 207
	struct list_head *next, *first;
	int rc = 0;

	kmutex_lock(&g_smscore_deviceslock);

208 209
	notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t),
			   GFP_KERNEL);
210 211
	if (notifyee) {
		/* now notify callback about existing devices */
212
		first = &g_smscore_devices;
213 214 215
		for (next = first->next;
		     next != first && !rc;
		     next = next->next) {
216 217
			struct smscore_device_t *coredev =
				(struct smscore_device_t *) next;
218 219 220
			rc = hotplug(coredev, coredev->device, 1);
		}

221
		if (rc >= 0) {
222 223
			notifyee->hotplug = hotplug;
			list_add(&notifyee->entry, &g_smscore_notifyees);
224
		} else
225
			kfree(notifyee);
226
	} else
227 228 229 230 231 232
		rc = -ENOMEM;

	kmutex_unlock(&g_smscore_deviceslock);

	return rc;
}
233
EXPORT_SYMBOL_GPL(smscore_register_hotplug);
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

/**
 * unregister a client callback that called when device plugged in/unplugged
 *
 * @param hotplug callback
 *
 */
void smscore_unregister_hotplug(hotplug_t hotplug)
{
	struct list_head *next, *first;

	kmutex_lock(&g_smscore_deviceslock);

	first = &g_smscore_notifyees;

249
	for (next = first->next; next != first;) {
250 251
		struct smscore_device_notifyee_t *notifyee =
			(struct smscore_device_notifyee_t *) next;
252 253
		next = next->next;

254
		if (notifyee->hotplug == hotplug) {
255 256 257 258 259 260 261
			list_del(&notifyee->entry);
			kfree(notifyee);
		}
	}

	kmutex_unlock(&g_smscore_deviceslock);
}
262
EXPORT_SYMBOL_GPL(smscore_unregister_hotplug);
263

264
static void smscore_notify_clients(struct smscore_device_t *coredev)
265
{
266
	struct smscore_client_t *client;
267

268 269
	/* the client must call smscore_unregister_client from remove handler */
	while (!list_empty(&coredev->clients)) {
270
		client = (struct smscore_client_t *) coredev->clients.next;
271 272 273 274
		client->onremove_handler(client->context);
	}
}

275 276
static int smscore_notify_callbacks(struct smscore_device_t *coredev,
				    struct device *device, int arrival)
277 278 279 280
{
	struct list_head *next, *first;
	int rc = 0;

281
	/* note: must be called under g_deviceslock */
282 283 284

	first = &g_smscore_notifyees;

285
	for (next = first->next; next != first; next = next->next) {
286
		rc = ((struct smscore_device_notifyee_t *) next)->
287
				hotplug(coredev, device, arrival);
288 289 290 291 292 293 294
		if (rc < 0)
			break;
	}

	return rc;
}

295 296
static struct
smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
297
				       dma_addr_t common_buffer_phys)
298
{
299 300
	struct smscore_buffer_t *cb =
		kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL);
301
	if (!cb) {
302
		sms_info("kmalloc(...) failed");
303 304 305 306
		return NULL;
	}

	cb->p = buffer;
307
	cb->offset_in_common = buffer - (u8 *) common_buffer;
308 309 310 311 312 313
	cb->phys = common_buffer_phys + cb->offset_in_common;

	return cb;
}

/**
314 315
 * creates coredev object for a device, prepares buffers,
 * creates buffer mappings, notifies registered hotplugs about new device.
316
 *
317 318
 * @param params device pointer to struct with device specific parameters
 *               and handlers
319 320 321 322
 * @param coredev pointer to a value that receives created coredev object
 *
 * @return 0 on success, <0 on error.
 */
323 324
int smscore_register_device(struct smsdevice_params_t *params,
			    struct smscore_device_t **coredev)
325
{
326
	struct smscore_device_t *dev;
327 328
	u8 *buffer;

329
	dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL);
330
	if (!dev) {
331
		sms_info("kzalloc(...) failed");
332 333 334
		return -ENOMEM;
	}

335
	/* init list entry so it could be safe in smscore_unregister_device */
336 337
	INIT_LIST_HEAD(&dev->entry);

338
	/* init queues */
339 340 341
	INIT_LIST_HEAD(&dev->clients);
	INIT_LIST_HEAD(&dev->buffers);

342
	/* init locks */
343 344 345
	spin_lock_init(&dev->clientslock);
	spin_lock_init(&dev->bufferslock);

346
	/* init completion events */
347 348 349 350 351 352
	init_completion(&dev->version_ex_done);
	init_completion(&dev->data_download_done);
	init_completion(&dev->trigger_done);
	init_completion(&dev->init_device_done);
	init_completion(&dev->reload_start_done);
	init_completion(&dev->resume_done);
353
	init_completion(&dev->ir_init_done);
354

355
	/* alloc common buffer */
356
	dev->common_buffer_size = params->buffer_size * params->num_buffers;
357 358 359 360
	dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,
						&dev->common_buffer_phys,
						GFP_KERNEL | GFP_DMA);
	if (!dev->common_buffer) {
361 362 363 364
		smscore_unregister_device(dev);
		return -ENOMEM;
	}

365 366 367
	/* prepare dma buffers */
	for (buffer = dev->common_buffer;
	     dev->num_buffers < params->num_buffers;
368
	     dev->num_buffers++, buffer += params->buffer_size) {
369
		struct smscore_buffer_t *cb =
370 371
			smscore_createbuffer(buffer, dev->common_buffer,
					     dev->common_buffer_phys);
372
		if (!cb) {
373 374 375 376 377 378 379
			smscore_unregister_device(dev);
			return -ENOMEM;
		}

		smscore_putbuffer(dev, cb);
	}

380
	sms_info("allocated %d buffers", dev->num_buffers);
381 382 383 384 385 386 387 388 389 390 391 392 393

	dev->mode = DEVICE_MODE_NONE;
	dev->context = params->context;
	dev->device = params->device;
	dev->setmode_handler = params->setmode_handler;
	dev->detectmode_handler = params->detectmode_handler;
	dev->sendrequest_handler = params->sendrequest_handler;
	dev->preload_handler = params->preload_handler;
	dev->postload_handler = params->postload_handler;

	dev->device_flags = params->flags;
	strcpy(dev->devpath, params->devpath);

394
	smscore_registry_settype(dev->devpath, params->device_type);
395

396
	/* add device to devices list */
397 398 399 400 401 402
	kmutex_lock(&g_smscore_deviceslock);
	list_add(&dev->entry, &g_smscore_devices);
	kmutex_unlock(&g_smscore_deviceslock);

	*coredev = dev;

403
	sms_info("device %p created", dev);
404 405 406

	return 0;
}
407
EXPORT_SYMBOL_GPL(smscore_register_device);
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,
		void *buffer, size_t size, struct completion *completion) {
	int rc = coredev->sendrequest_handler(coredev->context, buffer, size);
	if (rc < 0) {
		sms_info("sendrequest returned error %d", rc);
		return rc;
	}

	return wait_for_completion_timeout(completion,
			msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ?
			0 : -ETIME;
}

/**
 * Starts & enables IR operations
 *
 * @return 0 on success, < 0 on error.
 */
static int smscore_init_ir(struct smscore_device_t *coredev)
{
	int ir_io;
	int rc;
	void *buffer;

	coredev->ir.input_dev = NULL;
	ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir;
	if (ir_io) {/* only if IR port exist we use IR sub-module */
		sms_info("IR loading");
		rc = sms_ir_init(coredev);

		if	(rc != 0)
			sms_err("Error initialization DTV IR sub-module");
		else {
			buffer = kmalloc(sizeof(struct SmsMsgData_ST2) +
						SMS_DMA_ALIGNMENT,
						GFP_KERNEL | GFP_DMA);
			if (buffer) {
				struct SmsMsgData_ST2 *msg =
				(struct SmsMsgData_ST2 *)
				SMS_ALIGN_ADDRESS(buffer);

				SMS_INIT_MSG(&msg->xMsgHeader,
						MSG_SMS_START_IR_REQ,
						sizeof(struct SmsMsgData_ST2));
				msg->msgData[0] = coredev->ir.controller;
				msg->msgData[1] = coredev->ir.timeout;

				smsendian_handle_tx_message(
					(struct SmsMsgHdr_ST2 *)msg);
				rc = smscore_sendrequest_and_wait(coredev, msg,
						msg->xMsgHeader. msgLength,
						&coredev->ir_init_done);

				kfree(buffer);
			} else
				sms_err
				("Sending IR initialization message failed");
		}
	} else
		sms_info("IR port has not been detected");

	return 0;
}

474 475 476
/**
 * sets initial device mode and notifies client hotplugs that device is ready
 *
477 478
 * @param coredev pointer to a coredev object returned by
 * 		  smscore_register_device
479 480 481
 *
 * @return 0 on success, <0 on error.
 */
482
int smscore_start_device(struct smscore_device_t *coredev)
483
{
484 485
	int rc = smscore_set_device_mode(
			coredev, smscore_registry_getmode(coredev->devpath));
486
	if (rc < 0) {
487
		sms_info("set device mode faile , rc %d", rc);
488
		return rc;
489
	}
490 491 492 493

	kmutex_lock(&g_smscore_deviceslock);

	rc = smscore_notify_callbacks(coredev, coredev->device, 1);
494
	smscore_init_ir(coredev);
495

496
	sms_info("device %p started, rc %d", coredev, rc);
497 498 499 500 501

	kmutex_unlock(&g_smscore_deviceslock);

	return rc;
}
502
EXPORT_SYMBOL_GPL(smscore_start_device);
503 504


505 506
static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
					 void *buffer, size_t size)
507
{
508 509
	struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer;
	struct SmsMsgHdr_ST *msg;
510
	u32 mem_address = firmware->StartAddress;
511
	u8 *payload = firmware->Payload;
512 513
	int rc = 0;

514 515
	sms_info("loading FW to addr 0x%x size %d",
		 mem_address, firmware->Length);
516
	if (coredev->preload_handler) {
517 518 519 520 521
		rc = coredev->preload_handler(coredev->context);
		if (rc < 0)
			return rc;
	}

522
	/* PAGE_SIZE buffer shall be enough and dma aligned */
523
	msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
524 525 526
	if (!msg)
		return -ENOMEM;

527
	if (coredev->mode != DEVICE_MODE_NONE) {
528
		sms_debug("sending reload command.");
529
		SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ,
530
			     sizeof(struct SmsMsgHdr_ST));
531 532 533
		rc = smscore_sendrequest_and_wait(coredev, msg,
						  msg->msgLength,
						  &coredev->reload_start_done);
534
		mem_address = *(u32 *) &payload[20];
535 536
	}

537
	while (size && rc >= 0) {
538 539
		struct SmsDataDownload_ST *DataMsg =
			(struct SmsDataDownload_ST *) msg;
540 541
		int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);

542
		SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ,
543
			     (u16)(sizeof(struct SmsMsgHdr_ST) +
544
				      sizeof(u32) + payload_size));
545 546 547 548

		DataMsg->MemAddr = mem_address;
		memcpy(DataMsg->Payload, payload, payload_size);

549 550
		if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) &&
		    (coredev->mode == DEVICE_MODE_NONE))
551 552 553
			rc = coredev->sendrequest_handler(
				coredev->context, DataMsg,
				DataMsg->xMsgHeader.msgLength);
554
		else
555 556 557 558
			rc = smscore_sendrequest_and_wait(
				coredev, DataMsg,
				DataMsg->xMsgHeader.msgLength,
				&coredev->data_download_done);
559 560 561 562 563 564

		payload += payload_size;
		size -= payload_size;
		mem_address += payload_size;
	}

565 566
	if (rc >= 0) {
		if (coredev->mode == DEVICE_MODE_NONE) {
567 568
			struct SmsMsgData_ST *TriggerMsg =
				(struct SmsMsgData_ST *) msg;
569

570
			SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,
571
				     sizeof(struct SmsMsgHdr_ST) +
572
				     sizeof(u32) * 5);
573

574 575
			TriggerMsg->msgData[0] = firmware->StartAddress;
						/* Entry point */
576 577 578 579
			TriggerMsg->msgData[1] = 5; /* Priority */
			TriggerMsg->msgData[2] = 0x200; /* Stack size */
			TriggerMsg->msgData[3] = 0; /* Parameter */
			TriggerMsg->msgData[4] = 4; /* Task ID */
580

581
			if (coredev->device_flags & SMS_ROM_NO_RESPONSE) {
582 583 584
				rc = coredev->sendrequest_handler(
					coredev->context, TriggerMsg,
					TriggerMsg->xMsgHeader.msgLength);
585
				msleep(100);
586
			} else
587 588 589 590
				rc = smscore_sendrequest_and_wait(
					coredev, TriggerMsg,
					TriggerMsg->xMsgHeader.msgLength,
					&coredev->trigger_done);
591 592
		} else {
			SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ,
593
				     sizeof(struct SmsMsgHdr_ST));
594

595 596
			rc = coredev->sendrequest_handler(coredev->context,
							  msg, msg->msgLength);
597
		}
598
		msleep(500);
599 600
	}

601
	sms_debug("rc=%d, postload=%p ", rc,
602
		  coredev->postload_handler);
603 604 605

	kfree(msg);

606
	return ((rc >= 0) && coredev->postload_handler) ?
607 608 609 610 611 612 613
		coredev->postload_handler(coredev->context) :
		rc;
}

/**
 * loads specified firmware into a buffer and calls device loadfirmware_handler
 *
614 615
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
616 617 618 619 620
 * @param filename null-terminated string specifies firmware file name
 * @param loadfirmware_handler device handler that loads firmware
 *
 * @return 0 on success, <0 on error.
 */
621 622 623
static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,
					   char *filename,
					   loadfirmware_t loadfirmware_handler)
624 625 626
{
	int rc = -ENOENT;
	const struct firmware *fw;
627
	u8 *fw_buffer;
628

629 630
	if (loadfirmware_handler == NULL && !(coredev->device_flags &
					      SMS_DEVICE_FAMILY2))
631 632 633
		return -EINVAL;

	rc = request_firmware(&fw, filename, coredev->device);
634
	if (rc < 0) {
635
		sms_info("failed to open \"%s\"", filename);
636 637
		return rc;
	}
638
	sms_info("read FW %s, size=%zd", filename, fw->size);
639 640 641
	fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),
			    GFP_KERNEL | GFP_DMA);
	if (fw_buffer) {
642 643 644
		memcpy(fw_buffer, fw->data, fw->size);

		rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?
645 646 647 648 649
		      smscore_load_firmware_family2(coredev,
						    fw_buffer,
						    fw->size) :
		      loadfirmware_handler(coredev->context,
					   fw_buffer, fw->size);
650 651

		kfree(fw_buffer);
652
	} else {
653
		sms_info("failed to allocate firmware buffer");
654 655 656 657 658 659 660 661 662
		rc = -ENOMEM;
	}

	release_firmware(fw);

	return rc;
}

/**
663 664
 * notifies all clients registered with the device, notifies hotplugs,
 * frees all buffers and coredev object
665
 *
666 667
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
668 669 670
 *
 * @return 0 on success, <0 on error.
 */
671
void smscore_unregister_device(struct smscore_device_t *coredev)
672
{
673
	struct smscore_buffer_t *cb;
674
	int num_buffers = 0;
675
	int retry = 0;
676 677 678

	kmutex_lock(&g_smscore_deviceslock);

679 680 681
	/* Release input device (IR) resources */
	sms_ir_exit(coredev);

682 683 684
	smscore_notify_clients(coredev);
	smscore_notify_callbacks(coredev, NULL, 0);

685 686
	/* at this point all buffers should be back
	 * onresponse must no longer be called */
687

688 689
	while (1) {
		while ((cb = smscore_getbuffer(coredev))) {
690
			kfree(cb);
691
			num_buffers++;
692 693 694
		}
		if (num_buffers == coredev->num_buffers)
			break;
695
		if (++retry > 10) {
696 697
			sms_info("exiting although "
				 "not all buffers released.");
698 699
			break;
		}
700

701
		sms_info("waiting for %d buffer(s)",
702
			 coredev->num_buffers - num_buffers);
703 704 705
		msleep(100);
	}

706
	sms_info("freed %d buffers", num_buffers);
707 708

	if (coredev->common_buffer)
709 710 711
		dma_free_coherent(NULL, coredev->common_buffer_size,
				  coredev->common_buffer,
				  coredev->common_buffer_phys);
712 713 714 715 716 717

	list_del(&coredev->entry);
	kfree(coredev);

	kmutex_unlock(&g_smscore_deviceslock);

718
	sms_info("device %p destroyed", coredev);
719
}
720
EXPORT_SYMBOL_GPL(smscore_unregister_device);
721

722
static int smscore_detect_mode(struct smscore_device_t *coredev)
723
{
724
	void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT,
725
			       GFP_KERNEL | GFP_DMA);
726 727
	struct SmsMsgHdr_ST *msg =
		(struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);
728 729 730 731 732
	int rc;

	if (!buffer)
		return -ENOMEM;

733 734
	SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ,
		     sizeof(struct SmsMsgHdr_ST));
735

736 737 738
	rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength,
					  &coredev->version_ex_done);
	if (rc == -ETIME) {
739
		sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try");
740

741 742
		if (wait_for_completion_timeout(&coredev->resume_done,
						msecs_to_jiffies(5000))) {
743 744 745
			rc = smscore_sendrequest_and_wait(
				coredev, msg, msg->msgLength,
				&coredev->version_ex_done);
746
			if (rc < 0)
747 748
				sms_err("MSG_SMS_GET_VERSION_EX_REQ failed "
					"second try, rc %d", rc);
749
		} else
750 751 752 753 754 755 756 757
			rc = -ETIME;
	}

	kfree(buffer);

	return rc;
}

758
static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = {
759 760 761 762 763 764
	/*Stellar		NOVA A0		Nova B0		VEGA*/
	/*DVBT*/
	{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
	/*DVBH*/
	{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
	/*TDMB*/
765
	{"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"},
766 767 768 769 770 771 772 773 774 775
	/*DABIP*/
	{"none", "none", "none", "none"},
	/*BDA*/
	{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
	/*ISDBT*/
	{"none", "isdbt_nova_12mhz.inp", "dvb_nova_12mhz.inp", "none"},
	/*ISDBTBDA*/
	{"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"},
	/*CMMB*/
	{"none", "none", "none", "cmmb_vega_12mhz.inp"}
776 777
};

778 779 780 781 782 783
static inline char *sms_get_fw_name(struct smscore_device_t *coredev,
				    int mode, enum sms_device_type_st type)
{
	char **fw = sms_get_board(smscore_get_board_id(coredev))->fw;
	return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type];
}
784

785 786 787 788
/**
 * calls device handler to change mode of operation
 * NOTE: stellar/usb may disconnect when changing mode
 *
789 790
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
791 792 793 794
 * @param mode requested mode of operation
 *
 * @return 0 on success, <0 on error.
 */
795
int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
796 797 798
{
	void *buffer;
	int rc = 0;
799
	enum sms_device_type_st type;
800

801
	sms_debug("set device mode to %d", mode);
802 803
	if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
		if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) {
804
			sms_err("invalid mode specified %d", mode);
805 806 807
			return -EINVAL;
		}

808 809
		smscore_registry_setmode(coredev->devpath, mode);

810
		if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {
811
			rc = smscore_detect_mode(coredev);
812
			if (rc < 0) {
813
				sms_err("mode detect failed %d", rc);
814
				return rc;
815
			}
816
		}
817

818
		if (coredev->mode == mode) {
819
			sms_info("device mode %d already set", mode);
820 821 822
			return 0;
		}

823
		if (!(coredev->modes_supported & (1 << mode))) {
824 825
			char *fw_filename;

826
			type = smscore_registry_gettype(coredev->devpath);
827 828 829 830
			fw_filename = sms_get_fw_name(coredev, mode, type);

			rc = smscore_load_firmware_from_file(coredev,
							     fw_filename, NULL);
831
			if (rc < 0) {
832 833 834
				sms_warn("error %d loading firmware: %s, "
					 "trying again with default firmware",
					 rc, fw_filename);
835 836

				/* try again with the default firmware */
837
				fw_filename = smscore_fw_lkup[mode][type];
838
				rc = smscore_load_firmware_from_file(coredev,
839
							     fw_filename, NULL);
840 841

				if (rc < 0) {
842 843 844
					sms_warn("error %d loading "
						 "firmware: %s", rc,
						 fw_filename);
845 846
					return rc;
				}
847
			}
848
			sms_log("firmware download success: %s", fw_filename);
849
		} else
850 851
			sms_info("mode %d supported by running "
				 "firmware", mode);
852

853 854
		buffer = kmalloc(sizeof(struct SmsMsgData_ST) +
				 SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
855
		if (buffer) {
856 857 858
			struct SmsMsgData_ST *msg =
				(struct SmsMsgData_ST *)
					SMS_ALIGN_ADDRESS(buffer);
859

860
			SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ,
861
				     sizeof(struct SmsMsgData_ST));
862 863
			msg->msgData[0] = mode;

864 865 866
			rc = smscore_sendrequest_and_wait(
				coredev, msg, msg->xMsgHeader.msgLength,
				&coredev->init_device_done);
867 868

			kfree(buffer);
869
		} else {
870 871
			sms_err("Could not allocate buffer for "
				"init device message.");
872
			rc = -ENOMEM;
873 874 875
		}
	} else {
		if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
876
			sms_err("invalid mode specified %d", mode);
877 878 879 880 881
			return -EINVAL;
		}

		smscore_registry_setmode(coredev->devpath, mode);

882
		if (coredev->detectmode_handler)
883 884
			coredev->detectmode_handler(coredev->context,
						    &coredev->mode);
885 886 887 888 889

		if (coredev->mode != mode && coredev->setmode_handler)
			rc = coredev->setmode_handler(coredev->context, mode);
	}

890
	if (rc >= 0) {
891 892 893 894
		coredev->mode = mode;
		coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
	}

895
	if (rc != 0)
896
		sms_err("return error code %d.", rc);
897 898 899 900 901 902
	return rc;
}

/**
 * calls device handler to get current mode of operation
 *
903 904
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
905 906 907
 *
 * @return current mode
 */
908
int smscore_get_device_mode(struct smscore_device_t *coredev)
909 910 911
{
	return coredev->mode;
}
912
EXPORT_SYMBOL_GPL(smscore_get_device_mode);
913

914 915 916 917
/**
 * find client by response id & type within the clients list.
 * return client handle or NULL.
 *
918 919
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
920
 * @param data_type client data type (SMS_DONT_CARE for all types)
921
 * @param id client id (SMS_DONT_CARE for all id)
922 923
 *
 */
924 925
static struct
smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,
926
				      int data_type, int id)
927
{
928
	struct smscore_client_t *client = NULL;
929 930
	struct list_head *next, *first;
	unsigned long flags;
931
	struct list_head *firstid, *nextid;
932 933 934 935


	spin_lock_irqsave(&coredev->clientslock, flags);
	first = &coredev->clients;
936 937 938
	for (next = first->next;
	     (next != first) && !client;
	     next = next->next) {
939
		firstid = &((struct smscore_client_t *)next)->idlist;
940 941 942
		for (nextid = firstid->next;
		     nextid != firstid;
		     nextid = nextid->next) {
943 944 945 946
			if ((((struct smscore_idlist_t *)nextid)->id == id) &&
			    (((struct smscore_idlist_t *)nextid)->data_type == data_type ||
			    (((struct smscore_idlist_t *)nextid)->data_type == 0))) {
				client = (struct smscore_client_t *) next;
947 948
				break;
			}
949 950 951 952 953 954 955 956 957 958
		}
	}
	spin_unlock_irqrestore(&coredev->clientslock, flags);
	return client;
}

/**
 * find client by response id/type, call clients onresponse handler
 * return buffer to pool on error
 *
959 960
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
961 962 963
 * @param cb pointer to response buffer descriptor
 *
 */
964
void smscore_onresponse(struct smscore_device_t *coredev,
965 966 967 968
		struct smscore_buffer_t *cb) {
	struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p
			+ cb->offset);
	struct smscore_client_t *client;
969
	int rc = -EBUSY;
970 971
	static unsigned long last_sample_time; /* = 0; */
	static int data_total; /* = 0; */
972 973 974 975 976
	unsigned long time_now = jiffies_to_msecs(jiffies);

	if (!last_sample_time)
		last_sample_time = time_now;

977
	if (time_now - last_sample_time > 10000) {
978
		sms_debug("\ndata rate %d bytes/secs",
979 980
			  (int)((data_total * 1000) /
				(time_now - last_sample_time)));
981 982 983 984 985 986

		last_sample_time = time_now;
		data_total = 0;
	}

	data_total += cb->size;
987 988 989 990 991 992 993 994 995 996
	/* Do we need to re-route? */
	if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) ||
			(phdr->msgType == MSG_SMS_TRANSMISSION_IND)) {
		if (coredev->mode == DEVICE_MODE_DVBT_BDA)
			phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID;
	}


	client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId);

997 998
	/* If no client registered for type & id,
	 * check for control client where type is not registered */
999 1000 1001
	if (client)
		rc = client->onresponse_handler(client->context, cb);

1002 1003 1004
	if (rc < 0) {
		switch (phdr->msgType) {
		case MSG_SMS_GET_VERSION_EX_RES:
1005
		{
1006 1007
			struct SmsVersionRes_ST *ver =
				(struct SmsVersionRes_ST *) phdr;
1008 1009
			sms_debug("MSG_SMS_GET_VERSION_EX_RES "
				  "id %d prots 0x%x ver %d.%d",
1010 1011
				  ver->FirmwareId, ver->SupportedProtocols,
				  ver->RomVersionMajor, ver->RomVersionMinor);
1012

1013 1014 1015
			coredev->mode = ver->FirmwareId == 255 ?
				DEVICE_MODE_NONE : ver->FirmwareId;
			coredev->modes_supported = ver->SupportedProtocols;
1016

1017 1018 1019 1020
			complete(&coredev->version_ex_done);
			break;
		}
		case MSG_SMS_INIT_DEVICE_RES:
1021
			sms_debug("MSG_SMS_INIT_DEVICE_RES");
1022 1023 1024
			complete(&coredev->init_device_done);
			break;
		case MSG_SW_RELOAD_START_RES:
1025
			sms_debug("MSG_SW_RELOAD_START_RES");
1026 1027 1028 1029 1030 1031
			complete(&coredev->reload_start_done);
			break;
		case MSG_SMS_DATA_DOWNLOAD_RES:
			complete(&coredev->data_download_done);
			break;
		case MSG_SW_RELOAD_EXEC_RES:
1032
			sms_debug("MSG_SW_RELOAD_EXEC_RES");
1033 1034
			break;
		case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:
1035
			sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES");
1036 1037 1038 1039 1040
			complete(&coredev->trigger_done);
			break;
		case MSG_SMS_SLEEP_RESUME_COMP_IND:
			complete(&coredev->resume_done);
			break;
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
		case MSG_SMS_START_IR_RES:
			complete(&coredev->ir_init_done);
			break;
		case MSG_SMS_IR_SAMPLES_IND:
			sms_ir_event(coredev,
				(const char *)
				((char *)phdr
				+ sizeof(struct SmsMsgHdr_ST)),
				(int)phdr->msgLength
				- sizeof(struct SmsMsgHdr_ST));
			break;

1053 1054
		default:
			break;
1055 1056 1057 1058
		}
		smscore_putbuffer(coredev, cb);
	}
}
1059
EXPORT_SYMBOL_GPL(smscore_onresponse);
1060 1061 1062 1063

/**
 * return pointer to next free buffer descriptor from core pool
 *
1064 1065
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
1066 1067 1068
 *
 * @return pointer to descriptor on success, NULL on error.
 */
1069
struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
1070
{
1071
	struct smscore_buffer_t *cb = NULL;
1072 1073 1074 1075
	unsigned long flags;

	spin_lock_irqsave(&coredev->bufferslock, flags);

1076
	if (!list_empty(&coredev->buffers)) {
1077
		cb = (struct smscore_buffer_t *) coredev->buffers.next;
1078 1079 1080 1081 1082 1083 1084
		list_del(&cb->entry);
	}

	spin_unlock_irqrestore(&coredev->bufferslock, flags);

	return cb;
}
1085
EXPORT_SYMBOL_GPL(smscore_getbuffer);
1086 1087 1088 1089

/**
 * return buffer descriptor to a pool
 *
1090 1091
 * @param coredev pointer to a coredev object returned by
 *                smscore_register_device
1092 1093 1094
 * @param cb pointer buffer descriptor
 *
 */
1095 1096
void smscore_putbuffer(struct smscore_device_t *coredev,
		       struct smscore_buffer_t *cb)
1097 1098 1099
{
	list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
}
1100
EXPORT_SYMBOL_GPL(smscore_putbuffer);
1101

1102 1103 1104
static int smscore_validate_client(struct smscore_device_t *coredev,
				   struct smscore_client_t *client,
				   int data_type, int id)
1105
{
1106 1107
	struct smscore_idlist_t *listentry;
	struct smscore_client_t *registered_client;
1108

1109
	if (!client) {
1110
		sms_err("bad parameter.");
1111 1112 1113
		return -EFAULT;
	}
	registered_client = smscore_find_client(coredev, data_type, id);
1114
	if (registered_client == client)
1115
		return 0;
1116

1117
	if (registered_client) {
1118
		sms_err("The msg ID already registered to another client.");
1119 1120
		return -EEXIST;
	}
1121
	listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL);
1122
	if (!listentry) {
1123
		sms_err("Can't allocate memory for client id.");
1124
		return -ENOMEM;
1125 1126 1127
	}
	listentry->id = id;
	listentry->data_type = data_type;
1128 1129
	list_add_locked(&listentry->entry, &client->idlist,
			&coredev->clientslock);
1130 1131 1132 1133 1134 1135 1136 1137 1138
	return 0;
}

/**
 * creates smsclient object, check that id is taken by another client
 *
 * @param coredev pointer to a coredev object from clients hotplug
 * @param initial_id all messages with this id would be sent to this client
 * @param data_type all messages of this type would be sent to this client
1139 1140
 * @param onresponse_handler client handler that is called to
 *                           process incoming messages
1141 1142 1143 1144 1145 1146
 * @param onremove_handler client handler that is called when device is removed
 * @param context client-specific context
 * @param client pointer to a value that receives created smsclient object
 *
 * @return 0 on success, <0 on error.
 */
1147 1148 1149
int smscore_register_client(struct smscore_device_t *coredev,
			    struct smsclient_params_t *params,
			    struct smscore_client_t **client)
1150
{
1151
	struct smscore_client_t *newclient;
1152 1153 1154
	/* check that no other channel with same parameters exists */
	if (smscore_find_client(coredev, params->data_type,
				params->initial_id)) {
1155
		sms_err("Client already exist.");
1156
		return -EEXIST;
1157
	}
1158

1159
	newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL);
1160
	if (!newclient) {
1161
		sms_err("Failed to allocate memory for client.");
1162
		return -ENOMEM;
1163 1164
	}

1165
	INIT_LIST_HEAD(&newclient->idlist);
1166 1167 1168 1169
	newclient->coredev = coredev;
	newclient->onresponse_handler = params->onresponse_handler;
	newclient->onremove_handler = params->onremove_handler;
	newclient->context = params->context;
1170 1171 1172 1173
	list_add_locked(&newclient->entry, &coredev->clients,
			&coredev->clientslock);
	smscore_validate_client(coredev, newclient, params->data_type,
				params->initial_id);
1174
	*client = newclient;
1175 1176
	sms_debug("%p %d %d", params->context, params->data_type,
		  params->initial_id);
1177 1178 1179

	return 0;
}
1180
EXPORT_SYMBOL_GPL(smscore_register_client);
1181 1182 1183 1184

/**
 * frees smsclient object and all subclients associated with it
 *
1185 1186
 * @param client pointer to smsclient object returned by
 *               smscore_register_client
1187 1188
 *
 */
1189
void smscore_unregister_client(struct smscore_client_t *client)
1190
{
1191
	struct smscore_device_t *coredev = client->coredev;
1192 1193 1194 1195 1196
	unsigned long flags;

	spin_lock_irqsave(&coredev->clientslock, flags);


1197
	while (!list_empty(&client->idlist)) {
1198 1199
		struct smscore_idlist_t *identry =
			(struct smscore_idlist_t *) client->idlist.next;
1200 1201
		list_del(&identry->entry);
		kfree(identry);
1202 1203
	}

1204
	sms_info("%p", client->context);
1205 1206 1207 1208 1209 1210

	list_del(&client->entry);
	kfree(client);

	spin_unlock_irqrestore(&coredev->clientslock, flags);
}
1211
EXPORT_SYMBOL_GPL(smscore_unregister_client);
1212 1213 1214 1215 1216

/**
 * verifies that source id is not taken by another client,
 * calls device handler to send requests to the device
 *
1217 1218
 * @param client pointer to smsclient object returned by
 *               smscore_register_client
1219 1220 1221 1222 1223
 * @param buffer pointer to a request buffer
 * @param size size (in bytes) of request buffer
 *
 * @return 0 on success, <0 on error.
 */
1224 1225
int smsclient_sendrequest(struct smscore_client_t *client,
			  void *buffer, size_t size)
1226
{
1227 1228
	struct smscore_device_t *coredev;
	struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer;
1229 1230
	int rc;

1231
	if (client == NULL) {
1232
		sms_err("Got NULL client");
1233 1234 1235 1236
		return -EINVAL;
	}

	coredev = client->coredev;
1237

1238 1239
	/* check that no other channel with same id exists */
	if (coredev == NULL) {
1240
		sms_err("Got NULL coredev");
1241 1242 1243
		return -EINVAL;
	}

1244 1245
	rc = smscore_validate_client(client->coredev, client, 0,
				     phdr->msgSrcId);
1246 1247 1248 1249 1250
	if (rc < 0)
		return rc;

	return coredev->sendrequest_handler(coredev->context, buffer, size);
}
1251
EXPORT_SYMBOL_GPL(smsclient_sendrequest);
1252 1253


1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
			   struct smscore_gpio_config *pinconfig)
{
	struct {
		struct SmsMsgHdr_ST hdr;
		u32 data[6];
	} msg;

	if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
		msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
		msg.hdr.msgDstId = HIF_TASK;
		msg.hdr.msgFlags = 0;
		msg.hdr.msgType  = MSG_SMS_GPIO_CONFIG_EX_REQ;
		msg.hdr.msgLength = sizeof(msg);

		msg.data[0] = pin;
		msg.data[1] = pinconfig->pullupdown;

		/* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */
		msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;

		switch (pinconfig->outputdriving) {
		case SMS_GPIO_OUTPUTDRIVING_16mA:
			msg.data[3] = 7; /* Nova - 16mA */
			break;
		case SMS_GPIO_OUTPUTDRIVING_12mA:
			msg.data[3] = 5; /* Nova - 11mA */
			break;
		case SMS_GPIO_OUTPUTDRIVING_8mA:
			msg.data[3] = 3; /* Nova - 7mA */
			break;
		case SMS_GPIO_OUTPUTDRIVING_4mA:
		default:
			msg.data[3] = 2; /* Nova - 4mA */
			break;
		}

		msg.data[4] = pinconfig->direction;
		msg.data[5] = 0;
	} else /* TODO: SMS_DEVICE_FAMILY1 */
		return -EINVAL;

	return coredev->sendrequest_handler(coredev->context,
					    &msg, sizeof(msg));
}

int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)
{
	struct {
		struct SmsMsgHdr_ST hdr;
		u32 data[3];
	} msg;

	if (pin > MAX_GPIO_PIN_NUMBER)
		return -EINVAL;

	msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
	msg.hdr.msgDstId = HIF_TASK;
	msg.hdr.msgFlags = 0;
	msg.hdr.msgType  = MSG_SMS_GPIO_SET_LEVEL_REQ;
	msg.hdr.msgLength = sizeof(msg);

	msg.data[0] = pin;
	msg.data[1] = level ? 1 : 0;
	msg.data[2] = 0;

	return coredev->sendrequest_handler(coredev->context,
					    &msg, sizeof(msg));
}

1324
static int __init smscore_module_init(void)
1325
{
1326
	int rc = 0;
1327 1328 1329 1330 1331 1332 1333 1334

	INIT_LIST_HEAD(&g_smscore_notifyees);
	INIT_LIST_HEAD(&g_smscore_devices);
	kmutex_init(&g_smscore_deviceslock);

	INIT_LIST_HEAD(&g_smscore_registry);
	kmutex_init(&g_smscore_registrylock);

1335 1336


1337 1338 1339 1340



	return rc;
1341
	sms_debug("rc %d", rc);
1342 1343 1344 1345

	return rc;
}

1346
static void __exit smscore_module_exit(void)
1347
{
1348

1349 1350 1351 1352




1353
	kmutex_lock(&g_smscore_deviceslock);
1354
	while (!list_empty(&g_smscore_notifyees)) {
1355 1356 1357
		struct smscore_device_notifyee_t *notifyee =
			(struct smscore_device_notifyee_t *)
				g_smscore_notifyees.next;
1358 1359 1360 1361 1362 1363 1364

		list_del(&notifyee->entry);
		kfree(notifyee);
	}
	kmutex_unlock(&g_smscore_deviceslock);

	kmutex_lock(&g_smscore_registrylock);
1365
	while (!list_empty(&g_smscore_registry)) {
1366 1367 1368
		struct smscore_registry_entry_t *entry =
			(struct smscore_registry_entry_t *)
				g_smscore_registry.next;
1369 1370 1371 1372 1373 1374

		list_del(&entry->entry);
		kfree(entry);
	}
	kmutex_unlock(&g_smscore_registrylock);

1375
	sms_debug("");
1376 1377 1378 1379 1380
}

module_init(smscore_module_init);
module_exit(smscore_module_exit);

1381 1382
MODULE_DESCRIPTION("Siano MDTV Core module");
MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
1383
MODULE_LICENSE("GPL");