Zyxel 630-11/13 and Asus AAM600UG ADSL USB modem

Aurelio Arroyo listas_sk3 at yahoo.es
Sun Jan 16 19:13:12 EST 2005


It is the the first public version of amedyn module
for the new usb_atm.

Test by me whit Zyxel 630-11 and kernel 2.6.10 in FC3.
And by Emmanuel Counasse whit Asus AAM600UG and kernel
2.6.9 in Debian Sid.

CVS at :
http://cvs.sourceforge.net/viewcvs.py/zyxel630-11/amedyn2/module/

For full package:

- cvs
-d:pserver:anonymous at cvs.sourceforge.net:/cvsroot/zyxel630-11
login
- cvs -z3
-d:pserver:anonymous at cvs.sourceforge.net:/cvsroot/zyxel630-11
co -P amedyn2


---------------------------

/******************************************************************************
 *  amedyn.c  -  Zyxel 630-11/13 and Asus AAM600UG ALC
USB xDSL modem driver
 *
 *  Copyright (C) 2001, Alcatel
 *  Copyright (C) 2003, Duncan Sands
 *  Copyright (C) 2004, David Woodhouse
 *
 *  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.
 *

******************************************************************************/

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <linux/firmware.h>

#include "usb_atm.h"

#if defined(CONFIG_FW_LOADER) ||
defined(CONFIG_FW_LOADER_MODULE)
#	define USE_FW_LOADER
#endif

#define DRIVER_AUTHOR	"Aurelio Arroyo, Josep Comas,
David Woodhouse, Duncan Sands"
#define DRIVER_VERSION	"0.8"
#define DRIVER_DESC	"Zyxel 630-11/13 and Asus AAM600UG
ALC USB driver version " DRIVER_VERSION

static const char amedyn_driver_name[] = "amedyn";

/* Alcatel Microelectronics new reference design */
#define AME_VENDORID2		        0x06b9  /* Vendor =
Zyxel */
#define AME_PRODUCTID2		        0xa5a5  /* Product =
630-11 & 630-13 */
#define AME_VENDORID3		        0x0b05  /* Vendor =
Asustek */
#define AME_PRODUCTID3		        0x6206  /* Product =
AAM6000UG with Alcatel chipset */

/* Modem types */
#define UDSL_MODEM_TYPE1                0  /* Alcatel
reference design */
#define UDSL_MODEM_TYPE2                1  /* Conexant
reference design */
#define UDSL_MODEM_TYPE3                2  /* 3Com
reference design (Alcatel DSP) */

/* Timeout in jiffies */
#define CTRL_TIMEOUT (2*HZ)
#define DATA_TIMEOUT (2*HZ)

#define ANALOG 0x15
#define ISDN 0x11

static int linetype = 0;

module_param(linetype, uint, 0444);
MODULE_PARM_DESC(linetype, "Set phone line type
code");

#define UDSL_IOCTL_LINE_UP		1
#define UDSL_IOCTL_LINE_DOWN		2

#define AMEDYN_ENDPOINT_DATA_OUT	0x07
#define AMEDYN_ENDPOINT_DATA_IN		0x87
#define AMEDYN_ENDPOINT_FIRMWARE_OUT	0x05

// from the userspace tool
#define AMEDYN_USB_IN_INFO  0x81    // IN endpoint
address, read modem status /
#define AMEDYN_USB_OUT_INFO 0x01    // OUT endpoint
address /
#define AMEDYN_USB_IN_FIRM  0x85    // IN endpoint
address, read config /
#define AMEDYN_USB_OUT_FIRM 0x05    // OUT endpoint
address, send firmware /

#define AMEDYN_VENDOR_REQUEST_OUT 0x40      /* Vendor
specific requests, OUT direction */
#define AMEDYN_VENDOR_REQUEST_IN 0xC0       /* Vendor
specific requets, IN direction */

#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c -
'0') : ((c & 0xf) + 9) )

static struct usb_device_id amedyn_usb_ids[] = {
	{USB_DEVICE(AME_VENDORID2, AME_PRODUCTID2)},
	{USB_DEVICE(AME_VENDORID3, AME_PRODUCTID3)},
	{}
};

MODULE_DEVICE_TABLE(usb, amedyn_usb_ids);

struct amedyn_instance_data {
	struct udsl_instance_data u;

	int datamax;  /* maximum data that we can send in a
block */

	char *initfirmfile;  /* init firmware file name */
	char *firmfile;  /* firmware file name */

	char linetype; /* 0x15 = analog line, 0x11 ISDN line
*/

	unsigned char bufconf[8];  /* buffer to save config
bytes */

	int resynconlost; /* Resync line if it is lost?*/

	/* Status */
	struct work_struct poll_work;
	struct timer_list poll_timer;
};
/* USB */

static int amedyn_usb_probe(struct usb_interface
*intf,
			      const struct usb_device_id *id);
static void amedyn_usb_disconnect(struct usb_interface
*intf);
static int amedyn_usb_ioctl(struct usb_interface
*intf, unsigned int code,
			      void *user_data);
static void amedyn_poll_status(struct
amedyn_instance_data *instance);

static struct usb_driver amedyn_usb_driver = {
	.owner		= THIS_MODULE,
	.name		= amedyn_driver_name,
	.probe		= amedyn_usb_probe,
	.disconnect	= amedyn_usb_disconnect,
	.ioctl		= amedyn_usb_ioctl,
	.id_table	= amedyn_usb_ids,
};

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

static void amedyn_got_firmware(struct
amedyn_instance_data *instance,
				  int got_it)
{
	int err;

	down(&instance->u.serialize);	/* vs self,
amedyn_firmware_start */
	if (instance->u.status == UDSL_LOADED_FIRMWARE)
		goto out;
	if (!got_it) {
		instance->u.status = UDSL_NO_FIRMWARE;
		goto out;
	}
	if ((err = usb_set_interface(instance->u.usb_dev, 1,
1)) < 0) {
		dbg("amedyn_got_firmware: usb_set_interface returned
%d!", err);
		instance->u.status = UDSL_NO_FIRMWARE;
		goto out;
	}

	/* Start status polling */
	mod_timer(&instance->poll_timer, jiffies + (1 * HZ));

	instance->u.status = UDSL_LOADED_FIRMWARE;
	tasklet_schedule(&instance->u.receive_tasklet);
 out:
	up(&instance->u.serialize);
	wake_up_interruptible(&instance->u.firmware_waiters);
}

static int amedyn_line_down_signal (struct
amedyn_instance_data *instance)
{
	struct usb_device *dev = instance->u.usb_dev;
	unsigned char buf[0x1ff];
	int ret;

	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x03, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00,
			      buf, 0x00, CTRL_TIMEOUT);
	if (ret < 0) {
		dbg("amedyn: Failed sync down command: %d\n", ret);
		return ret; }
	dbg("amedyn: OK sync dowm command");

	return 0;
}

static int amedyn_get_status(struct
amedyn_instance_data *instance,
			       unsigned char *buf)
{
	struct usb_device *dev = instance->u.usb_dev;
	int ret;

	memset(buf, 0, 0x10);

	ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
			   buf, 0x10, NULL,  DATA_TIMEOUT);
	if (ret < 0) {
		dbg("Error retrieving info!");
		return ret;
	}

/* If buf isn't line status info or a error code try
again*/
	if ( buf[0]  != 0x01 && ret > 2 
	    && instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND ) {
    	    ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
	    		       buf, 0x10, NULL,  DATA_TIMEOUT);
	    if (ret < 0) {
		    dbg("Error retrieving info!");
		    return ret;
	    }
	}

/* If buf isn't line status info or a error code try
again, again */
	if ( buf[0]  != 0x01 && ret > 2
	    && instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND ) {
    	    ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
	    		       buf, 0x10, NULL,  DATA_TIMEOUT);
	    if (ret < 0) {
		    dbg("Error retrieving info!");
		    return ret;
	    }
	}

/* If buf isn't line status info or a error code try
again, again, again*/
	if ( buf[0]  != 0x01 && ret > 2
	    && instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND ) {
    	    ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
	    		       buf, 0x10, NULL,  DATA_TIMEOUT);
	    if (ret < 0) {
		    dbg("Error retrieving info!");
		    return ret;
	    }
	}

/* If buf isn't line status info or a error code try
again, again, again, again*/
	if ( buf[0] != 0x01 && ret > 2
	    && instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND ) {
    	    ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
	    		       buf, 0x10, NULL,  DATA_TIMEOUT);
	    if (ret < 0) {
		    dbg("Error retrieving info!");
		    return ret;
	    }
	}

/* If buf isn't line status info or a error code try
again, again, again, again, again*/
	if ( buf[0]  != 0x01 && ret > 2
	    && instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND ) {
    	    ret= usb_bulk_msg (dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
	    		       buf, 0x10, NULL,  DATA_TIMEOUT);
	    if (ret < 0) {
		    dbg("Error retrieving info!");
		    return ret;
	    }
	}

	return 0;
}

static int amedyn_start_synchro(struct
amedyn_instance_data *instance)
{
	struct usb_device *dev = instance->u.usb_dev;
	unsigned char buf[0x1ff];   /* buffer */
	int ret, i;

	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x0b, AMEDYN_VENDOR_REQUEST_OUT, 0x0c, 0x00,
			      NULL, 0x00, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 1:
%d\n", __func__, ret);
		return ret; }

	for (i = 0xba; i <= 0xc1; i++) {
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, i,
			      &(instance->bufconf[i-0xba]), 0x01,
CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 2:
%d\n", __func__, ret);
		return ret; }
	}

/* set AFE value, R_Function_Code = 0x15 (adjust
Alcatel DSP for our configuration) */
/* 0x1fd in CTRLE protocol */
/* 0x15 = analog line, 0x11 ISDN line */
	buf[0] = instance->linetype;
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x1fd,
			      buf, 0x01, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 3:
%d\n", __func__, ret);
		return ret; }

	buf[0] = 0x01;
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4a,
			      buf, 0x01, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 4:
%d\n", __func__, ret);
		return ret; }

	buf[0] = 0x00;
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4b,
			      buf, 0x01, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 5:
%d\n", __func__, ret);
		return ret; }

	buf[0] = 0x00;
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x06, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x4c,
			      buf, 0x01, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 6:
%d\n", __func__, ret);
		return ret; }

/* Only this command seem to be necesary to resync. */
	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			      0x02, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00,
			      NULL, 0x00, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 7:
%d\n", __func__, ret);
		return ret; }

	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
			      0x0e, AMEDYN_VENDOR_REQUEST_IN, 0x03, 0x00,
			      buf, 0x0c, CTRL_TIMEOUT);
	if (ret < 0) {
		printk(KERN_WARNING "%s failed on local urb 8:
%d\n", __func__, ret);
		return ret; }

	dbg("amedyn_start_synchro: send sync signals.");
	return 0;
}

static void amedyn_poll_status(struct
amedyn_instance_data *instance)
{
	unsigned char buf[0x10];
	int ret;

	ret = amedyn_get_status(instance, buf);
	if (ret) {
		printk(KERN_WARNING
		       "Zyxel: Error %d fetching device status\n",
ret);
		return;
	}

	if ( buf[0] == 0x02 ) {
		if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST)
{
			instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
			printk(KERN_NOTICE "ADSL line is down\n");
		}
		dbg("Line sync lost?");
		if ( instance->resynconlost == 1 ) {
			amedyn_line_down_signal (instance);
			amedyn_start_synchro(instance);
	    	}
	}

	if ( buf[0] == 0x40 ) {
		if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST)
{
			instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
			printk(KERN_NOTICE "ADSL line is down\n");
		}
		dbg("Line sync faild with code: %02x", buf[1]);
		if ( instance->resynconlost == 1 ) {
			amedyn_line_down_signal (instance);
			amedyn_start_synchro(instance);
	    	}
	}

	if ( buf[0] == 0x01 )
	    dbg("Line state %02x", buf[1]);
	else {
//	    printk(KERN_NOTICE "amedyn_get_status return
useless info\n");
	    return ;
	}

	switch (buf[1]) {
	case 0x00:
		if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST)
{
			instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
			printk(KERN_NOTICE "ADSL line is down\n");
		}
	    	if ( instance->resynconlost == 1 ) {
			amedyn_line_down_signal (instance);
	    		amedyn_start_synchro(instance);
		}
		break;

	case 0x08:
		if (instance->u.atm_dev->signal !=
ATM_PHY_SIG_UNKNOWN) {
			instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
			printk(KERN_NOTICE "ADSL line is blocked?\n");
		}
		break;

	case 0x10:
		if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST)
{
			instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
			printk(KERN_NOTICE "ADSL line is synchronising\n");
		}
		break;

	case 0x20:
		if (instance->u.atm_dev->signal !=
ATM_PHY_SIG_FOUND) {
			instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
			mod_timer(&instance->poll_timer, jiffies + (5 *
HZ));
			printk(KERN_NOTICE "ADSL line is up\n");
		}
		break;

	default:
		if (instance->u.atm_dev->signal !=
ATM_PHY_SIG_UNKNOWN) {
			instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
			printk(KERN_NOTICE "Unknown line state %02x\n",
buf[1]);
		}
		break;
	}
}

static void amedyn_timer_poll(unsigned long data)
{
	struct amedyn_instance_data *instance = (void *)data;

	schedule_work(&instance->poll_work);
	mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
}

#ifdef USE_FW_LOADER
/* From userspace tool */
int jump_to_address(struct amedyn_instance_data
*instance, unsigned int place)
{
	struct usb_device *dev = instance->u.usb_dev;
	unsigned char buf[6];  /* buffer */

	buf[0] = 0x08; // Command (= set base address)
	buf[1] = 0x04; // Length (= 4 bytes)
	// Value (base address = place)
	buf[2] = (place >> 24) & 0xff;
	buf[3] = (place >> 16) & 0xff;
	buf[4] = (place >> 8) & 0xff;
	buf[5] = place & 0xff;
	if (usb_bulk_msg (dev, usb_sndbulkpipe(dev,
AMEDYN_USB_OUT_FIRM), buf, 6, NULL,  DATA_TIMEOUT))
		return -1;
	buf[0] = 0x00;  // Command (= jump?)
	buf[1] = 0x01;  // Length (= 1 byte)
	buf[2] = 0x14;  // Value (= jump to base address)
	if (usb_bulk_msg (dev, usb_sndbulkpipe(dev,
AMEDYN_USB_OUT_FIRM), buf, 3, NULL,  DATA_TIMEOUT))
		return -1;
	return 0;
}

/* From userspace tool */
/* format a message */
void format_message(int cmd, int ldata, int address,
char *bufin)
{
	char buf[8];  /* initial bytes of a message */

	memset(buf, 0, sizeof(buf));
	buf[0] = cmd & 0xff;    /* usb command */
	/* address */
	buf[2] = address & 0xff;
	buf[3] = (address >> 8) & 0xff;
	buf[4] = (address >> 16) & 0xff;
	buf[5] = (address >> 24) & 0xff;
	/* data length */
	buf[6] = ldata & 0xff;
	buf[7] = (ldata >> 8) & 0xff;
	buf[1] = buf[6] + 6;
	memcpy(bufin, buf, sizeof(buf));
}

/* From userspace tool */
int send_block(struct amedyn_instance_data *instance,
int place, char *bufin, int len)
{
	struct usb_device *dev = instance->u.usb_dev;
	char buf[0x1ff];  /* = modem_char.datamax + 8 */

	if ((bufin == NULL) || (len > instance->datamax))
		return -1;
	memset(buf, 0, sizeof(buf));
	format_message(0x88, len, place, buf);
	memcpy(buf+8, bufin, len);

	if (usb_bulk_msg (dev, usb_sndbulkpipe(dev,
AMEDYN_USB_OUT_FIRM), buf, len+8, NULL, 
DATA_TIMEOUT))
		return -1;

	return 0;
}

static void amedyn_upload_firmware(struct
amedyn_instance_data *instance,
				     const struct firmware *fw1,
				     const struct firmware *fw2)
{
	unsigned char *buffer;
	struct usb_device *usb_dev = instance->u.usb_dev;
	struct usb_interface *intf;
	int ret;
	int offset;
	unsigned char buf[0x1ff];
	char value;  /* returned byte */
	int i;

	dbg("amedyn_upload_firmware");

	if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
		dbg("amedyn_upload_firmware: interface not found!");
		goto fail;
	}

	if (!(buffer = (unsigned char
*)__get_free_page(GFP_KERNEL))) {
		dbg("amedyn_upload_firmware: no memory for
buffer!");
		goto fail;
	}

	/* A user-space firmware loader may already have
claimed interface #2 */
	if ((ret =
	     usb_driver_claim_interface(&amedyn_usb_driver,
intf, NULL)) < 0) {
		dbg("amedyn_upload_firmware: interface in use
(%d)!", ret);
		goto fail_free;
	}

	usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev,
AMEDYN_USB_OUT_FIRM));
	usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev,
AMEDYN_USB_IN_FIRM));

	/* Init firmware upload*/
	offset = 0;
	do {
		int thislen = min_t(int, instance->datamax,
fw1->size - offset);
		memcpy(buf, fw1->data + offset, thislen);
		if (send_block(instance, offset, buf, thislen))
			goto fail_release;
		offset+=thislen;
		buf[0] = 0x40; buf[1] = 0x01; buf[2] = 0x12;
		ret = usb_bulk_msg (usb_dev,
usb_sndbulkpipe(usb_dev, AMEDYN_USB_OUT_FIRM), buf, 3,
NULL,  DATA_TIMEOUT);
		if (ret < 0) {
			goto fail_release;
			dbg("amedyn_upload_firmware: write Init firmware to
modem failed (%d)!", ret);
		}
	} while (offset < fw1->size );

	dbg("amedyn_upload_firmware: Init load");

	if (jump_to_address(instance, 0x00000000))
		goto fail_release;

	/* read something needed */
	ret = usb_bulk_msg (usb_dev, usb_rcvbulkpipe(usb_dev,
AMEDYN_USB_IN_FIRM), buf, 0x1ff, NULL,  DATA_TIMEOUT);
	if (ret < 0) {
		goto fail_release;
		dbg("amedyn_upload_firmware: read bufconf failed
(%d)!", ret);
	}
	memcpy(instance->bufconf, buf+0xb9, 8);

	dbg("amedyn_upload_firmware: Read bufconf OK");

	/* Firmware upload */
	offset = 0;
	do {
		int thislen = min_t(int, instance->datamax,
fw2->size - offset);
		memcpy(buf, fw2->data + offset, thislen);
		if (send_block(instance, offset, buf, thislen))
			goto fail_release;
		offset+=thislen;
		buf[0] = 0x40; buf[1] = 0x01; buf[2] = 0x12;
		ret = usb_bulk_msg (usb_dev,
usb_sndbulkpipe(usb_dev, AMEDYN_USB_OUT_FIRM), buf, 3,
NULL,  DATA_TIMEOUT);
		if (ret < 0) {
			dbg("amedyn_upload_firmware: write Init firmware to
modem failed (%d)!", ret);
			goto fail_release;
		}
	} while (offset < fw2->size );

	dbg("amedyn_upload_firmware: Firmware load");

	if (jump_to_address(instance, 0x00000000))
		goto fail_release;

	msleep(2000);

	dbg("PostInit...");

	/* configure something */

	ret = usb_control_msg(usb_dev,
usb_rcvctrlpipe(usb_dev, 0),
			      0x0a, AMEDYN_VENDOR_REQUEST_IN, 0x0c, 0x08,
			      buf, 0x01, CTRL_TIMEOUT);
	if (ret < 1) {
		dbg("amedyn_upload_firmware: PostInit fail at local
urb 1: %d\n", ret);
		goto fail_release; }
	value = buf[0];

	usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev,
AMEDYN_USB_IN_INFO));

	ret = usb_control_msg(usb_dev,
usb_sndctrlpipe(usb_dev, 0),
			      0x40, AMEDYN_VENDOR_REQUEST_OUT, 0x03, 0x00,
			      NULL, 0x00, CTRL_TIMEOUT);
	if (ret < 0) {
		dbg("amedyn_upload_firmware: PostInit fail at local
urb 2: %d\n", ret);
		goto fail_release; }

	for (i = 0xc2; i <= 0xcd; i++) {
		ret = usb_control_msg(usb_dev,
usb_rcvctrlpipe(usb_dev, 0),
			 	      value, AMEDYN_VENDOR_REQUEST_IN, 0x03, i,
			    	      buf, 0x03, CTRL_TIMEOUT);
		if (ret < 3) {
			dbg("amedyn_upload_firmware: PostInit fail at local
urb 3: %d\n", ret);
			goto fail_release; }
    		msleep(100);
   	}

	/* success */
	dbg("amedyn_upload_firmware: firmware upload OK");

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

	/* Start modem synchronisation */
	if (amedyn_start_synchro(instance))
		dbg("amedyn_start_synchro: failed");

	amedyn_got_firmware(instance, 1);

	free_page((unsigned long)buffer);
	return;

 fail_release:
	/* Only release interface #2 if uploading failed; we
don't release it
	   we succeeded.  This prevents the userspace tools
from trying to load
	   the firmware themselves */
	usb_driver_release_interface(&amedyn_usb_driver,
intf);
 fail_free:
	free_page((unsigned long)buffer);
 fail:
	amedyn_got_firmware(instance, 0);
}

static int amedyn_find_firmware(struct
amedyn_instance_data *instance,
				char* phase, const struct firmware **fw_p)
{
	char buf[16];

	sprintf(buf, "%s", phase);
	dbg("amedyn_find_firmware: looking for %s", phase);

	if (request_firmware(fw_p, buf,
&instance->u.usb_dev->dev)) {
		dev_warn(&instance->u.usb_dev->dev, "no stage %s
firmware found\n", phase);
		return -ENOENT;
	}

	dev_info(&instance->u.usb_dev->dev, "found firmware
%s\n", buf);

	return 0;
}

static int amedyn_load_firmware(void *arg)
{
	const struct firmware *fw1, *fw2;
	struct amedyn_instance_data *instance = arg;

	dbg("amedyn_load_firmware");

	BUG_ON(!instance);

	daemonize("firmware/amedyn");

	if (!amedyn_find_firmware(instance,
instance->initfirmfile, &fw1)) {
		if (!amedyn_find_firmware(instance,
instance->firmfile, &fw2)) {
			amedyn_upload_firmware(instance, fw1, fw2);
			release_firmware(fw2);
		}
		release_firmware(fw1);
	}

	/* In case we failed, set state back to NO_FIRMWARE
so that
	   another later attempt may work. Otherwise, we
never actually
	   manage to recover if, for example, the firmware is
on /usr and
	   we look for it too early. */
	amedyn_got_firmware(instance, 0);

	module_put(THIS_MODULE);
	udsl_put_instance(&instance->u);
	return 0;
}
#endif /* USE_FW_LOADER */

static void amedyn_firmware_start(struct
amedyn_instance_data *instance)
{
#ifdef USE_FW_LOADER
	int ret;
#endif

	dbg("amedyn_firmware_start");

	down(&instance->u.serialize);	/* vs self,
amedyn_got_firmware */

	if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
		up(&instance->u.serialize);
		dbg("amedyn_firmware_start: instance->u.status >=
UDSL_LOADING_FIRMWARE");
		return;
	}

	instance->u.status = UDSL_LOADING_FIRMWARE;
	up(&instance->u.serialize);

#ifdef USE_FW_LOADER
	dbg("amedyn_firmware_start: looking for firmware");
	udsl_get_instance(&instance->u);
	dbg("amedyn_firmware_start: looking for firmware.");
	try_module_get(THIS_MODULE);
	dbg("amedyn_firmware_start: looking for firmware..");

	ret = kernel_thread(amedyn_load_firmware, instance,
			    CLONE_FS | CLONE_FILES);

	dbg("amedyn_firmware_start: looking for
firmware...");

	if (ret >= 0)
		return;		/* OK */

	dbg("amedyn_firmware_start: kernel_thread failed
(%d)!", ret);

	module_put(THIS_MODULE);
	udsl_put_instance(&instance->u);
	/* Just pretend it never happened... hope modem_run
happens */
#endif				/* USE_FW_LOADER */

	amedyn_got_firmware(instance, 0);
}

static int amedyn_firmware_wait(struct
udsl_instance_data *instance)
{
	amedyn_firmware_start((void *)instance);

	if
(wait_event_interruptible(instance->firmware_waiters,
instance->status != UDSL_LOADING_FIRMWARE) < 0)
		return -ERESTARTSYS;

	return (instance->status == UDSL_LOADED_FIRMWARE) ? 0
: -EAGAIN;
}

/**********
**  USB  **
**********/

static int amedyn_usb_ioctl(struct usb_interface
*intf, unsigned int code,
			      void *user_data)
{
	struct amedyn_instance_data *instance =
usb_get_intfdata(intf);

	dbg("amedyn_usb_ioctl entered");

	if (!instance) {
		dbg("amedyn_usb_ioctl: NULL instance!");
		return -ENODEV;
	}

	switch (code) {
	case UDSL_IOCTL_LINE_UP:
		dbg("amedyn_usb_ioctl: UDSL_IOCTL_LINE_UP");
		instance->resynconlost = 1;
		amedyn_got_firmware(instance, 1);
		return (instance->u.status == UDSL_LOADED_FIRMWARE)
? 0 : -EIO;
	case UDSL_IOCTL_LINE_DOWN:
		dbg("amedyn_usb_ioctl: UDSL_IOCTL_LINE_DOWN");
		instance->resynconlost = 0;
	    	return amedyn_line_down_signal (instance);
	default:
		return -ENOTTY;
	}
}

static int amedyn_usb_probe(struct usb_interface
*intf, 
			   const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	int ifnum = intf->altsetting->desc.bInterfaceNumber;
	struct amedyn_instance_data *instance;
	unsigned char mac_str[13];
	int i;
	char *buf[0x20];

	int ret;

	dbg("amedyn_usb_probe: trying device with
vendor=0x%x, product=0x%x, ifnum %d",
dev->descriptor.idVendor, dev->descriptor.idProduct,
ifnum);

	if ((dev->descriptor.bDeviceClass !=
USB_CLASS_VENDOR_SPEC)
		|| (ifnum != 1))
		return -ENODEV;

	if ( !((dev->descriptor.idVendor == AME_VENDORID2)
		&& (dev->descriptor.idProduct == AME_PRODUCTID2))
	     && !((dev->descriptor.idVendor == AME_VENDORID3)
		&& (dev->descriptor.idProduct == AME_PRODUCTID3)) )
		return -ENODEV;

	dbg("amedyn_usb_probe: device accepted");

	/* instance init */
	instance = kmalloc(sizeof(*instance), GFP_KERNEL);
	if (!instance) {
		dbg("amedyn_usb_probe: no memory for instance
data!");
		return -ENOMEM;
	}

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

	if ((ret = usb_set_interface(dev, 0, 0)) < 0)
		goto fail;

	if ((ret = usb_set_interface(dev, 2, 0)) < 0)
		goto fail;

	instance->u.data_endpoint = AMEDYN_ENDPOINT_DATA_IN;
	instance->u.firmware_wait = amedyn_firmware_wait;
	instance->u.driver_name = amedyn_driver_name;

	if ( linetype == ANALOG ||  linetype == ISDN)
		instance->linetype = linetype;
	else {
		dbg("using default line type - 0x15 (analog)");
		instance->linetype = ANALOG;
	}

	instance->resynconlost = 1;
	instance->initfirmfile = "Init-usb.bin";
	if (dev->descriptor.idProduct == AME_PRODUCTID2) {
	    dbg("Config for modem type 2 (Zyxel)");
	    instance->datamax = 0x1a0;
	    instance->firmfile = "fw-usb.bin"; }
	if (dev->descriptor.idProduct == AME_PRODUCTID3) {
	    dbg("Config for modem type 3 (AAM600UG)");
	    instance->datamax = 0x1f2;
	    instance->firmfile = "Fw-usb_A.bin"; }


	ret = udsl_instance_setup(dev, &instance->u);
	if (ret)
		goto fail;

	init_timer(&instance->poll_timer);
	instance->poll_timer.function = amedyn_timer_poll;
	instance->poll_timer.data = (unsigned long)instance;

	INIT_WORK(&instance->poll_work, (void
*)amedyn_poll_status, instance);

	/* set MAC address, it is stored in the serial number
*/
	memset(instance->u.atm_dev->esi, 0,
sizeof(instance->u.atm_dev->esi));
	if (usb_string(dev, dev->descriptor.iSerialNumber,
mac_str, sizeof(mac_str)) == 12) {
		for (i = 0; i < 6; i++)
			instance->u.atm_dev->esi[i] =
				(hex2int(mac_str[i * 2]) * 16) +
(hex2int(mac_str[i * 2 + 1]));
	}

	/* First check whether the modem already seems to be
alive */
	ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev,
AMEDYN_USB_IN_INFO),
			   buf, 0x10, NULL,  DATA_TIMEOUT);

	if (ret >= 0) {
		dbg("firmware appears to be already loaded");
		amedyn_got_firmware(instance, 1);
		amedyn_poll_status(instance);
	} else {
		dbg("firmware appears to NOT be already loaded");
		amedyn_firmware_start(instance);
	}

	usb_set_intfdata(intf, instance);

	return 0;

 fail:
	kfree(instance);

	return -ENOMEM;
}

static void amedyn_usb_disconnect(struct usb_interface
*intf)
{
	struct amedyn_instance_data *instance =
usb_get_intfdata(intf);

	dbg("amedyn_usb_disconnect entered");

	if (!instance) {
		dbg("amedyn_usb_disconnect: NULL instance!");
		return;
	}

	del_timer_sync(&instance->poll_timer);
	wmb();
	flush_scheduled_work();

	udsl_instance_disconnect(&instance->u);

	/* clean up */
	usb_set_intfdata(intf, NULL);
	udsl_put_instance(&instance->u);
}

/***********
**  init  **
***********/

static int __init amedyn_usb_init(void)
{
	dbg("amedyn_usb_init: driver version "
DRIVER_VERSION);

#ifdef USE_FW_LOADER
	dbg("amedyn_usb_init: In kernel firmware upload");
#else
	dbg("amedyn_usb_init: NOT in kernel firmware
upload");
#endif

	return usb_register(&amedyn_usb_driver);
}

static void __exit amedyn_usb_cleanup(void)
{
	dbg("amedyn_usb_cleanup entered");

	usb_deregister(&amedyn_usb_driver);
}

module_init(amedyn_usb_init);
module_exit(amedyn_usb_cleanup);

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



	
	
		
______________________________________________ 
Renovamos el Correo Yahoo!: ¡250 MB GRATIS! 
Nuevos servicios, más seguridad 
http://correo.yahoo.es



More information about the Usbatm mailing list