USB: add EPIC support to the io_edgeport driver
[pandora-kernel.git] / drivers / usb / serial / io_edgeport.c
index f623d58..cca1607 100644 (file)
@@ -146,6 +146,8 @@ struct edgeport_serial {
        struct edge_manuf_descriptor    manuf_descriptor;       /* the manufacturer descriptor */
        struct edge_boot_descriptor     boot_descriptor;        /* the boot firmware descriptor */
        struct edgeport_product_info    product_info;           /* Product Info */
+       struct edge_compatibility_descriptor epic_descriptor;   /* Edgeport compatible descriptor */
+       int                     is_epic;                        /* flag if EPiC device or not */
 
        __u8                    interrupt_in_endpoint;          /* the interrupt endpoint handle */
        unsigned char *         interrupt_in_buffer;            /* the buffer we use for the interrupt endpoint */
@@ -397,6 +399,7 @@ static int get_string (struct usb_device *dev, int Id, char *string, int buflen)
        unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2);
 
        kfree(pStringDesc);
+       dbg("%s - USB String %s", __FUNCTION__, string);
        return strlen(string);
 }
 
@@ -434,6 +437,34 @@ static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_de
 }
 #endif
 
+static void dump_product_info(struct edgeport_product_info *product_info)
+{
+       // Dump Product Info structure
+       dbg("**Product Information:");
+       dbg("  ProductId             %x", product_info->ProductId );
+       dbg("  NumPorts              %d", product_info->NumPorts );
+       dbg("  ProdInfoVer           %d", product_info->ProdInfoVer );
+       dbg("  IsServer              %d", product_info->IsServer);
+       dbg("  IsRS232               %d", product_info->IsRS232 );
+       dbg("  IsRS422               %d", product_info->IsRS422 );
+       dbg("  IsRS485               %d", product_info->IsRS485 );
+       dbg("  RomSize               %d", product_info->RomSize );
+       dbg("  RamSize               %d", product_info->RamSize );
+       dbg("  CpuRev                %x", product_info->CpuRev  );
+       dbg("  BoardRev              %x", product_info->BoardRev);
+       dbg("  BootMajorVersion      %d.%d.%d", product_info->BootMajorVersion,
+           product_info->BootMinorVersion,
+           le16_to_cpu(product_info->BootBuildNumber));
+       dbg("  FirmwareMajorVersion  %d.%d.%d", product_info->FirmwareMajorVersion,
+           product_info->FirmwareMinorVersion,
+           le16_to_cpu(product_info->FirmwareBuildNumber));
+       dbg("  ManufactureDescDate   %d/%d/%d", product_info->ManufactureDescDate[0],
+           product_info->ManufactureDescDate[1],
+           product_info->ManufactureDescDate[2]+1900);
+       dbg("  iDownloadFile         0x%x", product_info->iDownloadFile);
+       dbg("  EpicVer               %d", product_info->EpicVer);
+}
+
 static void get_product_info(struct edgeport_serial *edge_serial)
 {
        struct edgeport_product_info *product_info = &edge_serial->product_info;
@@ -495,30 +526,60 @@ static void get_product_info(struct edgeport_serial *edge_serial)
                        break;
        }
 
-       // Dump Product Info structure
-       dbg("**Product Information:");
-       dbg("  ProductId             %x", product_info->ProductId );
-       dbg("  NumPorts              %d", product_info->NumPorts );
-       dbg("  ProdInfoVer           %d", product_info->ProdInfoVer );
-       dbg("  IsServer              %d", product_info->IsServer);
-       dbg("  IsRS232               %d", product_info->IsRS232 );
-       dbg("  IsRS422               %d", product_info->IsRS422 );
-       dbg("  IsRS485               %d", product_info->IsRS485 );
-       dbg("  RomSize               %d", product_info->RomSize );
-       dbg("  RamSize               %d", product_info->RamSize );
-       dbg("  CpuRev                %x", product_info->CpuRev  );
-       dbg("  BoardRev              %x", product_info->BoardRev);
-       dbg("  BootMajorVersion      %d.%d.%d", product_info->BootMajorVersion,
-           product_info->BootMinorVersion,
-           le16_to_cpu(product_info->BootBuildNumber));
-       dbg("  FirmwareMajorVersion  %d.%d.%d", product_info->FirmwareMajorVersion,
-           product_info->FirmwareMinorVersion,
-           le16_to_cpu(product_info->FirmwareBuildNumber));
-       dbg("  ManufactureDescDate   %d/%d/%d", product_info->ManufactureDescDate[0],
-           product_info->ManufactureDescDate[1],
-           product_info->ManufactureDescDate[2]+1900);
-       dbg("  iDownloadFile         0x%x",     product_info->iDownloadFile);
+       dump_product_info(product_info);
+}
 
+static int get_epic_descriptor(struct edgeport_serial *ep)
+{
+       int result;
+       struct usb_serial *serial = ep->serial;
+       struct edgeport_product_info *product_info = &ep->product_info;
+       struct edge_compatibility_descriptor *epic = &ep->epic_descriptor;
+       struct edge_compatibility_bits *bits;
+
+       ep->is_epic = 0;
+       result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                                USB_REQUEST_ION_GET_EPIC_DESC,
+                                0xC0, 0x00, 0x00,
+                                &ep->epic_descriptor,
+                                sizeof(struct edge_compatibility_descriptor),
+                                300);
+
+       dbg("%s result = %d", __FUNCTION__, result);
+
+       if (result > 0) {
+               ep->is_epic = 1;
+               memset(product_info, 0, sizeof(struct edgeport_product_info));
+
+               product_info->NumPorts                  = epic->NumPorts;
+               product_info->ProdInfoVer               = 0;
+               product_info->FirmwareMajorVersion      = epic->MajorVersion;
+               product_info->FirmwareMinorVersion      = epic->MinorVersion;
+               product_info->FirmwareBuildNumber       = epic->BuildNumber;
+               product_info->iDownloadFile             = epic->iDownloadFile;
+               product_info->EpicVer                   = epic->EpicVer;
+               product_info->Epic                      = epic->Supports;
+               product_info->ProductId                 = ION_DEVICE_ID_EDGEPORT_COMPATIBLE;
+               dump_product_info(product_info);
+
+               bits = &ep->epic_descriptor.Supports;
+               dbg("**EPIC descriptor:");
+               dbg("  VendEnableSuspend: %s", bits->VendEnableSuspend  ? "TRUE": "FALSE");
+               dbg("  IOSPOpen         : %s", bits->IOSPOpen           ? "TRUE": "FALSE" );
+               dbg("  IOSPClose        : %s", bits->IOSPClose          ? "TRUE": "FALSE" );
+               dbg("  IOSPChase        : %s", bits->IOSPChase          ? "TRUE": "FALSE" );
+               dbg("  IOSPSetRxFlow    : %s", bits->IOSPSetRxFlow      ? "TRUE": "FALSE" );
+               dbg("  IOSPSetTxFlow    : %s", bits->IOSPSetTxFlow      ? "TRUE": "FALSE" );
+               dbg("  IOSPSetXChar     : %s", bits->IOSPSetXChar       ? "TRUE": "FALSE" );
+               dbg("  IOSPRxCheck      : %s", bits->IOSPRxCheck        ? "TRUE": "FALSE" );
+               dbg("  IOSPSetClrBreak  : %s", bits->IOSPSetClrBreak    ? "TRUE": "FALSE" );
+               dbg("  IOSPWriteMCR     : %s", bits->IOSPWriteMCR       ? "TRUE": "FALSE" );
+               dbg("  IOSPWriteLCR     : %s", bits->IOSPWriteLCR       ? "TRUE": "FALSE" );
+               dbg("  IOSPSetBaudRate  : %s", bits->IOSPSetBaudRate    ? "TRUE": "FALSE" );
+               dbg("  TrueEdgeport     : %s", bits->TrueEdgeport       ? "TRUE": "FALSE" );
+       }
+
+       return result;
 }
 
 
@@ -1017,21 +1078,29 @@ static void edge_close (struct usb_serial_port *port, struct file * filp)
 
        edge_port->closePending = TRUE;
 
-       /* flush and chase */
-       edge_port->chaseResponsePending = TRUE;
-
-       dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
-       status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
-       if (status == 0) {
-               // block until chase finished
-               block_until_chase_response(edge_port);
-       } else {
-               edge_port->chaseResponsePending = FALSE;
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+               /* flush and chase */
+               edge_port->chaseResponsePending = TRUE;
+
+               dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
+               status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
+               if (status == 0) {
+                       // block until chase finished
+                       block_until_chase_response(edge_port);
+               } else {
+                       edge_port->chaseResponsePending = FALSE;
+               }
        }
 
-       /* close the port */
-       dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__);
-       send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPClose))) {
+              /* close the port */
+               dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__);
+               send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0);
+       }
 
        //port->close = TRUE;
        edge_port->closePending = FALSE;
@@ -1694,29 +1763,38 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned
 static void edge_break (struct usb_serial_port *port, int break_state)
 {
        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+       struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
        int status;
 
-       /* flush and chase */
-       edge_port->chaseResponsePending = TRUE;
-
-       dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
-       status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
-       if (status == 0) {
-               // block until chase finished
-               block_until_chase_response(edge_port);
-       } else {
-               edge_port->chaseResponsePending = FALSE;
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+               /* flush and chase */
+               edge_port->chaseResponsePending = TRUE;
+
+               dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__);
+               status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0);
+               if (status == 0) {
+                       // block until chase finished
+                       block_until_chase_response(edge_port);
+               } else {
+                       edge_port->chaseResponsePending = FALSE;
+               }
        }
 
-       if (break_state == -1) {
-               dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__);
-               status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0);
-       } else {
-               dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__);
-               status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0);
-       }
-       if (status) {
-               dbg("%s - error sending break set/clear command.", __FUNCTION__);
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) {
+               if (break_state == -1) {
+                       dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__);
+                       status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0);
+               } else {
+                       dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__);
+                       status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0);
+               }
+               if (status) {
+                       dbg("%s - error sending break set/clear command.", __FUNCTION__);
+               }
        }
 
        return;
@@ -2288,6 +2366,7 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer
  *****************************************************************************/
 static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate)
 {
+       struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial);
        unsigned char *cmdBuffer;
        unsigned char *currCmd;
        int cmdLen = 0;
@@ -2295,6 +2374,14 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa
        int status;
        unsigned char number = edge_port->port->number - edge_port->port->serial->minor;
 
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (!edge_serial->epic_descriptor.Supports.IOSPSetBaudRate))) {
+               dbg("SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d",
+                   edge_port->port->number, baudRate);
+               return 0;
+       }
+
        dbg("%s - port = %d, baud = %d", __FUNCTION__, edge_port->port->number, baudRate);
 
        status = calc_baud_rate_divisor (baudRate, &divisor);
@@ -2374,6 +2461,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor)
  *****************************************************************************/
 static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue)
 {
+       struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial);
        unsigned char *cmdBuffer;
        unsigned char *currCmd;
        unsigned long cmdLen = 0;
@@ -2381,6 +2469,22 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r
 
        dbg("%s - write to %s register 0x%02x", (regNum == MCR) ? "MCR" : "LCR", __FUNCTION__, regValue);
 
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (!edge_serial->epic_descriptor.Supports.IOSPWriteMCR) &&
+            (regNum == MCR))) {
+               dbg("SendCmdWriteUartReg - Not writting to MCR Register");
+               return 0;
+       }
+
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (!edge_serial->epic_descriptor.Supports.IOSPWriteLCR) &&
+            (regNum == LCR))) {
+               dbg ("SendCmdWriteUartReg - Not writting to LCR Register");
+               return 0;
+       }
+
        // Alloc memory for the string of commands.
        cmdBuffer = kmalloc (0x10, GFP_ATOMIC);
        if (cmdBuffer == NULL ) {
@@ -2414,6 +2518,7 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r
 #endif
 static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios)
 {
+       struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial);
        struct tty_struct *tty;
        int baud;
        unsigned cflag;
@@ -2494,8 +2599,12 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
                unsigned char stop_char  = STOP_CHAR(tty);
                unsigned char start_char = START_CHAR(tty);
 
-               send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
-               send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
+               if ((!edge_serial->is_epic) ||
+                   ((edge_serial->is_epic) &&
+                    (edge_serial->epic_descriptor.Supports.IOSPSetXChar))) {
+                       send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XON_CHAR, start_char);
+                       send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char);
+               }
 
                /* if we are implementing INBOUND XON/XOFF */
                if (I_IXOFF(tty)) {
@@ -2515,8 +2624,14 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
        }
 
        /* Set flow control to the configured value */
-       send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
-       send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)))
+               send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
+       if ((!edge_serial->is_epic) ||
+           ((edge_serial->is_epic) &&
+            (edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)))
+               send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
 
 
        edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
@@ -2728,6 +2843,13 @@ static int edge_startup (struct usb_serial *serial)
        struct edgeport_port *edge_port;
        struct usb_device *dev;
        int i, j;
+       int response;
+       int interrupt_in_found;
+       int bulk_in_found;
+       int bulk_out_found;
+       static __u32 descriptor[3] = {  EDGE_COMPATIBILITY_MASK0,
+                                       EDGE_COMPATIBILITY_MASK1,
+                                       EDGE_COMPATIBILITY_MASK2 };
 
        dev = serial->dev;
 
@@ -2750,38 +2872,50 @@ static int edge_startup (struct usb_serial *serial)
 
        dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
 
-       /* get the manufacturing descriptor for this device */
-       get_manufacturing_desc (edge_serial);
+       /* Read the epic descriptor */
+       if (get_epic_descriptor(edge_serial) <= 0) {
+               /* memcpy descriptor to Supports structures */
+               memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
+                      sizeof(struct edge_compatibility_bits));
 
-       /* get the boot descriptor */
-       get_boot_desc (edge_serial);
+               /* get the manufacturing descriptor for this device */
+               get_manufacturing_desc (edge_serial);
 
-       get_product_info(edge_serial);
+               /* get the boot descriptor */
+               get_boot_desc (edge_serial);
+
+               get_product_info(edge_serial);
+       }
 
        /* set the number of ports from the manufacturing description */
        /* serial->num_ports = serial->product_info.NumPorts; */
-       if (edge_serial->product_info.NumPorts != serial->num_ports) {
-               warn("%s - Device Reported %d serial ports vs core "
-                    "thinking we have %d ports, email greg@kroah.com this info.",
-                    __FUNCTION__, edge_serial->product_info.NumPorts, 
-                    serial->num_ports);
+       if ((!edge_serial->is_epic) &&
+           (edge_serial->product_info.NumPorts != serial->num_ports)) {
+               dev_warn(&serial->dev->dev, "Device Reported %d serial ports "
+                        "vs. core thinking we have %d ports, email "
+                        "greg@kroah.com this information.",
+                        edge_serial->product_info.NumPorts,
+                        serial->num_ports);
        }
 
        dbg("%s - time 1 %ld", __FUNCTION__, jiffies);
 
-       /* now load the application firmware into this device */
-       load_application_firmware (edge_serial);
+       /* If not an EPiC device */
+       if (!edge_serial->is_epic) {
+               /* now load the application firmware into this device */
+               load_application_firmware (edge_serial);
 
-       dbg("%s - time 2 %ld", __FUNCTION__, jiffies);
+               dbg("%s - time 2 %ld", __FUNCTION__, jiffies);
 
-       /* Check current Edgeport EEPROM and update if necessary */
-       update_edgeport_E2PROM (edge_serial);
-       
-       dbg("%s - time 3 %ld", __FUNCTION__, jiffies);
+               /* Check current Edgeport EEPROM and update if necessary */
+               update_edgeport_E2PROM (edge_serial);
 
-       /* set the configuration to use #1 */
-//     dbg("set_configuration 1");
-//     usb_set_configuration (dev, 1);
+               dbg("%s - time 3 %ld", __FUNCTION__, jiffies);
+
+               /* set the configuration to use #1 */
+//             dbg("set_configuration 1");
+//             usb_set_configuration (dev, 1);
+       }
 
        /* we set up the pointers to the endpoints in the edge_open function, 
         * as the structures aren't created yet. */
@@ -2804,8 +2938,101 @@ static int edge_startup (struct usb_serial *serial)
                edge_port->port = serial->port[i];
                usb_set_serial_port_data(serial->port[i], edge_port);
        }
-       
-       return 0;
+
+       response = 0;
+
+       if (edge_serial->is_epic) {
+               /* EPIC thing, set up our interrupt polling now and our read urb, so
+                * that the device knows it really is connected. */
+               interrupt_in_found = bulk_in_found = bulk_out_found = FALSE;
+               for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) {
+                       struct usb_endpoint_descriptor *endpoint;
+                       int buffer_size;
+
+                       endpoint = &serial->interface->altsetting[0].endpoint[i].desc;
+                       buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+                       if ((!interrupt_in_found) &&
+                           (usb_endpoint_is_int_in(endpoint))) {
+                               /* we found a interrupt in endpoint */
+                               dbg("found interrupt in");
+
+                               /* not set up yet, so do it now */
+                               edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL);
+                               if (!edge_serial->interrupt_read_urb) {
+                                       err("out of memory");
+                                       return -ENOMEM;
+                               }
+                               edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+                               if (!edge_serial->interrupt_in_buffer) {
+                                       err("out of memory");
+                                       usb_free_urb(edge_serial->interrupt_read_urb);
+                                       return -ENOMEM;
+                               }
+                               edge_serial->interrupt_in_endpoint = endpoint->bEndpointAddress;
+
+                               /* set up our interrupt urb */
+                               usb_fill_int_urb(edge_serial->interrupt_read_urb,
+                                                dev,
+                                                usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+                                                edge_serial->interrupt_in_buffer,
+                                                buffer_size,
+                                                edge_interrupt_callback,
+                                                edge_serial,
+                                                endpoint->bInterval);
+
+                               interrupt_in_found = TRUE;
+                       }
+
+                       if ((!bulk_in_found) &&
+                           (usb_endpoint_is_bulk_in(endpoint))) {
+                               /* we found a bulk in endpoint */
+                               dbg("found bulk in");
+
+                               /* not set up yet, so do it now */
+                               edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+                               if (!edge_serial->read_urb) {
+                                       err("out of memory");
+                                       return -ENOMEM;
+                               }
+                               edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+                               if (!edge_serial->bulk_in_buffer) {
+                                       err ("out of memory");
+                                       usb_free_urb(edge_serial->read_urb);
+                                       return -ENOMEM;
+                               }
+                               edge_serial->bulk_in_endpoint = endpoint->bEndpointAddress;
+
+                               /* set up our bulk in urb */
+                               usb_fill_bulk_urb(edge_serial->read_urb, dev,
+                                                 usb_rcvbulkpipe(dev, endpoint->bEndpointAddress),
+                                                 edge_serial->bulk_in_buffer,
+                                                 endpoint->wMaxPacketSize,
+                                                 edge_bulk_in_callback,
+                                                 edge_serial);
+                               bulk_in_found = TRUE;
+                       }
+
+                       if ((!bulk_out_found) &&
+                           (usb_endpoint_is_bulk_out(endpoint))) {
+                               /* we found a bulk out endpoint */
+                               dbg("found bulk out");
+                               edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress;
+                               bulk_out_found = TRUE;
+                       }
+               }
+
+               if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) {
+                       err ("Error - the proper endpoints were not found!");
+                       return -ENODEV;
+               }
+
+               /* start interrupt read for this edgeport this interrupt will
+                * continue as long as the edgeport is connected */
+               response = usb_submit_urb(edge_serial->interrupt_read_urb, GFP_KERNEL);
+               if (response)
+                       err("%s - Error %d submitting control urb", __FUNCTION__, response);
+       }
+       return response;
 }
 
 
@@ -2815,6 +3042,7 @@ static int edge_startup (struct usb_serial *serial)
  ****************************************************************************/
 static void edge_shutdown (struct usb_serial *serial)
 {
+       struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
        int i;
 
        dbg("%s", __FUNCTION__);
@@ -2824,7 +3052,18 @@ static void edge_shutdown (struct usb_serial *serial)
                kfree (usb_get_serial_port_data(serial->port[i]));
                usb_set_serial_port_data(serial->port[i],  NULL);
        }
-       kfree (usb_get_serial_data(serial));
+       /* free up our endpoint stuff */
+       if (edge_serial->is_epic) {
+               usb_unlink_urb(edge_serial->interrupt_read_urb);
+               usb_free_urb(edge_serial->interrupt_read_urb);
+               kfree(edge_serial->interrupt_in_buffer);
+
+               usb_unlink_urb(edge_serial->read_urb);
+               usb_free_urb(edge_serial->read_urb);
+               kfree(edge_serial->bulk_in_buffer);
+       }
+
+       kfree(edge_serial);
        usb_set_serial_data(serial, NULL);
 }
 
@@ -2846,6 +3085,9 @@ static int __init edgeport_init(void)
        retval = usb_serial_register(&edgeport_8port_device);
        if (retval)
                goto failed_8port_device_register;
+       retval = usb_serial_register(&epic_device);
+       if (retval)
+               goto failed_epic_device_register;
        retval = usb_register(&io_driver);
        if (retval) 
                goto failed_usb_register;
@@ -2853,6 +3095,8 @@ static int __init edgeport_init(void)
        return 0;
 
 failed_usb_register:
+       usb_serial_deregister(&epic_device);
+failed_epic_device_register:
        usb_serial_deregister(&edgeport_8port_device);
 failed_8port_device_register:
        usb_serial_deregister(&edgeport_4port_device);
@@ -2873,6 +3117,7 @@ static void __exit edgeport_exit (void)
        usb_serial_deregister (&edgeport_2port_device);
        usb_serial_deregister (&edgeport_4port_device);
        usb_serial_deregister (&edgeport_8port_device);
+       usb_serial_deregister (&epic_device);
 }
 
 module_init(edgeport_init);