Nope, the HCD's do that. This patch looks good, but could use a little bit of cleaning up.
JE On Fri, Jun 07, 2002, Christopher Hoover <[EMAIL PROTECTED]> wrote: > Shouldn't we use pci_alloc_consistent (or indirectly via pci_pool_alloc) > instead of kmalloc? > > -ch > > > -----Original Message----- > > From: [EMAIL PROTECTED] > > [mailto:[EMAIL PROTECTED]] On > > Behalf Of Roland Dreier > > Sent: Friday, June 07, 2002 4:57 PM > > To: [EMAIL PROTECTED]; > > [EMAIL PROTECTED] > > Subject: [linux-usb-devel] USB on PPC440GP (cache incoherent) > > Importance: High > > > > > > I just got USB working on my PPC440GP (IBM Ebony eval board). > > I'm using an Opti 82C861 OHCI controller, and so far I've > > tried an Alcor SD card reader (mass storage) and a Belkin > > usbnet device. My kernel is 2.4.19-pre10 from the > > bk://ppc.bkbits.net/linuxppc_2_4_devel > > BitKeeper tree. > > > > I had to make some changes to the USB driver to get this > > working as there are still some places where structures on > > the stack are being used for DMA. This causes problems on > > processors like the 440GP, which are not cache coherent (you > > can only invalidate a whole cache line, which can corrupt the > > stack). I just changed all of these places to use kmalloc to > > get memory instead. > > > > Note that this might not work perfectly on all > > cache-incoherent processors, since kmalloc could potentially > > allocate a chunk of memory that is smaller than the > > processor's cache line size. However it is safe on the 440GP > > since the 440GP's cache line size is 32 bytes. > > > > I've included a patch with my changes in this email. I'm > > sending it to the linuxppc-embedded list in case someone else > > is trying to do USB on a 440 or 405 or similar processor. > > I'm also sending it to linux-usb-devel in the hope that these > > changes make it into the mainline kernel. > > > > Best, > > Roland > > > > # This is a BitKeeper generated patch for the following > > project: # Project Name: Linux 2.4 for PowerPC development > > tree # This patch format is intended for GNU patch command > > version 2.5 or higher. # This patch includes the following deltas: > > # ChangeSet 1.1077 -> 1.1078 > > # drivers/usb/usb.c 1.19 -> 1.20 > > # drivers/usb/hub.c 1.14 -> 1.15 > > # drivers/usb/hub.h 1.7 -> 1.8 > > # drivers/usb/storage/transport.c 1.10 -> 1.11 > > # > > # The following is the BitKeeper ChangeSet Log > > # -------------------------------------------- > > # 02/06/07 [EMAIL PROTECTED] 1.1078 > > # Fix problems in USB when running on cache-incoherent cpus > > like PPC440GP # (Don't do DMAs into memory allocated on > > stack) # -------------------------------------------- > > # > > diff -Nru a/drivers/usb/hub.c b/drivers/usb/hub.c > > --- a/drivers/usb/hub.c Fri Jun 7 16:35:31 2002 > > +++ b/drivers/usb/hub.c Fri Jun 7 16:35:31 2002 > > @@ -155,7 +155,7 @@ > > static int usb_hub_configure(struct usb_hub *hub, struct > > usb_endpoint_descriptor *endpoint) { > > struct usb_device *dev = hub->dev; > > - struct usb_hub_status hubstatus; > > + struct usb_hub_status *hubstatus; > > char portstr[USB_MAXCHILDREN + 1]; > > unsigned int pipe; > > int i, maxp, ret; > > @@ -258,27 +258,36 @@ > > > > dbg("port removable status: %s", portstr); > > > > - ret = usb_get_hub_status(dev, &hubstatus); > > + hubstatus = kmalloc(sizeof *hubstatus, GFP_KERNEL); > > + if (!hubstatus) { > > + err("Unable to allocate hubstatus"); > > + kfree(hub->descriptor); > > + return -1; > > + } > > + ret = usb_get_hub_status(dev, hubstatus); > > if (ret < 0) { > > err("Unable to get hub status (err = %d)", ret); > > + kfree(hubstatus); > > kfree(hub->descriptor); > > return -1; > > } > > > > - le16_to_cpus(&hubstatus.wHubStatus); > > + le16_to_cpus(&hubstatus->wHubStatus); > > > > dbg("local power source is %s", > > - (hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER) > > ? "lost (inactive)" : "good"); > > + (hubstatus->wHubStatus & > > HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" > > +: "good"); > > > > dbg("%sover-current condition exists", > > - (hubstatus.wHubStatus & HUB_STATUS_OVERCURRENT) > > ? "" : "no "); > > + (hubstatus->wHubStatus & > > HUB_STATUS_OVERCURRENT) ? "" : "no "); > > + > > + kfree(hubstatus); > > > > /* Start the interrupt endpoint */ > > pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); > > maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); > > > > - if (maxp > sizeof(hub->buffer)) > > - maxp = sizeof(hub->buffer); > > + if (maxp > USB_HUB_BUFFER_SIZE) > > + maxp = USB_HUB_BUFFER_SIZE; > > > > hub->urb = usb_alloc_urb(0); > > if (!hub->urb) { > > @@ -357,6 +366,13 @@ > > > > memset(hub, 0, sizeof(*hub)); > > > > + hub->buffer = kmalloc(USB_HUB_BUFFER_SIZE, GFP_KERNEL); > > + if (!hub->buffer) { > > + err("couldn't kmalloc hub->buffer"); > > + kfree(hub); > > + return NULL; > > + } > > + > > INIT_LIST_HEAD(&hub->event_list); > > hub->dev = dev; > > init_MUTEX(&hub->khubd_sem); > > @@ -383,6 +399,7 @@ > > > > spin_unlock_irqrestore(&hub_event_lock, flags); > > > > + kfree(hub->buffer); > > kfree(hub); > > > > return NULL; > > @@ -417,6 +434,11 @@ > > hub->descriptor = NULL; > > } > > > > + if (hub->buffer) { > > + kfree(hub->buffer); > > + hub->buffer = NULL; > > + } > > + > > /* Free the memory */ > > kfree(hub); > > } > > @@ -782,7 +804,7 @@ > > struct list_head *tmp; > > struct usb_device *dev; > > struct usb_hub *hub; > > - struct usb_hub_status hubsts; > > + struct usb_hub_status *hubsts; > > u16 hubstatus; > > u16 hubchange; > > u16 portstatus; > > @@ -872,22 +894,28 @@ > > } /* end for i */ > > > > /* deal with hub status changes */ > > - if (usb_get_hub_status(dev, &hubsts) < 0) > > - err("get_hub_status failed"); > > - else { > > - hubstatus = le16_to_cpup(&hubsts.wHubStatus); > > - hubchange = le16_to_cpup(&hubsts.wHubChange); > > - if (hubchange & HUB_CHANGE_LOCAL_POWER) { > > - dbg("hub power change"); > > - usb_clear_hub_feature(dev, > > C_HUB_LOCAL_POWER); > > - } > > - if (hubchange & HUB_CHANGE_OVERCURRENT) { > > - dbg("hub overcurrent change"); > > - wait_ms(500); /* Cool down */ > > - usb_clear_hub_feature(dev, > > C_HUB_OVER_CURRENT); > > - usb_hub_power_on(hub); > > - } > > + hubsts = kmalloc(sizeof *hubsts, GFP_KERNEL); > > + if (!hubsts) { > > + err("couldn't allocate hubsts"); > > + } else { > > + if (usb_get_hub_status(dev, hubsts) < 0) > > + err("get_hub_status failed"); > > + else { > > + hubstatus = > > le16_to_cpup(&hubsts->wHubStatus); > > + hubchange = > > le16_to_cpup(&hubsts->wHubChange); > > + if (hubchange & > > HUB_CHANGE_LOCAL_POWER) { > > + dbg("hub power change"); > > + > > usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER); > > + } > > + if (hubchange & > > HUB_CHANGE_OVERCURRENT) { > > + dbg("hub overcurrent > > change"); > > + wait_ms(500); > > /* Cool down */ > > + > > usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); > > + usb_hub_power_on(hub); > > + } > > + } > > } > > + kfree(hubsts); > > up(&hub->khubd_sem); > > } /* end while (1) */ > > > > @@ -995,7 +1023,7 @@ > > int usb_reset_device(struct usb_device *dev) > > { > > struct usb_device *parent = dev->parent; > > - struct usb_device_descriptor descriptor; > > + struct usb_device_descriptor *descriptor; > > int i, ret, port = -1; > > > > if (!parent) { > > @@ -1044,17 +1072,19 @@ > > * If nothing changed, we reprogram the configuration and then > > * the alternate settings. > > */ > > - ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor, > > - sizeof(descriptor)); > > + descriptor = kmalloc(sizeof *descriptor, GFP_KERNEL); > > + ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor, > > + sizeof *descriptor); > > if (ret < 0) > > return ret; > > > > - le16_to_cpus(&descriptor.bcdUSB); > > - le16_to_cpus(&descriptor.idVendor); > > - le16_to_cpus(&descriptor.idProduct); > > - le16_to_cpus(&descriptor.bcdDevice); > > + 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))) { > > + if (memcmp(&dev->descriptor, descriptor, sizeof *descriptor)) { > > + kfree(descriptor); > > usb_destroy_configuration(dev); > > > > ret = usb_get_device_descriptor(dev); > > @@ -1083,6 +1113,7 @@ > > > > return 1; > > } > > + kfree(descriptor); > > > > ret = usb_set_configuration(dev, > > dev->actconfig->bConfigurationValue); > > if (ret < 0) { > > diff -Nru a/drivers/usb/hub.h b/drivers/usb/hub.h > > --- a/drivers/usb/hub.h Fri Jun 7 16:35:31 2002 > > +++ b/drivers/usb/hub.h Fri Jun 7 16:35:31 2002 > > @@ -120,13 +120,15 @@ > > > > struct usb_device; > > > > +/* add 1 bit for hub status change and add 7 bits to round > > up to byte > > +boundary */ #define USB_HUB_BUFFER_SIZE ((USB_MAXCHILDREN + > > 1 + 7) / 8) > > + > > struct usb_hub { > > struct usb_device *dev; > > > > struct urb *urb; /* Interrupt polling pipe */ > > > > - char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 > > bit for hub status change */ > > - /* and add 7 bits to > > round up to byte boundary */ > > + char *buffer; > > int error; > > int nerrors; > > > > diff -Nru a/drivers/usb/storage/transport.c > > b/drivers/usb/storage/transport.c > > --- a/drivers/usb/storage/transport.c Fri Jun 7 16:35:31 2002 > > +++ b/drivers/usb/storage/transport.c Fri Jun 7 16:35:31 2002 > > @@ -723,7 +723,7 @@ > > > > /* use the new buffer we have */ > > old_request_buffer = srb->request_buffer; > > - srb->request_buffer = srb->sense_buffer; > > + srb->request_buffer = kmalloc(18, > > in_interrupt() ? GFP_ATOMIC : > > +GFP_KERNEL); > > > > /* set the buffer length for transfer */ > > old_request_bufflen = srb->request_bufflen; > > @@ -733,8 +733,14 @@ > > old_sg = srb->use_sg; > > srb->use_sg = 0; > > > > - /* issue the auto-sense command */ > > - temp_result = us->transport(us->srb, us); > > + if (srb->request_buffer) { > > + /* issue the auto-sense command */ > > + temp_result = us->transport(us->srb, us); > > + memcpy(srb->sense_buffer, > > srb->request_buffer, 18); > > + kfree(srb->request_buffer); > > + } else { > > + temp_result = USB_STOR_TRANSPORT_ERROR; > > + } > > > > /* let's clean up right away */ > > srb->request_buffer = old_request_buffer; > > @@ -1041,9 +1047,12 @@ > > int usb_stor_Bulk_max_lun(struct us_data *us) > > { > > unsigned char data; > > + unsigned char *buffer; > > int result; > > int pipe; > > > > + buffer = kmalloc(sizeof data, GFP_KERNEL); > > + > > /* issue the command -- use usb_control_msg() because > > * the state machine is not yet alive */ > > pipe = usb_rcvctrlpipe(us->pusb_dev, 0); > > @@ -1051,7 +1060,9 @@ > > US_BULK_GET_MAX_LUN, > > USB_DIR_IN | USB_TYPE_CLASS | > > USB_RECIP_INTERFACE, > > - 0, us->ifnum, &data, sizeof(data), HZ); > > + 0, us->ifnum, buffer, > > sizeof(data), HZ); > > + data = *buffer; > > + kfree(buffer); > > > > US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", > > result, data); > > @@ -1077,41 +1088,54 @@ > > > > int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) { > > - struct bulk_cb_wrap bcb; > > - struct bulk_cs_wrap bcs; > > + struct bulk_cb_wrap *bcb; > > + struct bulk_cs_wrap *bcs; > > int result; > > int pipe; > > int partial; > > + int ret = USB_STOR_TRANSPORT_ERROR; > > + > > + bcb = kmalloc(sizeof *bcb, in_interrupt() ? > > GFP_ATOMIC : GFP_KERNEL); > > + if (!bcb) { > > + return USB_STOR_TRANSPORT_ERROR; > > + } > > + bcs = kmalloc(sizeof *bcs, in_interrupt() ? > > GFP_ATOMIC : GFP_KERNEL); > > + if (!bcs) { > > + kfree(bcb); > > + return USB_STOR_TRANSPORT_ERROR; > > + } > > > > /* set up the command wrapper */ > > - bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN); > > - bcb.DataTransferLength = > > cpu_to_le32(usb_stor_transfer_length(srb)); > > - bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? > > 1 << 7 : 0; > > - bcb.Tag = srb->serial_number; > > - bcb.Lun = srb->cmnd[1] >> 5; > > + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); > > + bcb->DataTransferLength = > > cpu_to_le32(usb_stor_transfer_length(srb)); > > + bcb->Flags = srb->sc_data_direction == SCSI_DATA_READ ? > > 1 << 7 : 0; > > + bcb->Tag = srb->serial_number; > > + bcb->Lun = srb->cmnd[1] >> 5; > > if (us->flags & US_FL_SCM_MULT_TARG) > > - bcb.Lun |= srb->target << 4; > > - bcb.Length = srb->cmd_len; > > + bcb->Lun |= srb->target << 4; > > + bcb->Length = srb->cmd_len; > > > > /* construct the pipe handle */ > > pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); > > > > /* copy the command payload */ > > - memset(bcb.CDB, 0, sizeof(bcb.CDB)); > > - memcpy(bcb.CDB, srb->cmnd, bcb.Length); > > + memset(bcb->CDB, 0, sizeof(bcb->CDB)); > > + memcpy(bcb->CDB, srb->cmnd, bcb->Length); > > > > /* send it to out endpoint */ > > US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L > > %d F %d CL %d\n", > > - le32_to_cpu(bcb.Signature), bcb.Tag, > > - (bcb.Lun >> 4), (bcb.Lun & 0x0F), > > - bcb.DataTransferLength, bcb.Flags, bcb.Length); > > - result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, > > + le32_to_cpu(bcb->Signature), bcb->Tag, > > + (bcb->Lun >> 4), (bcb->Lun & 0x0F), > > + bcb->DataTransferLength, bcb->Flags, bcb->Length); > > + result = usb_stor_bulk_msg(us, bcb, pipe, US_BULK_CB_WRAP_LEN, > > &partial); > > US_DEBUGP("Bulk command transfer result=%d\n", result); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > > > /* if we stall, we need to clear it before we go on */ > > if (result == -EPIPE) { > > @@ -1119,25 +1143,30 @@ > > result = usb_stor_clear_halt(us, pipe); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > result = -EPIPE; > > } else if (result) { > > /* unknown error -- we've got a problem */ > > - return USB_STOR_TRANSPORT_ERROR; > > + ret = USB_STOR_TRANSPORT_ERROR; > > + goto out; > > } > > > > /* if the command transfered well, then we go to the > > data stage */ > > if (result == 0) { > > /* send/receive data payload, if there is any */ > > - if (bcb.DataTransferLength) { > > + if (bcb->DataTransferLength) { > > usb_stor_transfer(srb, us); > > result = srb->result; > > US_DEBUGP("Bulk data transfer result > > 0x%x\n", result); > > > > /* if it was aborted, we need to > > indicate that */ > > - if (result == US_BULK_TRANSFER_ABORTED) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == US_BULK_TRANSFER_ABORTED) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > } > > } > > > > @@ -1150,12 +1179,14 @@ > > > > /* get CSW for device status */ > > US_DEBUGP("Attempting to get CSW...\n"); > > - result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, > > + result = usb_stor_bulk_msg(us, bcs, pipe, US_BULK_CS_WRAP_LEN, > > &partial); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > > > /* did the attempt to read the CSW fail? */ > > if (result == -EPIPE) { > > @@ -1163,17 +1194,21 @@ > > result = usb_stor_clear_halt(us, pipe); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > > > /* get the status again */ > > US_DEBUGP("Attempting to get CSW (2nd try)...\n"); > > - result = usb_stor_bulk_msg(us, &bcs, pipe, > > + result = usb_stor_bulk_msg(us, bcs, pipe, > > US_BULK_CS_WRAP_LEN, > > &partial); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > > > /* if it fails again, we need a reset and > > return an error*/ > > if (result == -EPIPE) { > > @@ -1181,48 +1216,60 @@ > > result = usb_stor_clear_halt(us, pipe); > > > > /* if the command was aborted, indicate that */ > > - if (result == -ENOENT) > > - return USB_STOR_TRANSPORT_ABORTED; > > - return USB_STOR_TRANSPORT_ERROR; > > + if (result == -ENOENT) { > > + ret = USB_STOR_TRANSPORT_ABORTED; > > + goto out; > > + } > > + ret = USB_STOR_TRANSPORT_ERROR; > > + goto out; > > } > > } > > > > /* if we still have a failure at this point, we're in trouble */ > > US_DEBUGP("Bulk status result = %d\n", result); > > if (result) { > > - return USB_STOR_TRANSPORT_ERROR; > > + ret = USB_STOR_TRANSPORT_ERROR; > > + goto out; > > } > > > > /* check bulk status */ > > US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n", > > - le32_to_cpu(bcs.Signature), bcs.Tag, > > - bcs.Residue, bcs.Status); > > - if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) || > > - bcs.Tag != bcb.Tag || > > - bcs.Status > US_BULK_STAT_PHASE || partial != 13) { > > + le32_to_cpu(bcs->Signature), bcs->Tag, > > + bcs->Residue, bcs->Status); > > + if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) || > > + bcs->Tag != bcb->Tag || > > + bcs->Status > US_BULK_STAT_PHASE || partial != 13) { > > US_DEBUGP("Bulk logical error\n"); > > - return USB_STOR_TRANSPORT_ERROR; > > + ret = USB_STOR_TRANSPORT_ERROR; > > + goto out; > > } > > > > /* based on the status code, we report good or bad */ > > - switch (bcs.Status) { > > + switch (bcs->Status) { > > case US_BULK_STAT_OK: > > /* command good -- note that data could > > be short */ > > - return USB_STOR_TRANSPORT_GOOD; > > + ret = USB_STOR_TRANSPORT_GOOD; > > + goto out; > > > > case US_BULK_STAT_FAIL: > > /* command failed */ > > - return USB_STOR_TRANSPORT_FAILED; > > + ret = USB_STOR_TRANSPORT_FAILED; > > + goto out; > > > > case US_BULK_STAT_PHASE: > > /* phase error -- note that a transport > > reset will be > > * invoked by the invoke_transport() function > > */ > > - return USB_STOR_TRANSPORT_ERROR; > > + ret = USB_STOR_TRANSPORT_ERROR; > > + goto out; > > } > > > > /* we should never get here, but if we do, we're in trouble */ > > - return USB_STOR_TRANSPORT_ERROR; > > + > > + out: > > + kfree(bcb); > > + kfree(bcs); > > + return ret; > > } > > > > > > /************************************************************* > > ********** > > diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c > > --- a/drivers/usb/usb.c Fri Jun 7 16:35:31 2002 > > +++ b/drivers/usb/usb.c Fri Jun 7 16:35:31 2002 > > @@ -1787,16 +1787,23 @@ > > { > > int i = 5; > > int result; > > + void *tmp_buf; > > > > - memset(buf,0,size); // Make sure we parse really > > received data > > + tmp_buf = kmalloc(size, in_interrupt() ? GFP_ATOMIC > > : GFP_KERNEL); > > + if (!tmp_buf) { > > + return -ENOMEM; > > + } > > + memset(tmp_buf,0,size); // Make sure we parse really > > received data > > > > while (i--) { > > if ((result = usb_control_msg(dev, > > usb_rcvctrlpipe(dev, 0), > > USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, > > - (type << 8) + index, 0, buf, size, HZ * > > GET_TIMEOUT)) > 0 || > > + (type << 8) + index, 0, tmp_buf, size, > > HZ * GET_TIMEOUT)) > 0 || > > result == -EPIPE) > > break; /* retry if the returned length > > was 0; flaky device */ > > } > > + memcpy(buf, tmp_buf, size); > > + kfree(tmp_buf); > > return result; > > } > > > > > > _______________________________________________________________ > > > > Don't miss the 2002 Sprint PCS Application Developer's > > Conference August 25-28 in Las Vegas - > > http://devcon.sprintpcs.com/adp/index.cfm?> source=osdntextlink > > > > > > _______________________________________________ > > [EMAIL PROTECTED] > > To unsubscribe, use the last form field at: > > https://lists.sourceforge.net/lists/listinfo/l> inux-usb-devel > > > > > _______________________________________________________________ > > Don't miss the 2002 Sprint PCS Application Developer's Conference > August 25-28 in Las Vegas - >http://devcon.sprintpcs.com/adp/index.cfm?source=osdntextlink > > _______________________________________________ > [EMAIL PROTECTED] > To unsubscribe, use the last form field at: > https://lists.sourceforge.net/lists/listinfo/linux-usb-devel _______________________________________________________________ Don't miss the 2002 Sprint PCS Application Developer's Conference August 25-28 in Las Vegas - http://devcon.sprintpcs.com/adp/index.cfm?source=osdntextlink _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel