11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/******************************************************************************
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  speedtch.c  -  Alcatel SpeedTouch USB xDSL modem driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (C) 2001, Alcatel
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (C) 2003, Duncan Sands
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Copyright (C) 2004, David Woodhouse
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands *  Based on "modem_run.c", copyright (C) 2001, Benoit Papillault
948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  This program is free software; you can redistribute it and/or modify it
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  under the terms of the GNU General Public License as published by the Free
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Software Foundation; either version 2 of the License, or (at your option)
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  any later version.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  This program is distributed in the hope that it will be useful, but WITHOUT
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  more details.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  You should have received a copy of the GNU General Public License along with
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  this program; if not, write to the Free Software Foundation, Inc., 59
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ******************************************************************************/
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <asm/page.h>
2748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/device.h>
2848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/errno.h>
2948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/firmware.h>
3048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/init.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
3248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/module.h>
3348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/moduleparam.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
3548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/stat.h>
3648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/timer.h>
3780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands#include <linux/types.h>
385f848137744106ee737f559454ce5adfceb38347David Brownell#include <linux/usb/ch9.h>
3948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include <linux/workqueue.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#include "usbatm.h"
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR	"Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
449b0e54addf3ea8488c7b57166fb38feeb8ea28fdDuncan Sands#define DRIVER_VERSION	"1.10"
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"Alcatel SpeedTouch USB driver version " DRIVER_VERSION
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char speedtch_driver_name[] = "speedtch";
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define CTRL_TIMEOUT 2000	/* milliseconds */
5048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define DATA_TIMEOUT 2000	/* milliseconds */
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define OFFSET_7	0		/* size 1 */
5348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define OFFSET_b	1		/* size 8 */
5448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define OFFSET_d	9		/* size 4 */
5548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define OFFSET_e	13		/* size 1 */
5648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define OFFSET_f	14		/* size 1 */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define SIZE_7		1
5948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define SIZE_b		8
6048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define SIZE_d		4
6148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define SIZE_e		1
6248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define SIZE_f		1
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define MIN_POLL_DELAY		5000	/* milliseconds */
6548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define MAX_POLL_DELAY		60000	/* milliseconds */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define RESUBMIT_DELAY		1000	/* milliseconds */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6980aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands#define DEFAULT_BULK_ALTSETTING	1
7067c752b41a4238c1a2d7eebcd061ff8c1127d3e9Duncan Sands#define DEFAULT_ISOC_ALTSETTING	3
7148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define DEFAULT_DL_512_FIRST	0
7280aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands#define DEFAULT_ENABLE_ISOC	0
7348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define DEFAULT_SW_BUFFERING	0
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sandsstatic unsigned int altsetting = 0; /* zero means: use the default */
7690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool dl_512_first = DEFAULT_DL_512_FIRST;
7790ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool enable_isoc = DEFAULT_ENABLE_ISOC;
7890ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool sw_buffering = DEFAULT_SW_BUFFERING;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
806a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands#define DEFAULT_B_MAX_DSL	8128
816a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands#define DEFAULT_MODEM_MODE	11
826a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands#define MODEM_OPTION_LENGTH	16
836a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsstatic const unsigned char DEFAULT_MODEM_OPTION[MODEM_OPTION_LENGTH] = {
846a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
856a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands};
866a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
876a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsstatic unsigned int BMaxDSL = DEFAULT_B_MAX_DSL;
886a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsstatic unsigned char ModemMode = DEFAULT_MODEM_MODE;
896a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsstatic unsigned char ModemOption[MODEM_OPTION_LENGTH];
9064a6f9500d8e8a8e1b1adc2120e56cc88df5727fAl Virostatic unsigned int num_ModemOption;
916a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
9280aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sandsmodule_param(altsetting, uint, S_IRUGO | S_IWUSR);
9348da7267ff1631b0bff1eab15db86adace11ea91Duncan SandsMODULE_PARM_DESC(altsetting,
9480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		"Alternative setting for data interface (bulk_default: "
9580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		__MODULE_STRING(DEFAULT_BULK_ALTSETTING) "; isoc_default: "
9680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		__MODULE_STRING(DEFAULT_ISOC_ALTSETTING) ")");
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsmodule_param(dl_512_first, bool, S_IRUGO | S_IWUSR);
9948da7267ff1631b0bff1eab15db86adace11ea91Duncan SandsMODULE_PARM_DESC(dl_512_first,
10048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		 "Read 512 bytes before sending firmware (default: "
10148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		 __MODULE_STRING(DEFAULT_DL_512_FIRST) ")");
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10380aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sandsmodule_param(enable_isoc, bool, S_IRUGO | S_IWUSR);
10480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan SandsMODULE_PARM_DESC(enable_isoc,
10580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		"Use isochronous transfers if available (default: "
10680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		__MODULE_STRING(DEFAULT_ENABLE_ISOC) ")");
10780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
10848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsmodule_param(sw_buffering, bool, S_IRUGO | S_IWUSR);
10948da7267ff1631b0bff1eab15db86adace11ea91Duncan SandsMODULE_PARM_DESC(sw_buffering,
11048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		 "Enable software buffering (default: "
11148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		 __MODULE_STRING(DEFAULT_SW_BUFFERING) ")");
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1136a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsmodule_param(BMaxDSL, uint, S_IRUGO | S_IWUSR);
1146a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan SandsMODULE_PARM_DESC(BMaxDSL,
1156a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		"default: " __MODULE_STRING(DEFAULT_B_MAX_DSL));
1166a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
1176a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsmodule_param(ModemMode, byte, S_IRUGO | S_IWUSR);
1186a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan SandsMODULE_PARM_DESC(ModemMode,
1196a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		"default: " __MODULE_STRING(DEFAULT_MODEM_MODE));
1206a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
1216a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsmodule_param_array(ModemOption, byte, &num_ModemOption, S_IRUGO);
1226a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan SandsMODULE_PARM_DESC(ModemOption, "default: 0x10,0x00,0x00,0x00,0x20");
1236a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
1246f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands#define INTERFACE_DATA		1
12548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define ENDPOINT_INT		0x81
12680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands#define ENDPOINT_BULK_DATA	0x07
12780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands#define ENDPOINT_ISOC_DATA	0x07
12848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands#define ENDPOINT_FIRMWARE	0x05
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1306a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sandsstruct speedtch_params {
1316a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	unsigned int altsetting;
1326a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	unsigned int BMaxDSL;
1336a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	unsigned char ModemMode;
1346a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	unsigned char ModemOption[MODEM_OPTION_LENGTH];
1356a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands};
1366a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct speedtch_instance_data {
13848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm;
13948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
1406a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	struct speedtch_params params; /* set in probe, constant afterwards */
1416f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands
14237c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	struct timer_list status_check_timer;
14337c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	struct work_struct status_check_work;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	unsigned char last_status;
1461a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands
14748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int poll_delay; /* milliseconds */
14848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
14948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct timer_list resubmit_timer;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct urb *int_urb;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char int_data[16];
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1536a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	unsigned char scratch_buffer[16];
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/***************
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**  firmware  **
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds***************/
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic void speedtch_set_swbuff(struct speedtch_instance_data *instance, int state)
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
16348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
16748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x32, 0x40, state ? 0x01 : 0x00, 0x00, NULL, 0, CTRL_TIMEOUT);
16848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (ret < 0)
16948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_warn(usbatm,
17048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			 "%sabling SW buffering: usb_control_msg returned %d\n",
17148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			 state ? "En" : "Dis", ret);
17248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	else
17348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void speedtch_test_sequence(struct speedtch_instance_data *instance)
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
17948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
18048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char *buf = instance->scratch_buffer;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* URB 147 */
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[0] = 0x1c;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[1] = 0x50;
18648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
18748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x01, 0x40, 0x0b, 0x00, buf, 2, CTRL_TIMEOUT);
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0)
18948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_warn(usbatm, "%s failed on URB147: %d\n", __func__, ret);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* URB 148 */
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[0] = 0x32;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[1] = 0x00;
19448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
19548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x01, 0x40, 0x02, 0x00, buf, 2, CTRL_TIMEOUT);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0)
19748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_warn(usbatm, "%s failed on URB148: %d\n", __func__, ret);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* URB 149 */
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[0] = 0x01;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[1] = 0x00;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[2] = 0x01;
20348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
20448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x01, 0x40, 0x03, 0x00, buf, 3, CTRL_TIMEOUT);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0)
20648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_warn(usbatm, "%s failed on URB149: %d\n", __func__, ret);
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* URB 150 */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[0] = 0x01;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[1] = 0x00;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[2] = 0x01;
21248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
21348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x01, 0x40, 0x04, 0x00, buf, 3, CTRL_TIMEOUT);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0)
21548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_warn(usbatm, "%s failed on URB150: %d\n", __func__, ret);
2166a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
2176a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* Extra initialisation in recent drivers - gives higher speeds */
2186a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
2196a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* URBext1 */
2206a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	buf[0] = instance->params.ModemMode;
2216a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
2226a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			      0x01, 0x40, 0x11, 0x00, buf, 1, CTRL_TIMEOUT);
2236a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (ret < 0)
2246a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		usb_warn(usbatm, "%s failed on URBext1: %d\n", __func__, ret);
2256a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
2266a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* URBext2 */
2276a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* This seems to be the one which actually triggers the higher sync
2286a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	   rate -- it does require the new firmware too, although it works OK
2296a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	   with older firmware */
2306a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
2316a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			      0x01, 0x40, 0x14, 0x00,
2326a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			      instance->params.ModemOption,
2336a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			      MODEM_OPTION_LENGTH, CTRL_TIMEOUT);
2346a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (ret < 0)
2356a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		usb_warn(usbatm, "%s failed on URBext2: %d\n", __func__, ret);
2366a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands
2376a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* URBext3 */
2386a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	buf[0] = instance->params.BMaxDSL & 0xff;
2396a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	buf[1] = instance->params.BMaxDSL >> 8;
2406a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	ret = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
2416a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			      0x01, 0x40, 0x12, 0x00, buf, 2, CTRL_TIMEOUT);
2426a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (ret < 0)
2436a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		usb_warn(usbatm, "%s failed on URBext3: %d\n", __func__, ret);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_upload_firmware(struct speedtch_instance_data *instance,
24748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				     const struct firmware *fw1,
24848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				     const struct firmware *fw2)
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
25048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char *buffer;
25148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
25248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
25348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int actual_length;
25448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int ret = 0;
25548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int offset;
25648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
25748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s entered\n", __func__);
25848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
25948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
26048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = -ENOMEM;
26148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__);
26248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto out;
26348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
26448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
265011db815231f40d4d53531b5d41b82c8dc7c44bfMicah Gruber	if (!usb_ifnum_to_if(usb_dev, 2)) {
26648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = -ENODEV;
26748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_dbg(usbatm, "%s: interface not found!\n", __func__);
26848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto out_free;
26948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
27048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
27148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* URB 7 */
27248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (dl_512_first) {	/* some modems need a read before writing the firmware */
27348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
27448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				   buffer, 0x200, &actual_length, 2000);
27548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
27648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (ret < 0 && ret != -ETIMEDOUT)
2770ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			usb_warn(usbatm, "%s: read BLOCK0 from modem failed (%d)!\n", __func__, ret);
27848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		else
27948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			usb_dbg(usbatm, "%s: BLOCK0 downloaded (%d bytes)\n", __func__, ret);
28048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
28148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
28248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* URB 8 : both leds are static green */
28348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
28448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
28548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		memcpy(buffer, fw1->data + offset, thislen);
28648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
28748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
28848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				   buffer, thislen, &actual_length, DATA_TIMEOUT);
28948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
29048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (ret < 0) {
2910ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			usb_err(usbatm, "%s: write BLOCK1 to modem failed (%d)!\n", __func__, ret);
29248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			goto out_free;
29348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		}
29448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_dbg(usbatm, "%s: BLOCK1 uploaded (%zu bytes)\n", __func__, fw1->size);
29548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
29648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
29748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* USB led blinking green, ADSL led off */
29848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
29948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* URB 11 */
30048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
30148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			   buffer, 0x200, &actual_length, DATA_TIMEOUT);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
3040ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_err(usbatm, "%s: read BLOCK2 from modem failed (%d)!\n", __func__, ret);
30548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto out_free;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
30748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s: BLOCK2 downloaded (%d bytes)\n", __func__, actual_length);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* URBs 12 to 139 - USB led blinking green, ADSL led off */
31048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
31148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
31248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		memcpy(buffer, fw2->data + offset, thislen);
31348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
31448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
31548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				   buffer, thislen, &actual_length, DATA_TIMEOUT);
31648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
31748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (ret < 0) {
3180ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			usb_err(usbatm, "%s: write BLOCK3 to modem failed (%d)!\n", __func__, ret);
31948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			goto out_free;
32048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		}
32148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
32248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s: BLOCK3 uploaded (%zu bytes)\n", __func__, fw2->size);
32348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
32448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* USB led static green, ADSL led static red */
32548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
32648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* URB 142 */
32748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, ENDPOINT_FIRMWARE),
32848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			   buffer, 0x200, &actual_length, DATA_TIMEOUT);
32948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
33048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (ret < 0) {
3310ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_err(usbatm, "%s: read BLOCK4 from modem failed (%d)!\n", __func__, ret);
33248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto out_free;
33348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
33448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
33548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* success */
33648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s: BLOCK4 downloaded (%d bytes)\n", __func__, actual_length);
33748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
33848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Delay to allow firmware to start up. We can do this here
33948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	   because we're in our own kernel thread anyway. */
34048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	msleep_interruptible(1000);
34148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
3426a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
3436a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		usb_err(usbatm, "%s: setting interface to %d failed (%d)!\n", __func__, instance->params.altsetting, ret);
3446f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands		goto out_free;
3456f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands	}
3466f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands
34748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Enable software buffering, if requested */
34848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (sw_buffering)
34948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		speedtch_set_swbuff(instance, 1);
35048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
35148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Magic spell; don't ask us what this does */
35248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	speedtch_test_sequence(instance);
35348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
35448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = 0;
35548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
35648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsout_free:
35748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	free_page((unsigned long)buffer);
35848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsout:
35948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return ret;
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3620ec3c7e856319b600311750d784262caa8ed94b9Duncan Sandsstatic int speedtch_find_firmware(struct usbatm_data *usbatm, struct usb_interface *intf,
3630ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands				  int phase, const struct firmware **fw_p)
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
36548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct device *dev = &intf->dev;
36648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	const u16 bcdDevice = le16_to_cpu(interface_to_usbdev(intf)->descriptor.bcdDevice);
36748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	const u8 major_revision = bcdDevice >> 8;
36848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	const u8 minor_revision = bcdDevice & 0xff;
36948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	char buf[24];
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
3720ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (request_firmware(fw_p, buf, dev)) {
37548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
3760ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
37848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (request_firmware(fw_p, buf, dev)) {
37948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			sprintf(buf, "speedtch-%d.bin", phase);
3800ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			usb_dbg(usbatm, "%s: looking for %s\n", __func__, buf);
38148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
38248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			if (request_firmware(fw_p, buf, dev)) {
3830ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands				usb_err(usbatm, "%s: no stage %d firmware found!\n", __func__, phase);
38448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				return -ENOENT;
38548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			}
38648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3890ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	usb_info(usbatm, "found stage %d firmware %s\n", phase, buf);
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return 0;
39248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands}
39348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
39448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_heavy_init(struct usbatm_data *usbatm, struct usb_interface *intf)
39548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands{
39648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	const struct firmware *fw1, *fw2;
39748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = usbatm->driver_data;
39848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int ret;
39948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
4000ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	if ((ret = speedtch_find_firmware(usbatm, intf, 1, &fw1)) < 0)
4010ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		return ret;
40248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
4030ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	if ((ret = speedtch_find_firmware(usbatm, intf, 2, &fw2)) < 0) {
40448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		release_firmware(fw1);
40548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		return ret;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4080ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	if ((ret = speedtch_upload_firmware(instance, fw1, fw2)) < 0)
4090ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_err(usbatm, "%s: firmware upload failed (%d)!\n", __func__, ret);
41048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
41148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	release_firmware(fw2);
41248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	release_firmware(fw1);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return ret;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
41748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
41848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands/**********
41948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands**  ATM  **
42048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands**********/
42148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
42248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_read_status(struct speedtch_instance_data *instance)
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
42448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
42548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
42648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char *buf = instance->scratch_buffer;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4296a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	memset(buf, 0, 16);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
43148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      CTRL_TIMEOUT);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
43548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: MSG 7 failed\n", __func__);
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
43948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      CTRL_TIMEOUT);
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
44348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: MSG B failed\n", __func__);
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      CTRL_TIMEOUT);
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
45148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: MSG D failed\n", __func__);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
45548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      CTRL_TIMEOUT);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
45948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: MSG E failed\n", __func__);
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
46348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      CTRL_TIMEOUT);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret < 0) {
46748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: MSG F failed\n", __func__);
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_start_synchro(struct speedtch_instance_data *instance)
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
47648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
47748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
47848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char *buf = instance->scratch_buffer;
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
48148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(usbatm, "%s entered\n", __func__);
48248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
48348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	memset(buf, 0, 2);
48448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
48548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
48648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x12, 0xc0, 0x04, 0x00,
48748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      buf, 2, CTRL_TIMEOUT);
48848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
48948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (ret < 0)
49048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_warn(usbatm, "failed to start ADSL synchronisation: %d\n", ret);
49148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	else
49248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: modem prodded. %d bytes returned: %02x %02x\n",
49348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			__func__, ret, buf[0], buf[1]);
49448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
49548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return ret;
49648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands}
49748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
498c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howellsstatic void speedtch_check_status(struct work_struct *work)
49948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands{
500c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells	struct speedtch_instance_data *instance =
501c4028958b6ecad064b1a6303a6a5906d4fe48d73David Howells		container_of(work, struct speedtch_instance_data,
50237c95bfe944babae817bfcf02c996729c9a3335dTejun Heo			     status_check_work);
50348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
50448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct atm_dev *atm_dev = usbatm->atm_dev;
50548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char *buf = instance->scratch_buffer;
5061a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	int down_speed, up_speed, ret;
5071a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	unsigned char status;
50848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
5090ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands#ifdef VERBOSE_DEBUG
51048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(usbatm, "%s entered\n", __func__);
5110ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands#endif
51248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
51348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = speedtch_read_status(instance);
51448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (ret < 0) {
51548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_warn(usbatm, "error %d fetching device status\n", ret);
516cd5c08fb7b0d960b7cd48bc977feee7b3bd8b046Duncan Sands		instance->poll_delay = min(2 * instance->poll_delay, MAX_POLL_DELAY);
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
520cd5c08fb7b0d960b7cd48bc977feee7b3bd8b046Duncan Sands	instance->poll_delay = max(instance->poll_delay / 2, MIN_POLL_DELAY);
52148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
5221a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	status = buf[OFFSET_7];
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5241a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	if ((status != instance->last_status) || !status) {
5250ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		atm_dbg(usbatm, "%s: line state 0x%02x\n", __func__, status);
5260ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands
5271a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		switch (status) {
5281a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		case 0:
52923f89f0488fa0fc843503fa07768d0d3edde3c44Karl Hiramoto			atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
5301a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			if (instance->last_status)
53152fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller				atm_info(usbatm, "ADSL line is down\n");
5321a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			/* It may never resync again unless we ask it to... */
53348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			ret = speedtch_start_synchro(instance);
5341a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			break;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		case 0x08:
53723f89f0488fa0fc843503fa07768d0d3edde3c44Karl Hiramoto			atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
53852fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller			atm_info(usbatm, "ADSL line is blocked?\n");
5391a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			break;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		case 0x10:
54223f89f0488fa0fc843503fa07768d0d3edde3c44Karl Hiramoto			atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST);
54352fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller			atm_info(usbatm, "ADSL line is synchronising\n");
5441a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			break;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		case 0x20:
5471a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				| (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
5491a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				| (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			if (!(down_speed & 0x0000ffff) && !(up_speed & 0x0000ffff)) {
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				down_speed >>= 16;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				up_speed >>= 16;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			atm_dev->link_rate = down_speed * 1000 / 424;
55823f89f0488fa0fc843503fa07768d0d3edde3c44Karl Hiramoto			atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND);
55948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
56048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			atm_info(usbatm,
561322a95bc8eba889d2f9d7222936d682c9aad8294Duncan Sands				 "ADSL line is up (%d kb/s down | %d kb/s up)\n",
56248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				 down_speed, up_speed);
5631a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			break;
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5651a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		default:
56623f89f0488fa0fc843503fa07768d0d3edde3c44Karl Hiramoto			atm_dev_signal_change(atm_dev, ATM_PHY_SIG_UNKNOWN);
5670ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			atm_info(usbatm, "unknown line state %02x\n", status);
5681a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands			break;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5701a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands
5711a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands		instance->last_status = status;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic void speedtch_status_poll(unsigned long data)
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct speedtch_instance_data *instance = (void *)data;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
57937c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	schedule_work(&instance->status_check_work);
58048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
58148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* The following check is racy, but the race is harmless */
58248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (instance->poll_delay < MAX_POLL_DELAY)
58337c95bfe944babae817bfcf02c996729c9a3335dTejun Heo		mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(instance->poll_delay));
58448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	else
58552fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller		atm_warn(instance->usbatm, "Too many failures - disabling line status polling\n");
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic void speedtch_resubmit_int(unsigned long data)
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
59048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = (void *)data;
59148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct urb *int_urb = instance->int_urb;
59248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int ret;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(instance->usbatm, "%s entered\n", __func__);
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (int_urb) {
59748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_submit_urb(int_urb, GFP_ATOMIC);
59848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (!ret)
59937c95bfe944babae817bfcf02c996729c9a3335dTejun Heo			schedule_work(&instance->status_check_work);
60048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		else {
60148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			atm_dbg(instance->usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
60248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
60548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands}
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6077d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void speedtch_handle_int(struct urb *int_urb)
60848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands{
60948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = int_urb->context;
61048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usbatm_data *usbatm = instance->usbatm;
61148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned int count = int_urb->actual_length;
6129a5a3e95b49c93813476974abaa038c9d36bdd14Greg Kroah-Hartman	int status = int_urb->status;
6139a5a3e95b49c93813476974abaa038c9d36bdd14Greg Kroah-Hartman	int ret;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* The magic interrupt for "up state" */
6163c6bee1d4037a5c569f30d40bd852a57ba250912Jesper Juhl	static const unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
61748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* The magic interrupt for "down state" */
6183c6bee1d4037a5c569f30d40bd852a57ba250912Jesper Juhl	static const unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
61948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
62048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(usbatm, "%s entered\n", __func__);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6229a5a3e95b49c93813476974abaa038c9d36bdd14Greg Kroah-Hartman	if (status < 0) {
6239a5a3e95b49c93813476974abaa038c9d36bdd14Greg Kroah-Hartman		atm_dbg(usbatm, "%s: nonzero urb status %d!\n", __func__, status);
62448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto fail;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
62748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
62837c95bfe944babae817bfcf02c996729c9a3335dTejun Heo		del_timer(&instance->status_check_timer);
62952fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller		atm_info(usbatm, "DSL line goes up\n");
63048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	} else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
63152fbae2a392b6e084195bedc7a280991a94c14d0David S. Miller		atm_info(usbatm, "DSL line goes down\n");
63248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	} else {
63348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		int i;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
63548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		atm_dbg(usbatm, "%s: unknown interrupt packet of length %d:", __func__, count);
63648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		for (i = 0; i < count; i++)
63748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			printk(" %02x", instance->int_data[i]);
63848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		printk("\n");
63948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto fail;
64048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
64248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if ((int_urb = instance->int_urb)) {
64348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_submit_urb(int_urb, GFP_ATOMIC);
64437c95bfe944babae817bfcf02c996729c9a3335dTejun Heo		schedule_work(&instance->status_check_work);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret < 0) {
64648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			atm_dbg(usbatm, "%s: usb_submit_urb failed with result %d\n", __func__, ret);
64748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			goto fail;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsfail:
65448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if ((int_urb = instance->int_urb))
65548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_atm_start(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
66048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = usbatm->usb_dev;
66148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = usbatm->driver_data;
66248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int i, ret;
66348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	unsigned char mac_str[13];
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
66548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(usbatm, "%s entered\n", __func__);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
66748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Set MAC address, it is stored in the serial number */
66848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	memset(atm_dev->esi, 0, sizeof(atm_dev->esi));
66948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
67048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		for (i = 0; i < 6; i++)
67196b89f323d6af996a7f6bd84d2119cbf7145f9a4Andy Shevchenko			atm_dev->esi[i] = (hex_to_bin(mac_str[i * 2]) << 4) +
67296b89f323d6af996a7f6bd84d2119cbf7145f9a4Andy Shevchenko				hex_to_bin(mac_str[i * 2 + 1]);
67348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
67548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Start modem synchronisation */
67648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = speedtch_start_synchro(instance);
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
67848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Set up interrupt endpoint */
67948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (instance->int_urb) {
68048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = usb_submit_urb(instance->int_urb, GFP_KERNEL);
68148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if (ret < 0) {
68248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			/* Doesn't matter; we'll poll anyway */
68348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			atm_dbg(usbatm, "%s: submission of interrupt URB failed (%d)!\n", __func__, ret);
68448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			usb_free_urb(instance->int_urb);
68548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			instance->int_urb = NULL;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* Start status polling */
69037c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	mod_timer(&instance->status_check_timer, jiffies + msecs_to_jiffies(1000));
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
69548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_dev)
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
69748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = usbatm->driver_data;
69848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct urb *int_urb = instance->int_urb;
69948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
70048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	atm_dbg(usbatm, "%s entered\n", __func__);
70148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
70237c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	del_timer_sync(&instance->status_check_timer);
70348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
70448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/*
70548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 * Since resubmit_timer and int_urb can schedule themselves and
70648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 * each other, shutting them down correctly takes some care
70748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 */
70848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->int_urb = NULL; /* signal shutdown */
70948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	mb();
71048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_kill_urb(int_urb);
71148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	del_timer_sync(&instance->resubmit_timer);
71248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/*
71348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 * At this point, speedtch_handle_int and speedtch_resubmit_int
71448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 * can run or be running, but instance->int_urb == NULL means that
71548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 * they will not reschedule
71648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	 */
71748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_kill_urb(int_urb);
71848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	del_timer_sync(&instance->resubmit_timer);
71948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_free_urb(int_urb);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721569ff2de2e1c8ac67c8df3a7367d46d0d9460a35Tejun Heo	flush_work_sync(&instance->status_check_work);
72248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands}
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7248fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Sternstatic int speedtch_pre_reset(struct usb_interface *intf)
7258fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern{
7268fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern	return 0;
7278fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern}
7288fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern
7298fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Sternstatic int speedtch_post_reset(struct usb_interface *intf)
7308fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern{
7318fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern	return 0;
7328fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern}
7338fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
73548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands/**********
73648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands**  USB  **
73748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands**********/
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
73948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic struct usb_device_id speedtch_usb_ids[] = {
74048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	{USB_DEVICE(0x06b9, 0x4061)},
74148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	{}
74248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands};
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74448da7267ff1631b0bff1eab15db86adace11ea91Duncan SandsMODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *);
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
74848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic struct usb_driver speedtch_usb_driver = {
74948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.name		= speedtch_driver_name,
75048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.probe		= speedtch_usb_probe,
75148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.disconnect	= usbatm_usb_disconnect,
7528fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern	.pre_reset	= speedtch_pre_reset,
7538fc7aeab3851ed8c3ecf28901ca2c6f0400955c7Alan Stern	.post_reset	= speedtch_post_reset,
75448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.id_table	= speedtch_usb_ids
75548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands};
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7576c4b7f70ba5ffb7fa1d19d2518664ea6ddb3cbf3Nicolas Kaiserstatic void speedtch_release_interfaces(struct usb_device *usb_dev,
7586c4b7f70ba5ffb7fa1d19d2518664ea6ddb3cbf3Nicolas Kaiser					int num_interfaces)
7596c4b7f70ba5ffb7fa1d19d2518664ea6ddb3cbf3Nicolas Kaiser{
76048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_interface *cur_intf;
76148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int i;
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7639196cc7bb46bece079398c9846050e8c8dc9235cNicolas Kaiser	for (i = 0; i < num_interfaces; i++)
76448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
76548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			usb_set_intfdata(cur_intf, NULL);
76648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
76748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		}
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
77048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_bind(struct usbatm_data *usbatm,
77148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			 struct usb_interface *intf,
77235644b0cce0ab8735944dcbfceb19e9e65da9a3dDuncan Sands			 const struct usb_device_id *id)
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
77448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = interface_to_usbdev(intf);
77580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	struct usb_interface *cur_intf, *data_intf;
77648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance;
77748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int ifnum = intf->altsetting->desc.bInterfaceNumber;
77848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int num_interfaces = usb_dev->actconfig->desc.bNumInterfaces;
77948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	int i, ret;
78080aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	int use_isoc;
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
78248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s entered\n", __func__);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7840ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands	/* sanity checks */
7850ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands
78648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (usb_dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
7870ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_err(usbatm, "%s: wrong device class %d\n", __func__, usb_dev->descriptor.bDeviceClass);
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
79180aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) {
79280aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		usb_err(usbatm, "%s: data interface not found!\n", __func__);
79380aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		return -ENODEV;
79480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	}
79580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
79648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* claim all interfaces */
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7989196cc7bb46bece079398c9846050e8c8dc9235cNicolas Kaiser	for (i = 0; i < num_interfaces; i++) {
79948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		cur_intf = usb_ifnum_to_if(usb_dev, i);
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
80148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		if ((i != ifnum) && cur_intf) {
80248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			ret = usb_driver_claim_interface(&speedtch_usb_driver, cur_intf, usbatm);
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
80448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			if (ret < 0) {
8050ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands				usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, i, ret);
80648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				speedtch_release_interfaces(usb_dev, i);
80748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				return ret;
80848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			}
80948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		}
81048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	}
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8129a734efec36c991a74610c6c81d28d4222e1c02bDuncan Sands	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
81348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!instance) {
8150ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		usb_err(usbatm, "%s: no memory for instance data!\n", __func__);
81648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		ret = -ENOMEM;
81748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		goto fail_release;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
82048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->usbatm = usbatm;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8226a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	/* module parameters may change at any moment, so take a snapshot */
8236a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	instance->params.altsetting = altsetting;
8246a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	instance->params.BMaxDSL = BMaxDSL;
8256a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	instance->params.ModemMode = ModemMode;
8266a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	memcpy(instance->params.ModemOption, DEFAULT_MODEM_OPTION, MODEM_OPTION_LENGTH);
8276a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	memcpy(instance->params.ModemOption, ModemOption, num_ModemOption);
82880aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	use_isoc = enable_isoc;
8296f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands
8306a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (instance->params.altsetting)
8316a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, instance->params.altsetting)) < 0) {
8326a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, instance->params.altsetting, ret);
8336a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands			instance->params.altsetting = 0; /* fall back to default */
8346f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands		}
8356f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands
8366a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (!instance->params.altsetting && use_isoc)
83780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_ISOC_ALTSETTING)) < 0) {
83880aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			usb_dbg(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_ISOC_ALTSETTING, ret);
83980aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			use_isoc = 0; /* fall back to bulk */
8406f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands		}
84180aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
84280aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	if (use_isoc) {
84380aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		const struct usb_host_interface *desc = data_intf->cur_altsetting;
84480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in;
84580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
84680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		use_isoc = 0; /* fall back to bulk if endpoint not found */
84780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
8489196cc7bb46bece079398c9846050e8c8dc9235cNicolas Kaiser		for (i = 0; i < desc->desc.bNumEndpoints; i++) {
84980aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
85080aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
85180aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			if ((endpoint_desc->bEndpointAddress == target_address)) {
852c5dd1f94246acdf6be6796db47efba8b2a93f93eLuiz Fernando N. Capitulino				use_isoc =
853c5dd1f94246acdf6be6796db47efba8b2a93f93eLuiz Fernando N. Capitulino					usb_endpoint_xfer_isoc(endpoint_desc);
85480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands				break;
85580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			}
85680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		}
85780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
85880aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		if (!use_isoc)
85980aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			usb_info(usbatm, "isochronous transfer not supported - using bulk\n");
8606f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands	}
8616f7494759870ec6fbb066f7202c5585fe36fbe82Duncan Sands
8626a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (!use_isoc && !instance->params.altsetting)
86380aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		if ((ret = usb_set_interface(usb_dev, INTERFACE_DATA, DEFAULT_BULK_ALTSETTING)) < 0) {
86480aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			usb_err(usbatm, "%s: setting interface to %2d failed (%d)!\n", __func__, DEFAULT_BULK_ALTSETTING, ret);
86580aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands			goto fail_free;
86680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands		}
86780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
8686a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands	if (!instance->params.altsetting)
8696a4f1b41357d2bd65d39f7a5d44e92f69daaf04bDuncan Sands		instance->params.altsetting = use_isoc ? DEFAULT_ISOC_ALTSETTING : DEFAULT_BULK_ALTSETTING;
87080aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
87180aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
87280aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands
87337c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	INIT_WORK(&instance->status_check_work, speedtch_check_status);
87437c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	init_timer(&instance->status_check_timer);
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
87637c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	instance->status_check_timer.function = speedtch_status_poll;
87737c95bfe944babae817bfcf02c996729c9a3335dTejun Heo	instance->status_check_timer.data = (unsigned long)instance;
8781a7aad15ff93be104c8e0851a43b94f8ccd92225Duncan Sands	instance->last_status = 0xff;
87948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->poll_delay = MIN_POLL_DELAY;
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
88148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	init_timer(&instance->resubmit_timer);
88248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->resubmit_timer.function = speedtch_resubmit_int;
88348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->resubmit_timer.data = (unsigned long)instance;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
88548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
88748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	if (instance->int_urb)
88848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_fill_int_urb(instance->int_urb, usb_dev,
88948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				 usb_rcvintpipe(usb_dev, ENDPOINT_INT),
89048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				 instance->int_data, sizeof(instance->int_data),
89148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands				 speedtch_handle_int, instance, 50);
89248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	else
89348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands		usb_dbg(usbatm, "%s: no memory for interrupt urb!\n", __func__);
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
89548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	/* check whether the modem already seems to be alive */
89648da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
89748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      0x12, 0xc0, 0x07, 0x00,
89848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			      instance->scratch_buffer + OFFSET_7, SIZE_7, 500);
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
90080aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	usbatm->flags |= (ret == SIZE_7 ? UDSL_SKIP_HEAVY_INIT : 0);
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
90235644b0cce0ab8735944dcbfceb19e9e65da9a3dDuncan Sands	usb_dbg(usbatm, "%s: firmware %s loaded\n", __func__, usbatm->flags & UDSL_SKIP_HEAVY_INIT ? "already" : "not");
90348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
90435644b0cce0ab8735944dcbfceb19e9e65da9a3dDuncan Sands	if (!(usbatm->flags & UDSL_SKIP_HEAVY_INIT))
9050ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		if ((ret = usb_reset_device(usb_dev)) < 0) {
9060ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands			usb_err(usbatm, "%s: device reset failed (%d)!\n", __func__, ret);
90748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands			goto fail_free;
9080ec3c7e856319b600311750d784262caa8ed94b9Duncan Sands		}
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands        usbatm->driver_data = instance;
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsfail_free:
91548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_free_urb(instance->int_urb);
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(instance);
91748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsfail_release:
91848da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	speedtch_release_interfaces(usb_dev, num_interfaces);
91948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return ret;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic void speedtch_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
92448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct usb_device *usb_dev = interface_to_usbdev(intf);
92548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	struct speedtch_instance_data *instance = usbatm->driver_data;
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92748da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_dbg(usbatm, "%s entered\n", __func__);
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	speedtch_release_interfaces(usb_dev, usb_dev->actconfig->desc.bNumInterfaces);
93048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	usb_free_urb(instance->int_urb);
93148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	kfree(instance);
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
93448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/***********
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**  init  **
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds***********/
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
93948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic struct usbatm_driver speedtch_usbatm_driver = {
94048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.driver_name	= speedtch_driver_name,
94148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.bind		= speedtch_bind,
94248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.heavy_init	= speedtch_heavy_init,
94348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.unbind		= speedtch_unbind,
94448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.atm_start	= speedtch_atm_start,
94548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	.atm_stop	= speedtch_atm_stop,
94680aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	.bulk_in	= ENDPOINT_BULK_DATA,
94780aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	.bulk_out	= ENDPOINT_BULK_DATA,
94880aae7a17afd21f7ba900dd566fb23a2444021f8Duncan Sands	.isoc_in	= ENDPOINT_ISOC_DATA
94948da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands};
95048da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
95148da7267ff1631b0bff1eab15db86adace11ea91Duncan Sandsstatic int speedtch_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
95248da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands{
95348da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands	return usbatm_usb_probe(intf, id, &speedtch_usbatm_driver);
95448da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands}
95548da7267ff1631b0bff1eab15db86adace11ea91Duncan Sands
95665db43054065790a75291b0834657445fea2cf56Greg Kroah-Hartmanmodule_usb_driver(speedtch_usb_driver);
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR(DRIVER_AUTHOR);
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(DRIVER_DESC);
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_VERSION(DRIVER_VERSION);
962