smsusb.c 15.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/****************************************************************

Siano Mobile Silicon, Inc.
MDTV receiver kernel modules.
Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

 This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

****************************************************************/
21

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

#include "smscoreapi.h"
30
#include "sms-cards.h"
31
#include "smsendian.h"
32

33 34 35 36
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))");

37 38 39 40 41 42
#define USB1_BUFFER_SIZE		0x1000
#define USB2_BUFFER_SIZE		0x4000

#define MAX_BUFFERS		50
#define MAX_URBS		10

43
struct smsusb_device_t;
44

45 46 47
struct smsusb_urb_t {
	struct smscore_buffer_t *cb;
	struct smsusb_device_t	*dev;
48

49 50
	struct urb urb;
};
51

52
struct smsusb_device_t {
53
	struct usb_device *udev;
54
	struct smscore_device_t *coredev;
55

56
	struct smsusb_urb_t 	surbs[MAX_URBS];
57

58 59
	int		response_alignment;
	int		buffer_size;
60
};
61

62 63
static int smsusb_submit_urb(struct smsusb_device_t *dev,
			     struct smsusb_urb_t *surb);
64

65
static void smsusb_onresponse(struct urb *urb)
66
{
67 68
	struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
	struct smsusb_device_t *dev = surb->dev;
69

70 71
	if (urb->status == -ESHUTDOWN) {
		sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",
72
			urb->status, urb->actual_length);
73 74 75
		return;
	}

76 77
	if ((urb->actual_length > 0) && (urb->status == 0)) {
		struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p;
78

79
		smsendian_handle_message_header(phdr);
80
		if (urb->actual_length >= phdr->msgLength) {
81 82
			surb->cb->size = phdr->msgLength;

83 84 85 86 87 88 89 90 91 92
			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) {
93 94 95 96 97 98
					sms_err("invalid response "
						"msglen %d offset %d "
						"size %d",
						phdr->msgLength,
						surb->cb->offset,
						urb->actual_length);
99 100 101
					goto exit_and_resubmit;
				}

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

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

120 121 122 123 124

exit_and_resubmit:
	smsusb_submit_urb(dev, surb);
}

125 126
static int smsusb_submit_urb(struct smsusb_device_t *dev,
			     struct smsusb_urb_t *surb)
127
{
128
	if (!surb->cb) {
129
		surb->cb = smscore_getbuffer(dev->coredev);
130
		if (!surb->cb) {
131
			sms_err("smscore_getbuffer(...) returned NULL");
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
			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);
}

151
static void smsusb_stop_streaming(struct smsusb_device_t *dev)
152 153 154
{
	int i;

155
	for (i = 0; i < MAX_URBS; i++) {
156 157
		usb_kill_urb(&dev->surbs[i].urb);

158
		if (dev->surbs[i].cb) {
159 160 161 162 163 164
			smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
			dev->surbs[i].cb = NULL;
		}
	}
}

165
static int smsusb_start_streaming(struct smsusb_device_t *dev)
166 167 168
{
	int i, rc;

169
	for (i = 0; i < MAX_URBS; i++) {
170
		rc = smsusb_submit_urb(dev, &dev->surbs[i]);
171
		if (rc < 0) {
172
			sms_err("smsusb_submit_urb(...) failed");
173 174 175 176 177 178 179 180
			smsusb_stop_streaming(dev);
			break;
		}
	}

	return rc;
}

181
static int smsusb_sendrequest(void *context, void *buffer, size_t size)
182
{
183
	struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
184 185
	int dummy;

186
	smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer);
187 188
	return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
			    buffer, size, &dummy, 1000);
189 190
}

191
static char *smsusb1_fw_lkup[] = {
192 193 194 195 196 197 198
	"dvbt_stellar_usb.inp",
	"dvbh_stellar_usb.inp",
	"tdmb_stellar_usb.inp",
	"none",
	"dvbt_bda_stellar_usb.inp",
};

199 200 201 202 203 204 205
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)
206 207
{
	const struct firmware *fw;
208
	u8 *fw_buffer;
209
	int rc, dummy;
210
	char *fw_filename;
211

212
	if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
213
		sms_err("invalid firmware id specified %d", id);
214 215 216
		return -EINVAL;
	}

217 218 219
	fw_filename = sms_get_fw_name(id, board_id);

	rc = request_firmware(&fw, fw_filename, &udev->dev);
220
	if (rc < 0) {
221 222 223 224 225 226 227 228 229 230 231
		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;
		}
232 233 234
	}

	fw_buffer = kmalloc(fw->size, GFP_KERNEL);
235
	if (fw_buffer) {
236 237
		memcpy(fw_buffer, fw->data, fw->size);

238 239
		rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
				  fw_buffer, fw->size, &dummy, 1000);
240

241
		sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
242 243

		kfree(fw_buffer);
244
	} else {
245
		sms_err("failed to allocate firmware buffer");
246 247
		rc = -ENOMEM;
	}
248
	sms_info("read FW %s, size=%zd", fw_filename, fw->size);
249 250 251 252 253 254

	release_firmware(fw);

	return rc;
}

255
static void smsusb1_detectmode(void *context, int *mode)
256
{
257 258
	char *product_string =
		((struct smsusb_device_t *) context)->udev->product;
259 260 261

	*mode = DEVICE_MODE_NONE;

262
	if (!product_string) {
263
		product_string = "none";
264
		sms_err("product string not found");
265 266 267 268 269 270 271 272
	} 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;
273

274
	sms_info("%d \"%s\"", *mode, product_string);
275 276
}

277
static int smsusb1_setmode(void *context, int mode)
278
{
279 280
	struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
			     sizeof(struct SmsMsgHdr_ST), 0 };
281

282
	if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
283
		sms_err("invalid firmware id specified %d", mode);
284 285 286 287 288 289
		return -EINVAL;
	}

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

290
static void smsusb_term_device(struct usb_interface *intf)
291
{
292
	struct smsusb_device_t *dev = usb_get_intfdata(intf);
293

294
	if (dev) {
295 296
		smsusb_stop_streaming(dev);

297
		/* unregister from smscore */
298 299 300
		if (dev->coredev)
			smscore_unregister_device(dev->coredev);

301
		sms_info("device %p destroyed", dev);
302
		kfree(dev);
303 304 305 306 307
	}

	usb_set_intfdata(intf, NULL);
}

308
static int smsusb_init_device(struct usb_interface *intf, int board_id)
309
{
310 311
	struct smsdevice_params_t params;
	struct smsusb_device_t *dev;
312 313
	int i, rc;

314
	/* create device object */
315
	dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
316
	if (!dev) {
317
		sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
318 319 320 321 322 323 324
		return -ENOMEM;
	}

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

325
	params.device_type = sms_get_board(board_id)->type;
326

327
	switch (params.device_type) {
328
	case SMS_STELLAR:
329 330 331 332 333 334
		dev->buffer_size = USB1_BUFFER_SIZE;

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

345 346
		params.flags |= SMS_DEVICE_FAMILY2;
		break;
347 348 349 350 351 352 353
	}

	params.device = &dev->udev->dev;
	params.buffer_size = dev->buffer_size;
	params.num_buffers = MAX_BUFFERS;
	params.sendrequest_handler = smsusb_sendrequest;
	params.context = dev;
354
	usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));
355

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

364 365
	smscore_set_board_id(dev->coredev, board_id);

366
	/* initialize urbs */
367
	for (i = 0; i < MAX_URBS; i++) {
368 369 370 371
		dev->surbs[i].dev = dev;
		usb_init_urb(&dev->surbs[i].urb);
	}

372
	sms_info("smsusb_start_streaming(...).");
373
	rc = smsusb_start_streaming(dev);
374
	if (rc < 0) {
375
		sms_err("smsusb_start_streaming(...) failed");
376 377 378 379 380
		smsusb_term_device(intf);
		return rc;
	}

	rc = smscore_start_device(dev->coredev);
381
	if (rc < 0) {
382
		sms_err("smscore_start_device(...) failed");
383 384 385 386
		smsusb_term_device(intf);
		return rc;
	}

387
	sms_info("device %p created", dev);
388 389 390 391

	return rc;
}

392
static int smsusb_probe(struct usb_interface *intf,
393
			const struct usb_device_id *id)
394 395 396 397 398
{
	struct usb_device *udev = interface_to_usbdev(intf);
	char devpath[32];
	int i, rc;

399 400 401
	rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
	rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));

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

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

419 420
	if ((udev->actconfig->desc.bNumInterfaces == 2) &&
	    (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
421
		sms_err("rom interface 0 is not used");
422 423 424
		return -ENODEV;
	}

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

434
	rc = smsusb_init_device(intf, id->driver_info);
435
	sms_info("rc %d", rc);
436
	sms_board_load_modules(id->driver_info);
437
	return rc;
438 439
}

440
static void smsusb_disconnect(struct usb_interface *intf)
441 442 443 444
{
	smsusb_term_device(intf);
}

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

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

459
	printk(KERN_INFO "%s: Entering.\n", __func__);
460 461 462 463 464 465 466 467 468 469 470 471 472 473
	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) {
474 475
			printk(KERN_INFO "%s usb_set_interface failed, "
			       "rc %d\n", __func__, rc);
476 477 478 479 480 481 482 483
			return rc;
		}
	}

	smsusb_start_streaming(dev);
	return 0;
}

484
static const struct usb_device_id smsusb_id_table[] = {
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
	{ 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 },
	{ 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 },
525 526 527 528
	{ USB_DEVICE(0x187f, 0x0202),
		.driver_info = SMS1XXX_BOARD_SIANO_NICE },
	{ USB_DEVICE(0x187f, 0x0301),
		.driver_info = SMS1XXX_BOARD_SIANO_VENICE },
529 530 531 532
	{ USB_DEVICE(0x2040, 0xb900),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0xb910),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
533 534 535 536
	{ USB_DEVICE(0x2040, 0xb980),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0xb990),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
537 538
	{ USB_DEVICE(0x2040, 0xc000),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
539 540 541 542 543 544
	{ USB_DEVICE(0x2040, 0xc010),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0xc080),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
	{ USB_DEVICE(0x2040, 0xc090),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
545 546
	{ USB_DEVICE(0x2040, 0xc0a0),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
547 548
	{ USB_DEVICE(0x2040, 0xf5a0),
		.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
549 550 551
	{ } /* Terminating entry */
	};

552 553
MODULE_DEVICE_TABLE(usb, smsusb_id_table);

554
static struct usb_driver smsusb_driver = {
555
	.name			= "smsusb",
556
	.probe			= smsusb_probe,
557
	.disconnect		= smsusb_disconnect,
558
	.id_table		= smsusb_id_table,
559 560 561

	.suspend		= smsusb_suspend,
	.resume			= smsusb_resume,
562 563
};

564
module_usb_driver(smsusb_driver);
565

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