Sorry, I forgot to send the file :-) .


Stavros Markou.

-------- Original Message --------
Subject: Re: [linux-usb-devel] usb_disconnect
Date: Thu, 15 Jan 2004 13:34:43 +0200
From: Stavros Markou <[EMAIL PROTECTED]>
Reply-To: [EMAIL PROTECTED]
To: David Brownell <[EMAIL PROTECTED]>
CC: Alan Stern <[EMAIL PROTECTED]>, [EMAIL PROTECTED]
References: <[EMAIL PROTECTED]> <[EMAIL PROTECTED]> <[EMAIL PROTECTED]>




David Brownell wrote:

Stavros Markou wrote:

Disconnect is called when I unplug the wlan card. In the case of a dataflash device when device_del is called dev->parent is not NULL
but in the case of eeprom device dev->parent is NULL. My opinion is that


Hmm, it might be interesting to compare dump_stack() output
at those two call sites.

Also:  could you briefly summarize how those two different
device types handle their firmware update?  For example,
one type of Atmel WLAN card uses a DFU stage, followed by
some additional commands with a usb_reset_device() in there.

In the case of EEPROM device, device boots with 1 interface which has 0 endpoints.
So, once the device boots driver downloads firmware via a DFU stage to the device.
After firmware is downloaded, driver issues reset to the device (bus reset)
and the device starts running the new firmware which has also 1 interface but with 2 endpoints.
The card works as a network device from that point on. When disconnecting this kind of device leaves the
system hanging.
So the case is that before reset and after reset the device presents to the OS the same device descriptor
but different configurations. In both configurations there is only 1 interface. However before reset
there are no other endpoints other than control endpoint as after reset there are 2 bulk endpoints.
This is the device which cannot work with the kernel version of bus reset function.


In the case of FLASH device, device boots with fw already in the NVM, so no reset is needed by the driver.
This device presents the same device descriptor as EEPROM device and configuration with 1 interface and 2 endpoints.
The card works as a network device. No DFU needed at boot. When disconnecting this kind of device the device
is disconnected without any problem.


Before long, everything up to the reset should work from
probe() routine.  But 2.6.0 won't.

First of all, I want to thank you and Alan for your help. Second for the drivers I am developing and In order to be independent from a kernel 's usb_reset_device (since it doesn't work for the moment and the users of the driver don't want to mess with the kernel too much) I 've put a small portion of
kernel usb code inside a file called kernel_usb_reset.c that I attach it to this mail. Inside this file I 've made some changes to usb_physical_reset_device (__usb_reset_device) and I 've included some functions in order to complete succesfully the reset of the device. Inside the kernel, usb_physical_reset_device stops when it puts down the usb0_address_sem and leaves the system hanging. All seems to be fine after my reset_device the card as network device is fully functional . When I try to hot_unplug the device, and I believe usb_disconnect function is called then, I get a NULL pointer reference that leaves the system
with pending urbs etc.



the use of (ex usb_physical_reset_device) by the one type of device and not the other has nothing to do with the fact that when I unplug those devices I get two distinct results.


Stavros, what patches are you running with?  __usb_reset_device
name suggests there are some.

The only patch I use for the moment is the patch Alan send me for the port number . Is there a patch for usb_disconnect as well ?



I posted a disconnect patch a while back, which included that re-name, and later submitted a tweaked/fixed version in four different segments. If you're using either of those, I hope it's the latter!

Can you send me this patch ?

- Dave





-------------------------------------------------------
This SF.net email is sponsored by: Perforce Software.
Perforce is the Fast Software Configuration Management System offering
advanced branching capabilities and atomic changes on 50+ platforms.
Free Eval! http://www.perforce.com/perforce/loadprog.html
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Stavros Markou.



/***************************************************************************************
        Copyright 2000-2001 ATMEL Corporation.
        
        This file is part of atmel wireless lan drivers.

    Atmel wireless lan drivers 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.

    Atmel wireless lan drivers 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 Atmel wireless lan drivers; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

**************************************************************************************/
#include "vnetusba.h"
#include "hub.h"
#include "hcd.h"
#include "linux/device.h"

#define HUB_RESET_TRIES                 5
#define HUB_LONG_RESET_TIME             200
#define USB_PORT_FEAT_ENABLE            1
#define USB_MAXCONFIG                   8
#define USB_RT_PORT     (USB_TYPE_CLASS | USB_RECIP_OTHER)
#define HUB_RESET_TIMEOUT       500
#define USB_PORT_FEAT_CONNECTION        0
#define USB_PORT_FEAT_ENABLE            1
#define USB_PORT_FEAT_SUSPEND           2
#define USB_PORT_FEAT_OVER_CURRENT      3
#define USB_PORT_FEAT_RESET             4
#define USB_PORT_FEAT_POWER             8
#define USB_PORT_FEAT_LOWSPEED          9
#define USB_PORT_FEAT_HIGHSPEED         10
#define USB_PORT_FEAT_C_CONNECTION      16
#define USB_PORT_FEAT_C_ENABLE          17
#define USB_PORT_FEAT_C_SUSPEND         18
#define USB_PORT_FEAT_C_OVER_CURRENT    19
#define USB_PORT_FEAT_C_RESET           20
#define USB_PORT_FEAT_TEST              21
#define USB_PORT_FEAT_INDICATOR         22
#define USB_PORT_STAT_CONNECTION        0x0001
#define USB_PORT_STAT_ENABLE            0x0002
#define USB_PORT_STAT_SUSPEND           0x0004
#define USB_PORT_STAT_OVERCURRENT       0x0008
#define USB_PORT_STAT_RESET             0x0010
/* bits 5 to 7 are reserved */
#define USB_PORT_STAT_POWER             0x0100
#define USB_PORT_STAT_LOW_SPEED         0x0200
#define USB_PORT_STAT_HIGH_SPEED        0x0400
#define USB_PORT_STAT_TEST              0x0800
#define USB_PORT_STAT_INDICATOR         0x1000
#define USB_PORT_STAT_C_CONNECTION      0x0001
#define USB_PORT_STAT_C_ENABLE          0x0002
#define USB_PORT_STAT_C_SUSPEND         0x0004
#define USB_PORT_STAT_C_OVERCURRENT     0x0008
#define USB_PORT_STAT_C_RESET           0x0010
#define USB_MAXALTSETTING               128
#define USB_MAXENDPOINTS                30
#define USB_MAXINTERFACES               32

#define usb_intf_attr(field, format_string)                             \
static ssize_t                                                          \
show_##field (struct device *dev, char *buf)                            \
{                                                                       \
        struct usb_interface *intf;                                     \
        int alt;                                                        \
                                                                        \
        intf = to_usb_interface (dev);                                  \
        alt = intf->act_altsetting;                                     \
                                                                        \
        return sprintf (buf, format_string, intf->altsetting[alt].desc.field); \
}                                                                       \
static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);

usb_intf_attr (bInterfaceNumber, "%02x\n")
usb_intf_attr (bAlternateSetting, "%2d\n")
usb_intf_attr (bNumEndpoints, "%02x\n")
usb_intf_attr (bInterfaceClass, "%02x\n")
usb_intf_attr (bInterfaceSubClass, "%02x\n")
usb_intf_attr (bInterfaceProtocol, "%02x\n")
usb_intf_attr (iInterface, "%02x\n")

static DECLARE_MUTEX(usb_address0_sem);

static inline struct device *hubdev (struct usb_device *dev)
{
        return &dev->actconfig->interface[0]->dev;
}
static int __clear_port_feature(struct usb_device *dev, int port, int feature)
{
        return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
}

static int __usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char 
*buffer, int size)
{
        unsigned char *buffer0 = buffer;
        struct usb_descriptor_header *header;
        unsigned char *begin;
        int numskipped;

        header = (struct usb_descriptor_header *)buffer;
        if (header->bDescriptorType != USB_DT_ENDPOINT) {
                warn("unexpected descriptor 0x%X, expecting endpoint, 0x%X",
                        header->bDescriptorType, USB_DT_ENDPOINT);
                return -EINVAL;
        }

        if (header->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
                memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
        else if (header->bLength >= USB_DT_ENDPOINT_SIZE)
                memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE);
        else {
                warn("invalid endpoint descriptor");
                return -EINVAL;
        }

        if ((endpoint->desc.bEndpointAddress & ~USB_ENDPOINT_DIR_MASK) >= 16) {
                warn("invalid endpoint address 0x%X",
                    endpoint->desc.bEndpointAddress);
                return -EINVAL;
        }

        le16_to_cpus(&endpoint->desc.wMaxPacketSize);

        buffer += header->bLength;
        size -= header->bLength;

        /* Skip over any Class Specific or Vendor Specific descriptors */
        begin = buffer;
        numskipped = 0;
        while (size >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *)buffer;

                /* If we find another "proper" descriptor then we're done  */
                if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
                    (header->bDescriptorType == USB_DT_INTERFACE))
                        break;

                dbg("skipping descriptor 0x%X", header->bDescriptorType);
                numskipped++;

                buffer += header->bLength;
                size -= header->bLength;
        }
        if (numskipped) {
                dbg("skipped %d class/vendor specific endpoint descriptors", 
numskipped);
                endpoint->extra = begin;
                endpoint->extralen = buffer - begin;
        }

        return buffer - buffer0;
}


static int __usb_parse_interface(struct usb_host_config *config, unsigned char 
*buffer, int size)
{
        unsigned char *buffer0 = buffer;
        struct usb_interface_descriptor *d;
        int inum, asnum;
        struct usb_interface *interface;
        struct usb_host_interface *ifp;
        int len, numskipped;
        struct usb_descriptor_header *header;
        unsigned char *begin;
        int i, retval;

        d = (struct usb_interface_descriptor *) buffer;
        if (d->bDescriptorType != USB_DT_INTERFACE) {
                warn("unexpected descriptor 0x%X, expecting interface, 0x%X",
                        d->bDescriptorType, USB_DT_INTERFACE);
                return -EINVAL;
        }

        inum = d->bInterfaceNumber;
        if (inum >= config->desc.bNumInterfaces) {

                /* Skip to the next interface descriptor */
                buffer += d->bLength;
                size -= d->bLength;
                while (size >= sizeof(struct usb_descriptor_header)) {
                        header = (struct usb_descriptor_header *) buffer;

                        if (header->bDescriptorType == USB_DT_INTERFACE)
                                break;
                        buffer += header->bLength;
                        size -= header->bLength;
                }
                return buffer - buffer0;
        }

        interface = config->interface[inum];
        asnum = d->bAlternateSetting;
        if (asnum >= interface->num_altsetting) {
                warn("invalid alternate setting %d for interface %d",
                    asnum, inum);
                return -EINVAL;
        }

        ifp = &interface->altsetting[asnum];
        if (ifp->desc.bLength) {
                warn("duplicate descriptor for interface %d altsetting %d",
                    inum, asnum);
                return -EINVAL;
        }
        memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE);

        buffer += d->bLength;
        size -= d->bLength;

        /* Skip over any Class Specific or Vendor Specific descriptors */
        begin = buffer;
        numskipped = 0;
        while (size >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *)buffer;

                /* If we find another "proper" descriptor then we're done  */
                if ((header->bDescriptorType == USB_DT_INTERFACE) ||
                    (header->bDescriptorType == USB_DT_ENDPOINT))
                        break;

                dbg("skipping descriptor 0x%X", header->bDescriptorType);
                numskipped++;

                buffer += header->bLength;
                size -= header->bLength;
        }
        if (numskipped) {
                dbg("skipped %d class/vendor specific interface descriptors", 
numskipped);
                ifp->extra = begin;
                ifp->extralen = buffer - begin;
        }

        if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) {
                warn("too many endpoints for interface %d altsetting %d",
                    inum, asnum);
                return -EINVAL;
        }

        len = ifp->desc.bNumEndpoints * sizeof(struct usb_host_endpoint);
        ifp->endpoint = kmalloc(len, GFP_KERNEL);
        if (!ifp->endpoint) {
                err("out of memory");
                return -ENOMEM;
        }
        memset(ifp->endpoint, 0, len);

        for (i = 0; i < ifp->desc.bNumEndpoints; i++) {
                if (size < USB_DT_ENDPOINT_SIZE) {
                        warn("ran out of descriptors while parsing endpoints");
                        return -EINVAL;
                }

                retval = __usb_parse_endpoint(ifp->endpoint + i, buffer, size);
                if (retval < 0)
                        return retval;

                buffer += retval;
                size -= retval;
        }

        return buffer - buffer0;
}


static void __usb_release_intf(struct device *dev)
{
        struct usb_interface *intf;
        int j;

        intf = to_usb_interface(dev);

        if (intf->altsetting) {
                for (j = 0; j < intf->num_altsetting; j++) {
                        struct usb_host_interface *as = &intf->altsetting[j];

                        kfree(as->endpoint);
                }
                kfree(intf->altsetting);
        }
        kfree(intf);
}
int __usb_parse_configuration(struct usb_host_config *config, char *buffer, int size)
{
        int nintf, nintf_orig;
        int i, j;
        struct usb_interface *interface;
        char *buffer2;
        int size2;
        struct usb_descriptor_header *header;
        int numskipped, len;
        char *begin;
        int retval;

        memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
        if (config->desc.bDescriptorType != USB_DT_CONFIG ||
            config->desc.bLength < USB_DT_CONFIG_SIZE) {
                warn("invalid configuration descriptor");
                return -EINVAL;
        }
        config->desc.wTotalLength = size;

        nintf = nintf_orig = config->desc.bNumInterfaces;
        if (nintf > USB_MAXINTERFACES) {
                warn("too many interfaces (%d max %d)",
                    nintf, USB_MAXINTERFACES);
                config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES;
        }

        for (i = 0; i < nintf; ++i) {
                interface = config->interface[i] =
                    kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
                dbg("kmalloc IF %p, numif %i", interface, i);
                if (!interface) {
                        err("out of memory");
                        return -ENOMEM;
                }
                memset(interface, 0, sizeof(struct usb_interface));
                interface->dev.release = __usb_release_intf;
                device_initialize(&interface->dev);
        }

        /* Go through the descriptors, checking their length and counting the
         * number of altsettings for each interface */
        buffer2 = buffer;
        size2 = size;
        j = 0;
        while (size2 >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *) buffer2;
                if ((header->bLength > size2) || (header->bLength < 2)) {
                        warn("invalid descriptor of length %d", header->bLength);
                        return -EINVAL;
                }

                if (header->bDescriptorType == USB_DT_INTERFACE) {
                        struct usb_interface_descriptor *d;

                        if (header->bLength < USB_DT_INTERFACE_SIZE) {
                                warn("invalid interface descriptor");
                                return -EINVAL;
                        }
                        d = (struct usb_interface_descriptor *) header;
                        i = d->bInterfaceNumber;
                        if (i >= nintf_orig) {
                                warn("invalid interface number (%d/%d)",
                                    i, nintf_orig);
                                return -EINVAL;
                        }
                        if (i < nintf)
                                ++config->interface[i]->num_altsetting;

                } else if ((header->bDescriptorType == USB_DT_DEVICE ||
                    header->bDescriptorType == USB_DT_CONFIG) && j) {
                        warn("unexpected descriptor type 0x%X", 
header->bDescriptorType);
                        return -EINVAL;
                }

                j = 1;
                buffer2 += header->bLength;
                size2 -= header->bLength;
        }

        /* Allocate the altsetting arrays */
        for (i = 0; i < config->desc.bNumInterfaces; ++i) {
                interface = config->interface[i];
                if (interface->num_altsetting > USB_MAXALTSETTING) {
                        warn("too many alternate settings for interface %d (%d max 
%d)\n",
                            i, interface->num_altsetting, USB_MAXALTSETTING);
                        return -EINVAL;
                }
                if (interface->num_altsetting == 0) {
                        warn("no alternate settings for interface %d", i);
                        return -EINVAL;
                }

                len = sizeof(*interface->altsetting) * interface->num_altsetting;
                interface->altsetting = kmalloc(len, GFP_KERNEL);
                if (!interface->altsetting) {
                        err("couldn't kmalloc interface->altsetting");
                        return -ENOMEM;
                }
                memset(interface->altsetting, 0, len);
        }

        buffer += config->desc.bLength;
        size -= config->desc.bLength;

        /* Skip over any Class Specific or Vendor Specific descriptors */
        begin = buffer;
        numskipped = 0;
        while (size >= sizeof(struct usb_descriptor_header)) {
                header = (struct usb_descriptor_header *)buffer;

                /* If we find another "proper" descriptor then we're done  */
                if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
                    (header->bDescriptorType == USB_DT_INTERFACE))
                        break;

                dbg("skipping descriptor 0x%X", header->bDescriptorType);
                numskipped++;

                buffer += header->bLength;
                size -= header->bLength;
        }
        if (numskipped) {
                dbg("skipped %d class/vendor specific configuration descriptors", 
numskipped);
                config->extra = begin;
                config->extralen = buffer - begin;
        }

        /* Parse all the interface/altsetting descriptors */
        while (size >= sizeof(struct usb_descriptor_header)) {
                retval = __usb_parse_interface(config, buffer, size);
                if (retval < 0)
                        return retval;

                buffer += retval;
                size -= retval;
        }

        /* Check for missing altsettings */
        for (i = 0; i < nintf; ++i) {
                interface = config->interface[i];
                for (j = 0; j < interface->num_altsetting; ++j) {
                        if (!interface->altsetting[j].desc.bLength) {
                                warn("missing altsetting %d for interface %d", j, i);
                                return -EINVAL;
                        }
                }
        }

        return size;
}


static int __get_port_status(struct usb_device *dev, int port, struct usb_port_status 
*data)
{
        return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
                data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
}
/**
 * __usb_disable_endpoint -- Disable an endpoint by address
 * @dev: the device whose endpoint is being disabled
 * @epaddr: the endpoint's address.  Endpoint number for output,
 *      endpoint number + USB_DIR_IN for input
 *
 * Deallocates hcd/hardware state for this endpoint ... and nukes all
 * pending urbs.
 *
 * If the HCD hasn't registered a disable() function, this marks the
 * endpoint as halted and sets its maxpacket size to 0 to prevent
 * further submissions.
 */
void __usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
{
        if (dev && dev->bus && dev->bus->op && dev->bus->op->disable)
                dev->bus->op->disable(dev, epaddr);
        else {
                unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;

                if (usb_endpoint_out(epaddr)) {
                        usb_endpoint_halt(dev, epnum, 1);
                        dev->epmaxpacketout[epnum] = 0;
                } else {
                        usb_endpoint_halt(dev, epnum, 0);
                        dev->epmaxpacketin[epnum] = 0;
                }
        }
}

/*
 * __usb_disable_device - Disable all the endpoints for a USB device
 * @dev: the device whose endpoints are being disabled
 * @skip_ep0: 0 to disable endpoint 0, 1 to skip it.
 *
 * Disables all the device's endpoints, potentially including endpoint 0.
 * Deallocates hcd/hardware state for the endpoints (nuking all or most
 * pending urbs) and usbcore state for the interfaces, so that usbcore
 * must usb_set_configuration() before any interfaces could be used.
 */
void __usb_disable_device(struct usb_device *dev, int skip_ep0)
{
        int i;

        dev_dbg(&dev->dev, "%s nuking %s URBs\n", __FUNCTION__,
                        skip_ep0 ? "non-ep0" : "all");
        for (i = skip_ep0; i < 16; ++i) {
                __usb_disable_endpoint(dev, i);
                __usb_disable_endpoint(dev, i + USB_DIR_IN);
        }
        dev->toggle[0] = dev->toggle[1] = 0;
        dev->halted[0] = dev->halted[1] = 0;

        /* getting rid of interfaces will disconnect
         * any drivers bound to them (a key side effect)
         */
        if (dev->actconfig) {
                for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
                        struct usb_interface    *interface;

                        /* remove this interface */
                        interface = dev->actconfig->interface[i];
                        dev_dbg (&dev->dev, "unregistering interface %s\n",
                                interface->dev.bus_id);
                        device_del(&interface->dev);
                }
                dev->actconfig = 0;
                if (dev->state == USB_STATE_CONFIGURED)
                        dev->state = USB_STATE_ADDRESS;
        }
}
void __usb_create_driverfs_intf_files (struct usb_interface *intf)
{
        device_create_file (&intf->dev, &dev_attr_bInterfaceNumber);
        device_create_file (&intf->dev, &dev_attr_bAlternateSetting);
        device_create_file (&intf->dev, &dev_attr_bNumEndpoints);
        device_create_file (&intf->dev, &dev_attr_bInterfaceClass);
        device_create_file (&intf->dev, &dev_attr_bInterfaceSubClass);
        device_create_file (&intf->dev, &dev_attr_bInterfaceProtocol);
        device_create_file (&intf->dev, &dev_attr_iInterface);
}

static int __hub_port_status(struct usb_device *dev, int port, UINT *status, UINT 
*change)
{
        struct usb_hub *hub = usb_get_intfdata(dev->actconfig->interface[0]);
        int ret;

        ret = __get_port_status(dev, port + 1, &hub->status->port);
        if (ret < 0)
                dev_err (hubdev (dev),
                        "%s failed (err = %d)\n", __FUNCTION__, ret);
        else {
                *status = le16_to_cpu(hub->status->port.wPortStatus);
                *change = le16_to_cpu(hub->status->port.wPortChange); 
                ret = 0;
        }
        return ret;
}
/*
 * __usb_enable_endpoint - Enable an endpoint for USB communications
 * @dev: the device whose interface is being enabled
 * @epd: pointer to the endpoint descriptor
 *
 * Marks the endpoint as running, resets its toggle, and stores
 * its maxpacket value.  For control endpoints, both the input
 * and output sides are handled.
 */
void __usb_enable_endpoint(struct usb_device *dev,
                struct usb_endpoint_descriptor *epd)
{
        int maxsize = epd->wMaxPacketSize;
        unsigned int epaddr = epd->bEndpointAddress;
        unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
        int is_control = ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
                                USB_ENDPOINT_XFER_CONTROL);

        if (usb_endpoint_out(epaddr) || is_control) {
                usb_endpoint_running(dev, epnum, 1);
                usb_settoggle(dev, epnum, 1, 0);
                dev->epmaxpacketout[epnum] = maxsize;
        }
        if (!usb_endpoint_out(epaddr) || is_control) {
                usb_endpoint_running(dev, epnum, 0);
                usb_settoggle(dev, epnum, 0, 0);
                dev->epmaxpacketin[epnum] = maxsize;
        }
}

/*
 * __usb_enable_interface - Enable all the endpoints for an interface
 * @dev: the device whose interface is being enabled
 * @intf: pointer to the interface descriptor
 *
 * Enables all the endpoints for the interface's current altsetting.
 */
void __usb_enable_interface(struct usb_device *dev,
                struct usb_interface *intf)
{
        struct usb_host_interface *hintf =
                        &intf->altsetting[intf->act_altsetting];
        int i;

        for (i = 0; i < hintf->desc.bNumEndpoints; ++i)
                __usb_enable_endpoint(dev, &hintf->endpoint[i].desc);
}

static int __set_port_feature(struct usb_device *dev, int port, int feature)
{
        return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
}
/* return: -1 on error, 0 on success, 1 on disconnect.  */
static int __hub_port_wait_reset(struct usb_device *hub, int port,
                                struct usb_device *dev, unsigned int delay)
{
        int delay_time, ret;
        UINT portstatus;
        UINT portchange;

        for (delay_time = 0;
                        delay_time < HUB_RESET_TIMEOUT;
                        delay_time += delay) {
                /* wait to give the device a chance to reset */
                wait_ms(delay);

                /* read and decode port status */
                ret = __hub_port_status(hub, port, &portstatus, &portchange);
                if (ret < 0) {
                        return -1;
                }

                /* Device went away? */
                if (!(portstatus & USB_PORT_STAT_CONNECTION))
                        return 1;

                /* bomb out completely if something weird happened */
                if ((portchange & USB_PORT_STAT_C_CONNECTION))
                        return -1;

                /* if we`ve finished resetting, then break out of the loop */
                if (!(portstatus & USB_PORT_STAT_RESET) &&
                    (portstatus & USB_PORT_STAT_ENABLE)) {
                        if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                                dev->speed = USB_SPEED_HIGH;
                        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                                dev->speed = USB_SPEED_LOW;
                        else
                                dev->speed = USB_SPEED_FULL;
                        return 0;
                }

                /* switch to the long delay after two short delay failures */
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;

                dev_dbg (hubdev (hub),
                        "port %d not reset yet, waiting %dms\n",
                        port + 1, delay);
        }

        return -1;
}

/* return: -1 on error, 0 on success, 1 on disconnect.  */
static int __hub_port_reset(struct usb_device *hub, int port,
                                struct usb_device *dev, unsigned int delay)
{
        int i, status;

        /* Reset the port */
        for (i = 0; i < HUB_RESET_TRIES; i++) {
                __set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);

                /* return on disconnect or reset */
                status = __hub_port_wait_reset(hub, port, dev, delay);
                if (status != -1) {
                        __clear_port_feature(hub,
                                port + 1, USB_PORT_FEAT_C_RESET);
                        dev->state = status
                                        ? USB_STATE_NOTATTACHED
                                        : USB_STATE_DEFAULT;
                        return status;
                }

                dev_dbg (hubdev (hub),
                        "port %d not enabled, trying reset again...\n",
                        port + 1);
                delay = HUB_LONG_RESET_TIME;
        }

        dev_err (hubdev (hub),
                "Cannot enable port %i.  Maybe the USB cable is bad?\n",
                port + 1);

        return -1;
}

int __hub_port_disable(struct usb_device *hub, int port)
{
        int ret;

        ret = __clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
        if (ret)
                dev_err(hubdev(hub), "cannot disable port %d (err = %d)\n",
                        port + 1, ret);

        return ret;
}
// hub-only!! ... and only exported for reset/reinit path.
// otherwise used internally, for usb_new_device()
int __usb_set_address(struct usb_device *dev)
{
        int retval;

        if (dev->devnum == 0)
                return -EINVAL;
        if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)
                return -EINVAL;
        retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
                0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
        if (retval == 0)
                dev->state = USB_STATE_ADDRESS;
        return retval;
}
/**
 * usb_set_configuration - Makes a particular device setting be current
 * @dev: the device whose configuration is being updated
 * @configuration: the configuration being chosen.
 * Context: !in_interrupt ()
 *
 * This is used to enable non-default device modes.  Not all devices
 * use this kind of configurability; many devices only have one
 * configuration.
 *
 * USB device configurations may affect Linux interoperability,
 * power consumption and the functionality available.  For example,
 * the default configuration is limited to using 100mA of bus power,
 * so that when certain device functionality requires more power,
 * and the device is bus powered, that functionality should be in some
 * non-default device configuration.  Other device modes may also be
 * reflected as configuration options, such as whether two ISDN
 * channels are available independently; and choosing between open
 * standard device protocols (like CDC) or proprietary ones.
 *
 * Note that USB has an additional level of device configurability,
 * associated with interfaces.  That configurability is accessed using
 * usb_set_interface().
 *
 * This call is synchronous. The calling context must be able to sleep,
 * and must not hold the driver model lock for USB; usb device driver
 * probe() methods may not use this routine.
 *
 * Returns zero on success, or else the status code returned by the
 * underlying call that failed.  On succesful completion, each interface
 * in the original device configuration has been destroyed, and each one
 * in the new configuration has been probed by all relevant usb device
 * drivers currently known to the kernel.
 */
int __usb_set_configuration(struct usb_device *dev, int configuration)
{
        int i, ret;
        struct usb_host_config *cp = NULL;
        
        /* dev->serialize guards all config changes */
        down(&dev->serialize);

        for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
                if (dev->config[i].desc.bConfigurationValue == configuration) {
                        cp = &dev->config[i];
                        break;
                }
        }
        if ((!cp && configuration != 0)) {
                ret = -EINVAL;
                goto out;
        }
        if (cp && configuration == 0)
                dev_warn(&dev->dev, "config 0 descriptor??\n");

        /* if it's already configured, clear out old state first.
         * getting rid of old interfaces means unbinding their drivers.
         */
        if (dev->state != USB_STATE_ADDRESS)
                __usb_disable_device (dev, 1);  // Skip ep0

        if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
                        NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
                goto out;
                return ret;

        dev->actconfig = cp;
        if (!configuration)
                dev->state = USB_STATE_ADDRESS;
        else {
                dev->state = USB_STATE_CONFIGURED;

                /* re-initialize hc/hcd/usbcore interface/endpoint state.
                 * this triggers binding of drivers to interfaces; and
                 * maybe probe() calls will choose different altsettings.
                 */
                for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
                        struct usb_interface *intf = cp->interface[i];
                        struct usb_interface_descriptor *desc;

                        intf->act_altsetting = 0;
                        desc = &intf->altsetting [0].desc;
                        __usb_enable_interface(dev, intf);

                        intf->dev.parent = &dev->dev;
                        intf->dev.driver = NULL;
                        //intf->dev.bus = &usb_bus_type;
                        intf->dev.dma_mask = dev->dev.dma_mask;
                        sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
                                 dev->bus->busnum, dev->devpath,
                                 configuration,
                                 desc->bInterfaceNumber);
                        dev_dbg (&dev->dev,
                                "registering %s (config #%d, interface %d)\n",
                                intf->dev.bus_id, configuration,
                                desc->bInterfaceNumber);
                        device_add (&intf->dev);
                        __usb_create_driverfs_intf_files (intf);
                }
        }

out:
        up(&dev->serialize);
        return ret;
}

// hub-only!! ... and only exported for reset/reinit path.
// otherwise used internally on disconnect/destroy path
void __usb_destroy_configuration(struct usb_device *dev)
{
        int c, i;

        if (!dev->config)
                return;

        if (dev->rawdescriptors) {
                for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
                        kfree(dev->rawdescriptors[i]);

                kfree(dev->rawdescriptors);
        }

        for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
                struct usb_host_config *cf = &dev->config[c];

                for (i = 0; i < cf->desc.bNumInterfaces; i++) {
                        struct usb_interface *ifp = cf->interface[i];

                        if (ifp)
                                put_device(&ifp->dev);
                }
        }
        kfree(dev->config);
}


// hub-only!! ... and only in reset path, or usb_new_device()
// (used by real hubs and virtual root hubs)
int __usb_get_configuration(struct usb_device *dev)
{
        int ncfg = dev->descriptor.bNumConfigurations;
        int result;
        unsigned int cfgno, length;
        unsigned char *buffer;
        unsigned char *bigbuffer;
        struct usb_config_descriptor *desc;

        if (ncfg > USB_MAXCONFIG) {
                warn("too many configurations (%d max %d)",
                    ncfg, USB_MAXCONFIG);
                dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
        }

        if (ncfg < 1) {
                warn("no configurations");
                return -EINVAL;
        }

        length = ncfg * sizeof(struct usb_host_config);
        dev->config = kmalloc(length, GFP_KERNEL);
        if (!dev->config) {
                err("out of memory");
                return -ENOMEM;
        }
        memset(dev->config, 0, length);

        length = ncfg * sizeof(char *);
        dev->rawdescriptors = kmalloc(length, GFP_KERNEL);
        if (!dev->rawdescriptors) {
                err("out of memory");
                return -ENOMEM;
        }
        memset(dev->rawdescriptors, 0, length);

        buffer = kmalloc(8, GFP_KERNEL);
        if (!buffer) {
                err("unable to allocate memory for configuration descriptors");
                return -ENOMEM;
        }
        desc = (struct usb_config_descriptor *)buffer;

        for (cfgno = 0; cfgno < ncfg; cfgno++) {
                /* We grab the first 8 bytes so we know how long the whole */
                /*  configuration is */
                result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
                if (result < 8) {
                        if (result < 0)
                                err("unable to get descriptor");
                        else {
                                warn("config descriptor too short (expected %i, got 
%i)", 8, result);
                                result = -EINVAL;
                        }
                        goto err;
                }

                /* Get the full buffer */
                length = max((int) le16_to_cpu(desc->wTotalLength), 
USB_DT_CONFIG_SIZE);

                bigbuffer = kmalloc(length, GFP_KERNEL);
                if (!bigbuffer) {
                        err("unable to allocate memory for configuration descriptors");
                        result = -ENOMEM;
                        goto err;
                }

                /* Now that we know the length, get the whole thing */
                result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, 
length);
                if (result < 0) {
                        err("couldn't get all of config descriptors");
                        kfree(bigbuffer);
                        goto err;
                }

                if (result < length) {
                        err("config descriptor too short (expected %i, got %i)", 
length, result);
                        result = -EINVAL;
                        kfree(bigbuffer);
                        goto err;
                }

                dev->rawdescriptors[cfgno] = bigbuffer;

                result = __usb_parse_configuration(&dev->config[cfgno], bigbuffer, 
length);
                if (result > 0)
                        dbg("descriptor data left");
                else if (result < 0) {
                        ++cfgno;
                        goto err;
                }
        }

        kfree(buffer);
        return 0;
err:
        kfree(buffer);
        dev->descriptor.bNumConfigurations = cfgno;
        return result;
}


/*
 * WARNING - If a driver calls usb_reset_device, you should simulate a
 * disconnect() and probe() for other interfaces you doesn't claim. This
 * is left up to the driver writer right now. This insures other drivers
 * have a chance to re-setup their interface.
 *
 * Take a look at proc_resetdevice in devio.c for some sample code to
 * do this.
 * Use this only from within your probe function, otherwise use
 * __usb_reset_device() below, which ensure proper locking
 */
int __usb_reset_device(struct usb_device *dev)
{
        struct usb_device *parent = dev->parent;
        struct usb_device_descriptor *descriptor;
        int i, ret, port = -1, new_conf=0;

        printk(__FILE__ ": " "Attempting to Reset the Device\n");
        if (!parent) {
                err("attempting to reset root hub!");
                return -EINVAL;
        }

        for (i = 0; i < parent->maxchild; i++){
                if (parent->children[i] == dev) {
                        printk(__FILE__ ": " "The Port of Atmel Device is : %d\n", i);
                        port = i;
                        break;
                }
        }

        if (port < 0) {
                        printk(__FILE__ ": " "The Port of Atmel Device was not found : 
%d\n", port);
                return -ENOENT;
        }

        descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
        if (!descriptor) {
                return -ENOMEM;
        }

        down(&usb_address0_sem);

        /* Send a reset to the device */
        if (__hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) {
                err("smarkou: attempting to reset root hub!");
                __hub_port_disable(parent, port);
                up(&usb_address0_sem);
                kfree(descriptor);
                return(-ENODEV);
        }

        dev->state = USB_STATE_DEFAULT;
        /* Reprogram the Address */
        ret = __usb_set_address(dev);
        if (ret < 0) {
                err("USB device not accepting new address (error=%d)", ret);
                __hub_port_disable(parent, port);
                up(&usb_address0_sem);
                kfree(descriptor);
                return ret;
        }

        /* Let the SET_ADDRESS settle */
        wait_ms(10);

        up(&usb_address0_sem);

        /*
         * Now we fetch the configuration descriptors for the device and
         * see if anything has changed. If it has, we dump the current
         * parsed descriptors and reparse from scratch. Then we leave
         * the device alone for the caller to finish setting up.
         *
         * If nothing changed, we reprogram the configuration and then
         * the alternate settings.
         */

        ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
                        sizeof(*descriptor));
        if (ret < 0) {
                info("smarkou : Getting the Descriptor Failed");
                kfree(descriptor);
                return ret;
        }

        le16_to_cpus(&descriptor->bcdUSB);
        le16_to_cpus(&descriptor->idVendor);
        le16_to_cpus(&descriptor->idProduct);
        le16_to_cpus(&descriptor->bcdDevice);

                if (memcmp(&dev->descriptor, descriptor, sizeof(*descriptor))) {
                    new_conf = 1; //flag defined for us to know the descriptors 
matching.
                }
                kfree(descriptor);
                __usb_destroy_configuration(dev);

                ret = usb_get_device_descriptor(dev);
                if (ret < sizeof(dev->descriptor)) {
                        if (ret < 0)
                                err("unable to get device %s descriptor "
                                        "(error=%d)", dev->devpath, ret);
                        else
                                err("USB device %s descriptor short read "
                                        "(expected %Zi, got %i)",
                                        dev->devpath,
                                        sizeof(dev->descriptor), ret);

                        clear_bit(dev->devnum, dev->bus->devmap.devicemap);
                        dev->devnum = -1;
                        return -EIO;
                }

                ret = __usb_get_configuration(dev);
                if (ret < 0) {
                        err("unable to get configuration (error=%d)", ret);
                        __usb_destroy_configuration(dev);
                        clear_bit(dev->devnum, dev->bus->devmap.devicemap);
                        dev->devnum = -1;
                        return 1;
                }

                dev->actconfig = dev->config;
                
                if(new_conf){
                    info("smarkou : FLAG Enabled");
                    return 1;
                }

        ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        USB_REQ_SET_CONFIGURATION, 0,
                        dev->actconfig->desc.bConfigurationValue, 0,
                        NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
        if (ret < 0) {
                err("failed to set dev %s active configuration (error=%d)",
                        dev->devpath, ret);
                return ret;
        }

        dev->state = USB_STATE_CONFIGURED;

        for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
                struct usb_interface *intf = dev->actconfig->interface[i];
                struct usb_interface_descriptor *as;

                as = &intf->altsetting[intf->act_altsetting].desc;
                ret = usb_set_interface(dev, as->bInterfaceNumber,
                        as->bAlternateSetting);
                        printk(KERN_ALERT "smarcy : Set active alternate setting "
                                "for dev %s interface %d\n",
                                dev->devpath, i);
                if (ret < 0) {
                        err("failed to set active alternate setting "
                                "for dev %s interface %d (error=%d)",
                                dev->devpath, i, ret);
                        return ret;
                }
        }
        if(dev->parent){
            printk(KERN_INFO __FILE__ ": " "Reset Completed Succesfully !\n");
        } else {
            printk(KERN_INFO __FILE__ ": " "Something Wicked Happened !\n");
        }
            


        return 0;
}

Reply via email to