smsusb.c 14.2 KB
Newer Older
1
/*
2
 *  Driver for the Siano SMS1xxx USB dongle
3
 *
4 5 6
 *  author: Anatoly Greenblat
 *
 *  Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
7 8
 *
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License version 2 as
10
 *  published by the Free Software Foundation;
11
 *
12 13
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
14
 *
15
 *  See the GNU General Public License for more details.
16 17 18 19 20 21
 *
 *  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.
 */

22 23 24 25 26 27
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/firmware.h>

#include "smscoreapi.h"
28
#include "sms-cards.h"
29

30 31 32 33
static int sms_dbg;
module_param_named(debug, sms_dbg, int, 0644);
MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");

34 35 36 37 38 39
#define USB1_BUFFER_SIZE		0x1000
#define USB2_BUFFER_SIZE		0x4000

#define MAX_BUFFERS		50
#define MAX_URBS		10

40
struct smsusb_device_t;
41

42 43 44
struct smsusb_urb_t {
	struct smscore_buffer_t *cb;
	struct smsusb_device_t	*dev;
45

46 47
	struct urb urb;
};
48

49
struct smsusb_device_t {
50
	struct usb_device *udev;
51
	struct smscore_device_t *coredev;
52

53
	struct smsusb_urb_t 	surbs[MAX_URBS];
54

55 56
	int		response_alignment;
	int		buffer_size;
57
};
58

59 60
static int smsusb_submit_urb(struct smsusb_device_t *dev,
			     struct smsusb_urb_t *surb);
61

62
static void smsusb_onresponse(struct urb *urb)
63
{
64 65
	struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
	struct smsusb_device_t *dev = surb->dev;
66

67
	if (urb->status < 0) {
68 69
		sms_err("error, urb status %d, %d bytes",
			urb->status, urb->actual_length);
70 71 72
		return;
	}

73
	if (urb->actual_length > 0) {
74
		struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) surb->cb->p;
75

76
		smsendian_handle_message_header(phdr);
77
		if (urb->actual_length >= phdr->msgLength) {
78 79
			surb->cb->size = phdr->msgLength;

80 81 82 83 84 85 86 87 88 89
			if (dev->response_alignment &&
			    (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {

				surb->cb->offset =
					dev->response_alignment +
					((phdr->msgFlags >> 8) & 3);

				/* sanity check */
				if (((int) phdr->msgLength +
				     surb->cb->offset) > urb->actual_length) {
90 91 92 93 94 95
					sms_err("invalid response "
						"msglen %d offset %d "
						"size %d",
						phdr->msgLength,
						surb->cb->offset,
						urb->actual_length);
96 97 98
					goto exit_and_resubmit;
				}

99 100
				/* move buffer pointer and
				 * copy header to its new location */
101
				memcpy((char *) phdr + surb->cb->offset,
102
				       phdr, sizeof(struct SmsMsgHdr_ST));
103
			} else
104 105 106 107
				surb->cb->offset = 0;

			smscore_onresponse(dev->coredev, surb->cb);
			surb->cb = NULL;
108
		} else {
109 110 111
			sms_err("invalid response "
				"msglen %d actual %d",
				phdr->msgLength, urb->actual_length);
112 113 114 115 116 117 118
		}
	}

exit_and_resubmit:
	smsusb_submit_urb(dev, surb);
}

119 120
static int smsusb_submit_urb(struct smsusb_device_t *dev,
			     struct smsusb_urb_t *surb)
121
{
122
	if (!surb->cb) {
123
		surb->cb = smscore_getbuffer(dev->coredev);
124
		if (!surb->cb) {
125
			sms_err("smscore_getbuffer(...) returned NULL");
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
			return -ENOMEM;
		}
	}

	usb_fill_bulk_urb(
		&surb->urb,
		dev->udev,
		usb_rcvbulkpipe(dev->udev, 0x81),
		surb->cb->p,
		dev->buffer_size,
		smsusb_onresponse,
		surb
	);
	surb->urb.transfer_dma = surb->cb->phys;
	surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	return usb_submit_urb(&surb->urb, GFP_ATOMIC);
}

145
static void smsusb_stop_streaming(struct smsusb_device_t *dev)
146 147 148
{
	int i;

149
	for (i = 0; i < MAX_URBS; i++) {
150 151
		usb_kill_urb(&dev->surbs[i].urb);

152
		if (dev->surbs[i].cb) {
153 154 155 156 157 158
			smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
			dev->surbs[i].cb = NULL;
		}
	}
}

159
static int smsusb_start_streaming(struct smsusb_device_t *dev)
160 161 162
{
	int i, rc;

163
	for (i = 0; i < MAX_URBS; i++) {
164
		rc = smsusb_submit_urb(dev, &dev->surbs[i]);
165
		if (rc < 0) {
166
			sms_err("smsusb_submit_urb(...) failed");
167 168 169 170 171 172 173 174
			smsusb_stop_streaming(dev);
			break;
		}
	}

	return rc;
}

175
static int smsusb_sendrequest(void *context, void *buffer, size_t size)
176
{
177
	struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
178 179
	int dummy;

180 181
	return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
			    buffer, size, &dummy, 1000);
182 183
}

184
static char *smsusb1_fw_lkup[] = {
185 186 187 188 189 190 191
	"dvbt_stellar_usb.inp",
	"dvbh_stellar_usb.inp",
	"tdmb_stellar_usb.inp",
	"none",
	"dvbt_bda_stellar_usb.inp",
};

192 193 194 195 196 197 198
static inline char *sms_get_fw_name(int mode, int board_id)
{
	char **fw = sms_get_board(board_id)->fw;
	return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];
}

static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
199 200
{
	const struct firmware *fw;
201
	u8 *fw_buffer;
202
	int rc, dummy;
203
	char *fw_filename;
204

205
	if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
206
		sms_err("invalid firmware id specified %d", id);
207 208 209
		return -EINVAL;
	}

210 211 212
	fw_filename = sms_get_fw_name(id, board_id);

	rc = request_firmware(&fw, fw_filename, &udev->dev);
213
	if (rc < 0) {
214 215 216 217 218 219 220 221 222 223 224
		sms_warn("failed to open \"%s\" mode %d, "
			 "trying again with default firmware", fw_filename, id);

		fw_filename = smsusb1_fw_lkup[id];
		rc = request_firmware(&fw, fw_filename, &udev->dev);
		if (rc < 0) {
			sms_warn("failed to open \"%s\" mode %d",
				 fw_filename, id);

			return rc;
		}
225 226 227
	}

	fw_buffer = kmalloc(fw->size, GFP_KERNEL);
228
	if (fw_buffer) {
229 230
		memcpy(fw_buffer, fw->data, fw->size);

231 232
		rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
				  fw_buffer, fw->size, &dummy, 1000);
233

234
		sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
235 236

		kfree(fw_buffer);
237
	} else {
238
		sms_err("failed to allocate firmware buffer");
239 240
		rc = -ENOMEM;
	}
241
	sms_info("read FW %s, size=%zd", fw_filename, fw->size);
242 243 244 245 246 247

	release_firmware(fw);

	return rc;
}

248
static void smsusb1_detectmode(void *context, int *mode)
249
{
250 251
	char *product_string =
		((struct smsusb_device_t *) context)->udev->product;
252 253 254

	*mode = DEVICE_MODE_NONE;

255
	if (!product_string) {
256
		product_string = "none";
257
		sms_err("product string not found");
258 259 260 261 262 263 264 265
	} else if (strstr(product_string, "DVBH"))
		*mode = 1;
	else if (strstr(product_string, "BDA"))
		*mode = 4;
	else if (strstr(product_string, "DVBT"))
		*mode = 0;
	else if (strstr(product_string, "TDMB"))
		*mode = 2;
266

267
	sms_info("%d \"%s\"", *mode, product_string);
268 269
}

270
static int smsusb1_setmode(void *context, int mode)
271
{
272 273
	struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
			     sizeof(struct SmsMsgHdr_ST), 0 };
274

275
	if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
276
		sms_err("invalid firmware id specified %d", mode);
277 278 279 280 281 282
		return -EINVAL;
	}

	return smsusb_sendrequest(context, &Msg, sizeof(Msg));
}

283
static void smsusb_term_device(struct usb_interface *intf)
284
{
285 286
	struct smsusb_device_t *dev =
		(struct smsusb_device_t *) usb_get_intfdata(intf);
287

288
	if (dev) {
289 290
		smsusb_stop_streaming(dev);

291
		/* unregister from smscore */
292 293 294 295 296
		if (dev->coredev)
			smscore_unregister_device(dev->coredev);

		kfree(dev);

297
		sms_info("device %p destroyed", dev);
298 299 300 301 302
	}

	usb_set_intfdata(intf, NULL);
}

303
static int smsusb_init_device(struct usb_interface *intf, int board_id)
304
{
305 306
	struct smsdevice_params_t params;
	struct smsusb_device_t *dev;
307 308
	int i, rc;

309
	/* create device object */
310
	dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
311
	if (!dev) {
312
		sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
313 314 315 316 317 318 319
		return -ENOMEM;
	}

	memset(&params, 0, sizeof(params));
	usb_set_intfdata(intf, dev);
	dev->udev = interface_to_usbdev(intf);

320
	params.device_type = sms_get_board(board_id)->type;
321

322
	switch (params.device_type) {
323
	case SMS_STELLAR:
324 325 326 327 328 329
		dev->buffer_size = USB1_BUFFER_SIZE;

		params.setmode_handler = smsusb1_setmode;
		params.detectmode_handler = smsusb1_detectmode;
		break;
	default:
330 331 332 333 334
		sms_err("Unspecified sms device type!");
		/* fall-thru */
	case SMS_NOVA_A0:
	case SMS_NOVA_B0:
	case SMS_VEGA:
335
		dev->buffer_size = USB2_BUFFER_SIZE;
336 337
		dev->response_alignment =
			dev->udev->ep_in[1]->desc.wMaxPacketSize -
338
			sizeof(struct SmsMsgHdr_ST);
339

340 341
		params.flags |= SMS_DEVICE_FAMILY2;
		break;
342 343 344 345 346 347 348
	}

	params.device = &dev->udev->dev;
	params.buffer_size = dev->buffer_size;
	params.num_buffers = MAX_BUFFERS;
	params.sendrequest_handler = smsusb_sendrequest;
	params.context = dev;
349 350
	snprintf(params.devpath, sizeof(params.devpath),
		 "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath);
351

352
	/* register in smscore */
353
	rc = smscore_register_device(&params, &dev->coredev);
354
	if (rc < 0) {
355
		sms_err("smscore_register_device(...) failed, rc %d", rc);
356 357 358 359
		smsusb_term_device(intf);
		return rc;
	}

360 361
	smscore_set_board_id(dev->coredev, board_id);

362
	/* initialize urbs */
363
	for (i = 0; i < MAX_URBS; i++) {
364 365 366 367
		dev->surbs[i].dev = dev;
		usb_init_urb(&dev->surbs[i].urb);
	}

368
	sms_info("smsusb_start_streaming(...).");
369
	rc = smsusb_start_streaming(dev);
370
	if (rc < 0) {
371
		sms_err("smsusb_start_streaming(...) failed");
372 373 374 375 376
		smsusb_term_device(intf);
		return rc;
	}

	rc = smscore_start_device(dev->coredev);
377
	if (rc < 0) {
378
		sms_err("smscore_start_device(...) failed");
379 380 381 382
		smsusb_term_device(intf);
		return rc;
	}

383
	sms_info("device %p created", dev);
384 385 386 387

	return rc;
}

388 389
static int smsusb_probe(struct usb_interface *intf,
			const struct usb_device_id *id)
390 391 392 393 394
{
	struct usb_device *udev = interface_to_usbdev(intf);
	char devpath[32];
	int i, rc;

395 396 397
	rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
	rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));

398
	if (intf->num_altsetting > 0) {
399 400
		rc = usb_set_interface(
			udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
401
		if (rc < 0) {
402
			sms_err("usb_set_interface failed, rc %d", rc);
403 404 405 406
			return rc;
		}
	}

407
	sms_info("smsusb_probe %d",
408
	       intf->cur_altsetting->desc.bInterfaceNumber);
409
	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
410
		sms_info("endpoint %d %02x %02x %d", i,
411 412 413
		       intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
		       intf->cur_altsetting->endpoint[i].desc.bmAttributes,
		       intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
414

415 416
	if ((udev->actconfig->desc.bNumInterfaces == 2) &&
	    (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
417
		sms_err("rom interface 0 is not used");
418 419 420
		return -ENODEV;
	}

421 422 423
	if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
		snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
			 udev->bus->busnum, udev->devpath);
424
		sms_info("stellar device was found.");
425
		return smsusb1_load_firmware(
426 427
				udev, smscore_registry_getmode(devpath),
				id->driver_info);
428 429
	}

430
	rc = smsusb_init_device(intf, id->driver_info);
431
	sms_info("rc %d", rc);
432
	sms_board_load_modules(id->driver_info);
433
	return rc;
434 435
}

436
static void smsusb_disconnect(struct usb_interface *intf)
437 438 439 440
{
	smsusb_term_device(intf);
}

441 442 443 444
static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
{
	struct smsusb_device_t *dev =
		(struct smsusb_device_t *)usb_get_intfdata(intf);
445
	printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);
446 447 448 449 450 451 452 453 454 455 456
	smsusb_stop_streaming(dev);
	return 0;
}

static int smsusb_resume(struct usb_interface *intf)
{
	int rc, i;
	struct smsusb_device_t *dev =
		(struct smsusb_device_t *)usb_get_intfdata(intf);
	struct usb_device *udev = interface_to_usbdev(intf);

457
	printk(KERN_INFO "%s: Entering.\n", __func__);
458 459 460 461 462 463 464 465 466 467 468 469 470 471
	usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
	usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));

	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
		printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
		       intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
		       intf->cur_altsetting->endpoint[i].desc.bmAttributes,
		       intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);

	if (intf->num_altsetting > 0) {
		rc = usb_set_interface(udev,
				       intf->cur_altsetting->desc.
				       bInterfaceNumber, 0);
		if (rc < 0) {
472 473
			printk(KERN_INFO "%s usb_set_interface failed, "
			       "rc %d\n", __func__, rc);
474 475 476 477 478 479 480 481
			return rc;
		}
	}

	smsusb_start_streaming(dev);
	return 0;
}

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
struct usb_device_id smsusb_id_table[] = {
#ifdef CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS
	{ USB_DEVICE(0x187f, 0x0010),
		.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
	{ USB_DEVICE(0x187f, 0x0100),
		.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
	{ USB_DEVICE(0x187f, 0x0200),
		.driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
	{ USB_DEVICE(0x187f, 0x0201),
		.driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
	{ USB_DEVICE(0x187f, 0x0300),
		.driver_info = SMS1XXX_BOARD_SIANO_VEGA },
#endif
	{ USB_DEVICE(0x2040, 0x1700),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
	{ USB_DEVICE(0x2040, 0x1800),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
	{ USB_DEVICE(0x2040, 0x1801),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
	{ USB_DEVICE(0x2040, 0x2000),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
	{ USB_DEVICE(0x2040, 0x2009),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
	{ USB_DEVICE(0x2040, 0x200a),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
	{ USB_DEVICE(0x2040, 0x2010),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
	{ USB_DEVICE(0x2040, 0x2011),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
	{ USB_DEVICE(0x2040, 0x2019),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
	{ USB_DEVICE(0x2040, 0x5500),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0x5510),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0x5520),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0x5530),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0x5580),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0x5590),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ }		/* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, smsusb_id_table);

529
static struct usb_driver smsusb_driver = {
530
	.name			= "smsusb",
531
	.probe			= smsusb_probe,
532
	.disconnect		= smsusb_disconnect,
533
	.id_table		= smsusb_id_table,
534 535 536

	.suspend		= smsusb_suspend,
	.resume			= smsusb_resume,
537 538
};

539
int smsusb_module_init(void)
540 541 542
{
	int rc = usb_register(&smsusb_driver);
	if (rc)
543
		sms_err("usb_register failed. Error number %d", rc);
544

545
	sms_debug("");
546 547 548 549

	return rc;
}

550
void smsusb_module_exit(void)
551
{
552
	sms_debug("");
553
	/* Regular USB Cleanup */
554 555 556
	usb_deregister(&smsusb_driver);
}

557 558 559 560 561 562
module_init(smsusb_module_init);
module_exit(smsusb_module_exit);

MODULE_DESCRIPTION("Driver for the Siano SMS1XXX USB dongle");
MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
MODULE_LICENSE("GPL");