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:[EMAIL PROTECTED]:/cvsroot/zyxel630-11
login
- cvs -z3
-d:pserver:[EMAIL PROTECTED]:/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


-------------------------------------------------------
The SF.Net email is sponsored by: Beat the post-holiday blues
Get a FREE limited edition SourceForge.net t-shirt from ThinkGeek.
It's fun and FREE -- well, almost....http://www.thinkgeek.com/sfshirt
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to