speedtch.c 23.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7
/******************************************************************************
 *  speedtch.c  -  Alcatel SpeedTouch USB xDSL modem driver
 *
 *  Copyright (C) 2001, Alcatel
 *  Copyright (C) 2003, Duncan Sands
 *  Copyright (C) 2004, David Woodhouse
 *
8 9
 *  Based on "modem_run.c", copyright (C) 2001, Benoit Papillault
 *
Linus Torvalds's avatar
Linus Torvalds committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *  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, write to the Free Software Foundation, Inc., 59
 *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ******************************************************************************/

26 27 28 29
#include <asm/page.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firmware.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
#include <linux/gfp.h>
31
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
32
#include <linux/kernel.h>
33 34
#include <linux/module.h>
#include <linux/moduleparam.h>
Linus Torvalds's avatar
Linus Torvalds committed
35
#include <linux/slab.h>
36 37 38
#include <linux/stat.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
Linus Torvalds's avatar
Linus Torvalds committed
39

40
#include "usbatm.h"
Linus Torvalds's avatar
Linus Torvalds committed
41 42

#define DRIVER_AUTHOR	"Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
43
#define DRIVER_VERSION	"1.9"
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46 47
#define DRIVER_DESC	"Alcatel SpeedTouch USB driver version " DRIVER_VERSION

static const char speedtch_driver_name[] = "speedtch";

48 49
#define CTRL_TIMEOUT 2000	/* milliseconds */
#define DATA_TIMEOUT 2000	/* milliseconds */
Linus Torvalds's avatar
Linus Torvalds committed
50

51 52 53 54 55 56
#define OFFSET_7	0		/* size 1 */
#define OFFSET_b	1		/* size 8 */
#define OFFSET_d	9		/* size 4 */
#define OFFSET_e	13		/* size 1 */
#define OFFSET_f	14		/* size 1 */
#define TOTAL		15
Linus Torvalds's avatar
Linus Torvalds committed
57

58 59 60 61 62
#define SIZE_7		1
#define SIZE_b		8
#define SIZE_d		4
#define SIZE_e		1
#define SIZE_f		1
Linus Torvalds's avatar
Linus Torvalds committed
63

64 65
#define MIN_POLL_DELAY		5000	/* milliseconds */
#define MAX_POLL_DELAY		60000	/* milliseconds */
Linus Torvalds's avatar
Linus Torvalds committed
66

67
#define RESUBMIT_DELAY		1000	/* milliseconds */
Linus Torvalds's avatar
Linus Torvalds committed
68

69 70 71
#define DEFAULT_ALTSETTING	1
#define DEFAULT_DL_512_FIRST	0
#define DEFAULT_SW_BUFFERING	0
Linus Torvalds's avatar
Linus Torvalds committed
72

73 74 75
static int altsetting = DEFAULT_ALTSETTING;
static int dl_512_first = DEFAULT_DL_512_FIRST;
static int sw_buffering = DEFAULT_SW_BUFFERING;
Linus Torvalds's avatar
Linus Torvalds committed
76

77 78 79 80
module_param(altsetting, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(altsetting,
		 "Alternative setting for data interface (default: "
		 __MODULE_STRING(DEFAULT_ALTSETTING) ")");
Linus Torvalds's avatar
Linus Torvalds committed
81

82 83 84 85
module_param(dl_512_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(dl_512_first,
		 "Read 512 bytes before sending firmware (default: "
		 __MODULE_STRING(DEFAULT_DL_512_FIRST) ")");
Linus Torvalds's avatar
Linus Torvalds committed
86

87 88 89 90
module_param(sw_buffering, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(sw_buffering,
		 "Enable software buffering (default: "
		 __MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
Linus Torvalds's avatar
Linus Torvalds committed
91

92 93 94
#define ENDPOINT_INT		0x81
#define ENDPOINT_DATA		0x07
#define ENDPOINT_FIRMWARE	0x05
Linus Torvalds's avatar
Linus Torvalds committed
95

96
#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
Linus Torvalds's avatar
Linus Torvalds committed
97 98

struct speedtch_instance_data {
99 100 101
	struct usbatm_data *usbatm;

	struct work_struct status_checker;
Linus Torvalds's avatar
Linus Torvalds committed
102

103 104
	unsigned char last_status;

105 106 107
	int poll_delay; /* milliseconds */

	struct timer_list resubmit_timer;
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110
	struct urb *int_urb;
	unsigned char int_data[16];

111
	unsigned char scratch_buffer[TOTAL];
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117
};

/***************
**  firmware  **
***************/

118
static void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state)
Linus Torvalds's avatar
Linus Torvalds committed
119
{
120 121
	struct usbatm_data *usbatm = instance->usbatm;
	struct usb_device *usb_dev = usbatm->usb_dev;
Linus Torvalds's avatar
Linus Torvalds committed
122 123
	int ret;

124 125 126 127 128 129 130 131
	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			      0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT);
	if (ret < 0)
		usb_warn(usbatm,
			 "%sabling SW buffering: usb_control_msg returned %d\n",
			 state ? "En" : "Dis", ret);
	else
		dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
Linus Torvalds's avatar
Linus Torvalds committed
132 133 134 135
}

static void speedtch_test_sequence(struct speedtch_instance_data *instance)
{
136 137 138
	struct usbatm_data *usbatm = instance->usbatm;
	struct usb_device *usb_dev = usbatm->usb_dev;
	unsigned char *buf = instance->scratch_buffer;
Linus Torvalds's avatar
Linus Torvalds committed
139 140 141 142 143
	int ret;

	/* URB 147 */
	buf[0] = 0x1c;
	buf[1] = 0x50;
144 145
	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			      0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
146
	if (ret < 0)
147
		usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret);
Linus Torvalds's avatar
Linus Torvalds committed
148 149 150 151

	/* URB 148 */
	buf[0] = 0x32;
	buf[1] = 0x00;
152 153
	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			      0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
154
	if (ret < 0)
155
		usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret);
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159 160

	/* URB 149 */
	buf[0] = 0x01;
	buf[1] = 0x00;
	buf[2] = 0x01;
161 162
	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			      0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
163
	if (ret < 0)
164
		usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret);
Linus Torvalds's avatar
Linus Torvalds committed
165 166 167 168 169

	/* URB 150 */
	buf[0] = 0x01;
	buf[1] = 0x00;
	buf[2] = 0x01;
170 171
	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			      0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
172
	if (ret < 0)
173
		usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
Linus Torvalds's avatar
Linus Torvalds committed
174 175
}

176 177 178
static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
				     const struct firmware *fw1,
				     const struct firmware *fw2)
Linus Torvalds's avatar
Linus Torvalds committed
179
{
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
	unsigned char *buffer;
	struct usbatm_data *usbatm = instance->usbatm;
	struct usb_interface *intf;
	struct usb_device *usb_dev = usbatm->usb_dev;
	int actual_length;
	int ret = 0;
	int offset;

	usb_dbg(usbatm, "%s entered\n", __func__);

	if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
		ret = -ENOMEM;
		usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__);
		goto out;
	}

	if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
		ret = -ENODEV;
		usb_dbg(usbatm, "%s: interface not found!\n", __func__);
		goto out_free;
	}

	/* URB 7 */
	if (dl_512_first) {	/* some modems need a read before writing the firmware */
		ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
				   buffer, 0x200, &actual_length, 2000);

		if (ret < 0 && ret != -ETIMEDOUT)
			usb_dbg(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
		else
			usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret);
	}

	/* URB 8 : both leds are static green */
	for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
		int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
		memcpy(buffer, fw1->data + offset, thislen);

		ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
				   buffer, thislen, &actual_length, DATA_TIMEOUT);

		if (ret < 0) {
			usb_dbg(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
			goto out_free;
		}
		usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size);
	}

	/* USB led blinking green, ADSL led off */

	/* URB 11 */
	ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
			   buffer, 0x200, &actual_length, DATA_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
233 234

	if (ret < 0) {
235 236
		usb_dbg(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
		goto out_free;
Linus Torvalds's avatar
Linus Torvalds committed
237
	}
238
	usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length);
Linus Torvalds's avatar
Linus Torvalds committed
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	/* URBs 12 to 139 - USB led blinking green, ADSL led off */
	for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
		int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
		memcpy(buffer, fw2->data + offset, thislen);

		ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
				   buffer, thislen, &actual_length, DATA_TIMEOUT);

		if (ret < 0) {
			usb_dbg(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
			goto out_free;
		}
	}
	usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size);

	/* USB led static green, ADSL led static red */

	/* URB 142 */
	ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
			   buffer, 0x200, &actual_length, DATA_TIMEOUT);

	if (ret < 0) {
		usb_dbg(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
		goto out_free;
	}

	/* success */
	usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length);

	/* Delay to allow firmware to start up. We can do this here
	   because we're in our own kernel thread anyway. */
	msleep_interruptible(1000);

	/* Enable software buffering, if requested */
	if (sw_buffering)
		speedtch_set_swbuff(instance, 1);

	/* Magic spell; don't ask us what this does */
	speedtch_test_sequence(instance);

	ret = 0;

out_free:
	free_page((unsigned long)buffer);
out:
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
286 287
}

288 289
static int speedtch_find_firmware(struct usb_interface *intf, int phase,
				  const struct firmware **fw_p)
Linus Torvalds's avatar
Linus Torvalds committed
290
{
291 292 293 294 295
	struct device *dev = &intf->dev;
	const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice);
	const u8 major_revision = bcdDevice >> 8;
	const u8 minor_revision = bcdDevice & 0xff;
	char buf[24];
Linus Torvalds's avatar
Linus Torvalds committed
296

297 298
	sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
	dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
Linus Torvalds's avatar
Linus Torvalds committed
299

300 301 302
	if (request_firmware(fw_p, buf, dev)) {
		sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
		dev_dbg(dev, "%s: looking for %s\n", __func__, buf);
Linus Torvalds's avatar
Linus Torvalds committed
303

304 305 306 307 308 309 310 311 312
		if (request_firmware(fw_p, buf, dev)) {
			sprintf(buf, "speedtch-%d.bin", phase);
			dev_dbg(dev, "%s: looking for %s\n", __func__, buf);

			if (request_firmware(fw_p, buf, dev)) {
				dev_warn(dev, "no stage %d firmware found!\n", phase);
				return -ENOENT;
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
313 314
	}

315
	dev_info(dev, "found stage %d firmware %s\n", phase, buf);
Linus Torvalds's avatar
Linus Torvalds committed
316

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
	return 0;
}

static int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf)
{
	const struct firmware *fw1, *fw2;
	struct speedtch_instance_data *instance = usbatm->driver_data;
	int ret;

	if ((ret = speedtch_find_firmware(intf, 1, &fw1)) < 0)
			return ret;

	if ((ret = speedtch_find_firmware(intf, 2, &fw2)) < 0) {
		release_firmware(fw1);
		return ret;
Linus Torvalds's avatar
Linus Torvalds committed
332 333
	}

334 335 336 337
	ret = speedtch_upload_firmware(instance, fw1, fw2);

	release_firmware(fw2);
	release_firmware(fw1);
Linus Torvalds's avatar
Linus Torvalds committed
338

339
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
340 341
}

342 343 344 345 346 347

/**********
**  ATM  **
**********/

static int speedtch_read_status(struct speedtch_instance_data *instance)
Linus Torvalds's avatar
Linus Torvalds committed
348
{
349 350 351
	struct usbatm_data *usbatm = instance->usbatm;
	struct usb_device *usb_dev = usbatm->usb_dev;
	unsigned char *buf = instance->scratch_buffer;
Linus Torvalds's avatar
Linus Torvalds committed
352 353 354 355
	int ret;

	memset(buf, 0, TOTAL);

356
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
357 358 359
			      0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
			      CTRL_TIMEOUT);
	if (ret < 0) {
360
		atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
361 362 363
		return ret;
	}

364
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
365 366 367
			      0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
			      CTRL_TIMEOUT);
	if (ret < 0) {
368
		atm_dbg(usbatm, "%s: MSG B failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
369 370 371
		return ret;
	}

372
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375
			      0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
			      CTRL_TIMEOUT);
	if (ret < 0) {
376
		atm_dbg(usbatm, "%s: MSG D failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
377 378 379
		return ret;
	}

380
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
381 382 383
			      0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
			      CTRL_TIMEOUT);
	if (ret < 0) {
384
		atm_dbg(usbatm, "%s: MSG E failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387
		return ret;
	}

388
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
389 390 391
			      0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
			      CTRL_TIMEOUT);
	if (ret < 0) {
392
		atm_dbg(usbatm, "%s: MSG F failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
393 394 395 396 397 398
		return ret;
	}

	return 0;
}

399
static int speedtch_start_synchro(struct speedtch_instance_data *instance)
Linus Torvalds's avatar
Linus Torvalds committed
400
{
401 402 403
	struct usbatm_data *usbatm = instance->usbatm;
	struct usb_device *usb_dev = usbatm->usb_dev;
	unsigned char *buf = instance->scratch_buffer;
Linus Torvalds's avatar
Linus Torvalds committed
404 405
	int ret;

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
	atm_dbg(usbatm, "%s entered\n", __func__);

	memset(buf, 0, 2);

	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
			      0x12, 0xc0, 0x04, 0x00,
			      buf, 2, CTRL_TIMEOUT);

	if (ret < 0)
		atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret);
	else
		atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n",
			__func__, ret, buf[0], buf[1]);

	return ret;
}

static void speedtch_check_status(struct speedtch_instance_data *instance)
{
	struct usbatm_data *usbatm = instance->usbatm;
	struct atm_dev *atm_dev = usbatm->atm_dev;
	unsigned char *buf = instance->scratch_buffer;
428 429
	int down_speed, up_speed, ret;
	unsigned char status;
430 431 432 433 434 435

	atm_dbg(usbatm, "%s entered\n", __func__);

	ret = speedtch_read_status(instance);
	if (ret < 0) {
		atm_warn(usbatm, "error %d fetching device status\n", ret);
436
		instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY);
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439
		return;
	}

440
	instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY);
441

442
	status = buf[OFFSET_7];
Linus Torvalds's avatar
Linus Torvalds committed
443

444 445 446 447 448
	atm_dbg(usbatm, "%s: line state %02x\n", __func__, status);

	if ((status != instance->last_status) || !status) {
		switch (status) {
		case 0:
449
			atm_dev->signal = ATM_PHY_SIG_LOST;
450
			if (instance->last_status)
451
				atm_info(usbatm, "ADSL line is down\n");
452
			/* It may never resync again unless we ask it to... */
453
			ret = speedtch_start_synchro(instance);
454
			break;
Linus Torvalds's avatar
Linus Torvalds committed
455

456
		case 0x08:
457
			atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
458
			atm_info(usbatm, "ADSL line is blocked?\n");
459
			break;
Linus Torvalds's avatar
Linus Torvalds committed
460

461
		case 0x10:
462
			atm_dev->signal = ATM_PHY_SIG_LOST;
463
			atm_info(usbatm, "ADSL line is synchronising\n");
464
			break;
Linus Torvalds's avatar
Linus Torvalds committed
465

466 467
		case 0x20:
			down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
Linus Torvalds's avatar
Linus Torvalds committed
468
				| (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
469
			up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
Linus Torvalds's avatar
Linus Torvalds committed
470 471
				| (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);

472
			if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) {
Linus Torvalds's avatar
Linus Torvalds committed
473 474 475 476
				down_speed >>= 16;
				up_speed >>= 16;
			}

477 478 479 480
			atm_dev->link_rate = down_speed * 1000 / 424;
			atm_dev->signal = ATM_PHY_SIG_FOUND;

			atm_info(usbatm,
481
				 "ADSL line is up (%d kb/s down | %d kb/s up)\n",
482
				 down_speed, up_speed);
483
			break;
Linus Torvalds's avatar
Linus Torvalds committed
484

485
		default:
486
			atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
487 488
			atm_info(usbatm, "Unknown line state %02x\n", status);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
489
		}
490 491

		instance->last_status = status;
Linus Torvalds's avatar
Linus Torvalds committed
492 493 494
	}
}

495
static void speedtch_status_poll(unsigned long data)
Linus Torvalds's avatar
Linus Torvalds committed
496 497 498
{
	struct speedtch_instance_data *instance = (void *)data;

499 500 501 502 503 504
	schedule_work(&instance->status_checker);

	/* The following check is racy, but the race is harmless */
	if (instance->poll_delay < MAX_POLL_DELAY)
		mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(instance->poll_delay));
	else
505
		atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n");
Linus Torvalds's avatar
Linus Torvalds committed
506 507
}

508
static void speedtch_resubmit_int(unsigned long data)
Linus Torvalds's avatar
Linus Torvalds committed
509
{
510 511 512
	struct speedtch_instance_data *instance = (void *)data;
	struct urb *int_urb = instance->int_urb;
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
513

514
	atm_dbg(instance->usbatm, "%s entered\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
515

516 517 518 519 520 521 522
	if (int_urb) {
		ret = usb_submit_urb(int_urb, GFP_ATOMIC);
		if (!ret)
			schedule_work(&instance->status_checker);
		else {
			atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
			mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
Linus Torvalds's avatar
Linus Torvalds committed
523 524
		}
	}
525
}
Linus Torvalds's avatar
Linus Torvalds committed
526

527 528 529 530 531 532
static void speedtch_handle_int(struct urb *int_urb, struct pt_regs *regs)
{
	struct speedtch_instance_data *instance = int_urb->context;
	struct usbatm_data *usbatm = instance->usbatm;
	unsigned int count = int_urb->actual_length;
	int ret = int_urb->status;
Linus Torvalds's avatar
Linus Torvalds committed
533

534
	/* The magic interrupt for "up state" */
535
	static const unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
536
	/* The magic interrupt for "down state" */
537
	static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
538 539

	atm_dbg(usbatm, "%s entered\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
540 541

	if (ret < 0) {
542 543
		atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, ret);
		goto fail;
Linus Torvalds's avatar
Linus Torvalds committed
544 545
	}

546 547
	if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
		del_timer(&instance->status_checker.timer);
548
		atm_info(usbatm, "DSL line goes up\n");
549
	} else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
550
		atm_info(usbatm, "DSL line goes down\n");
551 552
	} else {
		int i;
Linus Torvalds's avatar
Linus Torvalds committed
553

554 555 556 557 558 559
		atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count);
		for (i = 0; i < count; i++)
			printk(" %02x", instance->int_data[i]);
		printk("\n");
		goto fail;
	}
Linus Torvalds's avatar
Linus Torvalds committed
560

561 562 563
	if ((int_urb = instance->int_urb)) {
		ret = usb_submit_urb(int_urb, GFP_ATOMIC);
		schedule_work(&instance->status_checker);
Linus Torvalds's avatar
Linus Torvalds committed
564
		if (ret < 0) {
565 566
			atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
			goto fail;
Linus Torvalds's avatar
Linus Torvalds committed
567 568 569 570 571
		}
	}

	return;

572 573 574
fail:
	if ((int_urb = instance->int_urb))
		mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
Linus Torvalds's avatar
Linus Torvalds committed
575 576
}

577
static int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
Linus Torvalds's avatar
Linus Torvalds committed
578
{
579 580 581 582
	struct usb_device *usb_dev = usbatm->usb_dev;
	struct speedtch_instance_data *instance = usbatm->driver_data;
	int i, ret;
	unsigned char mac_str[13];
Linus Torvalds's avatar
Linus Torvalds committed
583

584
	atm_dbg(usbatm, "%s entered\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
585

586 587 588
	if ((ret = usb_set_interface(usb_dev, 1, altsetting)) < 0) {
		atm_dbg(usbatm, "%s: usb_set_interface returned %d!\n", __func__, ret);
		return ret;
Linus Torvalds's avatar
Linus Torvalds committed
589 590
	}

591 592 593 594 595 596
	/* Set MAC address, it is stored in the serial number */
	memset(atm_dev->esi, 0, sizeof(atm_dev->esi));
	if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
		for (i = 0; i < 6; i++)
			atm_dev->esi[i] = (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
	}
Linus Torvalds's avatar
Linus Torvalds committed
597

598 599
	/* Start modem synchronisation */
	ret = speedtch_start_synchro(instance);
Linus Torvalds's avatar
Linus Torvalds committed
600

601 602 603 604 605 606 607 608
	/* Set up interrupt endpoint */
	if (instance->int_urb) {
		ret = usb_submit_urb(instance->int_urb, GFP_KERNEL);
		if (ret < 0) {
			/* Doesn't matter; we'll poll anyway */
			atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret);
			usb_free_urb(instance->int_urb);
			instance->int_urb = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
609 610 611
		}
	}

612 613
	/* Start status polling */
	mod_timer(&instance->status_checker.timer, jiffies + msecs_to_jiffies(1000));
Linus Torvalds's avatar
Linus Torvalds committed
614 615 616 617

	return 0;
}

618
static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
Linus Torvalds's avatar
Linus Torvalds committed
619
{
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
	struct speedtch_instance_data *instance = usbatm->driver_data;
	struct urb *int_urb = instance->int_urb;

	atm_dbg(usbatm, "%s entered\n", __func__);

	del_timer_sync(&instance->status_checker.timer);

	/*
	 * Since resubmit_timer and int_urb can schedule themselves and
	 * each other, shutting them down correctly takes some care
	 */
	instance->int_urb = NULL; /* signal shutdown */
	mb();
	usb_kill_urb(int_urb);
	del_timer_sync(&instance->resubmit_timer);
	/*
	 * At this point, speedtch_handle_int and speedtch_resubmit_int
	 * can run or be running, but instance->int_urb == NULL means that
	 * they will not reschedule
	 */
	usb_kill_urb(int_urb);
	del_timer_sync(&instance->resubmit_timer);
	usb_free_urb(int_urb);
Linus Torvalds's avatar
Linus Torvalds committed
643

644 645
	flush_scheduled_work();
}
Linus Torvalds's avatar
Linus Torvalds committed
646 647


648 649 650
/**********
**  USB  **
**********/
Linus Torvalds's avatar
Linus Torvalds committed
651

652 653 654 655
static struct usb_device_id speedtch_usb_ids[] = {
	{USB_DEVICE(0x06b9, 0x4061)},
	{}
};
Linus Torvalds's avatar
Linus Torvalds committed
656

657
MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
Linus Torvalds's avatar
Linus Torvalds committed
658

659
static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *);
Linus Torvalds's avatar
Linus Torvalds committed
660

661 662 663 664 665 666
static struct usb_driver speedtch_usb_driver = {
	.name		= speedtch_driver_name,
	.probe		= speedtch_usb_probe,
	.disconnect	= usbatm_usb_disconnect,
	.id_table	= speedtch_usb_ids
};
Linus Torvalds's avatar
Linus Torvalds committed
667

668 669 670
static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) {
	struct usb_interface *cur_intf;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
671

672 673 674 675 676
	for(i = 0; i < num_interfaces; i++)
		if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
			usb_set_intfdata(cur_intf, NULL);
			usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
		}
Linus Torvalds's avatar
Linus Torvalds committed
677 678
}

679 680 681 682
static int speedtch_bind(struct usbatm_data *usbatm,
			 struct usb_interface *intf,
			 const struct usb_device_id *id,
			 int *need_heavy_init)
Linus Torvalds's avatar
Linus Torvalds committed
683
{
684 685 686 687 688 689
	struct usb_device *usb_dev = interface_to_usbdev(intf);
	struct usb_interface *cur_intf;
	struct speedtch_instance_data *instance;
	int ifnum = intf->altsetting->desc.bInterfaceNumber;
	int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces;
	int i, ret;
Linus Torvalds's avatar
Linus Torvalds committed
690

691
	usb_dbg(usbatm, "%s entered\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
692

693 694
	if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
		usb_dbg(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
Linus Torvalds's avatar
Linus Torvalds committed
695 696 697
		return -ENODEV;
	}

698
	/* claim all interfaces */
Linus Torvalds's avatar
Linus Torvalds committed
699

700 701
	for (i=0; i < num_interfaces; i++) {
		cur_intf = usb_ifnum_to_if(usb_dev, i);
Linus Torvalds's avatar
Linus Torvalds committed
702

703 704
		if ((i != ifnum) && cur_intf) {
			ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm);
Linus Torvalds's avatar
Linus Torvalds committed
705

706 707 708 709 710 711 712
			if (ret < 0) {
				usb_dbg(usbatm, "%s: failed to claim interface %d (%d)\n", __func__, i, ret);
				speedtch_release_interfaces(usb_dev, i);
				return ret;
			}
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
713 714

	instance = kmalloc(sizeof(*instance), GFP_KERNEL);
715

Linus Torvalds's avatar
Linus Torvalds committed
716
	if (!instance) {
717 718 719
		usb_dbg(usbatm, "%s: no memory for instance data!\n", __func__);
		ret = -ENOMEM;
		goto fail_release;
Linus Torvalds's avatar
Linus Torvalds committed
720 721 722 723
	}

	memset(instance, 0, sizeof(struct speedtch_instance_data));

724
	instance->usbatm = usbatm;
Linus Torvalds's avatar
Linus Torvalds committed
725

726
	INIT_WORK(&instance->status_checker, (void *)speedtch_check_status, instance);
Linus Torvalds's avatar
Linus Torvalds committed
727

728 729
	instance->status_checker.timer.function = speedtch_status_poll;
	instance->status_checker.timer.data = (unsigned long)instance;
730
	instance->last_status = 0xff;
731
	instance->poll_delay = MIN_POLL_DELAY;
Linus Torvalds's avatar
Linus Torvalds committed
732

733 734 735
	init_timer(&instance->resubmit_timer);
	instance->resubmit_timer.function = speedtch_resubmit_int;
	instance->resubmit_timer.data = (unsigned long)instance;
Linus Torvalds's avatar
Linus Torvalds committed
736

737
	instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
738

739 740 741 742 743 744 745
	if (instance->int_urb)
		usb_fill_int_urb(instance->int_urb, usb_dev,
				 usb_rcvintpipe(usb_dev, ENDPOINT_INT),
				 instance->int_data, sizeof(instance->int_data),
				 speedtch_handle_int, instance, 50);
	else
		usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
746

747 748 749 750
	/* check whether the modem already seems to be alive */
	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
			      0x12, 0xc0, 0x07, 0x00,
			      instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
Linus Torvalds's avatar
Linus Torvalds committed
751

752
	*need_heavy_init = (ret != SIZE_7);
Linus Torvalds's avatar
Linus Torvalds committed
753

754 755 756 757 758
	usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, need_heavy_init ? "not" : "already");

	if (*need_heavy_init)
		if ((ret = usb_reset_device(usb_dev)) < 0)
			goto fail_free;
Linus Torvalds's avatar
Linus Torvalds committed
759

760
        usbatm->driver_data = instance;
Linus Torvalds's avatar
Linus Torvalds committed
761 762 763

	return 0;

764 765
fail_free:
	usb_free_urb(instance->int_urb);
Linus Torvalds's avatar
Linus Torvalds committed
766
	kfree(instance);
767 768 769
fail_release:
	speedtch_release_interfaces(usb_dev, num_interfaces);
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
770 771
}

772
static void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
Linus Torvalds's avatar
Linus Torvalds committed
773
{
774 775
	struct usb_device *usb_dev = interface_to_usbdev(intf);
	struct speedtch_instance_data *instance = usbatm->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
776

777
	usb_dbg(usbatm, "%s entered\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
778

779 780 781
	speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces);
	usb_free_urb(instance->int_urb);
	kfree(instance);
Linus Torvalds's avatar
Linus Torvalds committed
782 783
}

784

Linus Torvalds's avatar
Linus Torvalds committed
785 786 787 788
/***********
**  init  **
***********/

789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
static struct usbatm_driver speedtch_usbatm_driver = {
	.owner		= THIS_MODULE,
	.driver_name	= speedtch_driver_name,
	.bind		= speedtch_bind,
	.heavy_init	= speedtch_heavy_init,
	.unbind		= speedtch_unbind,
	.atm_start	= speedtch_atm_start,
	.atm_stop	= speedtch_atm_stop,
	.in		= ENDPOINT_DATA,
	.out		= ENDPOINT_DATA
};

static int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver);
}

Linus Torvalds's avatar
Linus Torvalds committed
806 807
static int __init speedtch_usb_init(void)
{
808
	dbg("%s: driver version %s", __func__, DRIVER_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
809 810 811 812 813 814

	return usb_register(&speedtch_usb_driver);
}

static void __exit speedtch_usb_cleanup(void)
{
815
	dbg("%s", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
816 817 818 819 820 821 822 823 824 825 826

	usb_deregister(&speedtch_usb_driver);
}

module_init(speedtch_usb_init);
module_exit(speedtch_usb_cleanup);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);