USB: add EPIC support to the io_edgeport driver

This patch adds EPiC support to the io_edgeport driver which adds
support for a number of NCR printers:
	- NCR (Axiohm) 7401-K580 printer
	- NCR (TEC) 7401-K590 printer, 7402-K592
	- NCR (TEC) 7167, 7168 printers
	- NCR (TEC) 7197, 7198, F306, F307, F309 printers
	- NCR (Axiohm) 7194 printer
	- NCR (Axiohm) 7158 printer
and a few more.

It is based on the 2.6.19 kernel.

Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>

diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index f623d58..cca1607 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -146,6 +146,8 @@
 	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 @@
 	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 @@
 }
 #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 @@
 			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 @@
 
 	edge_port->closePending = TRUE;
 
-	/* flush and chase */
-	edge_port->chaseResponsePending = TRUE;
+	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;
+		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 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;
+	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;
+		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 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 @@
 	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 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 @@
 
 	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 @@
 #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 @@
 		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 @@
 	}
 
 	/* 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 @@
 	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 @@
 
 	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 @@
 		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 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 @@
 		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 @@
 	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 @@
 	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 @@
 	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);
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 123fa8a..29a913a 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -111,10 +111,12 @@
 	__le16	FirmwareBuildNumber;		/*				zzzz (LE format) */
 
 	__u8	ManufactureDescDate[3];		/* MM/DD/YY when descriptor template was compiled */
-	__u8	Unused1[1];			/* Available */
+	__u8	HardwareType;
 
 	__u8	iDownloadFile;			/* What to download to EPiC device */
-	__u8	Unused2[2];			/* Available */
+	__u8	EpicVer;			/* What version of EPiC spec this device supports */
+
+	struct edge_compatibility_bits Epic;
 };
 
 /*
diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h
index fad561c..3cbb8c1 100644
--- a/drivers/usb/serial/io_tables.h
+++ b/drivers/usb/serial/io_tables.h
@@ -47,6 +47,18 @@
 	{ }
 };
 
+static struct usb_device_id Epic_port_id_table [] = {
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
+	{ }
+};
+
 /* Devices that this driver supports */
 static struct usb_device_id id_table_combined [] = {
 	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4) },
@@ -70,6 +82,14 @@
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
+	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
+	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
 	{ }							/* Terminating entry */
 };
 
@@ -165,5 +185,35 @@
 	.write_bulk_callback	= edge_bulk_out_data_callback,
 };
 
+static struct usb_serial_driver epic_device = {
+	.driver = {
+		.owner		= THIS_MODULE,
+		.name		= "epic",
+	},
+	.description		= "EPiC device",
+	.id_table		= Epic_port_id_table,
+	.num_interrupt_in	= 1,
+	.num_bulk_in		= 1,
+	.num_bulk_out		= 1,
+	.num_ports		= 1,
+	.open			= edge_open,
+	.close			= edge_close,
+	.throttle		= edge_throttle,
+	.unthrottle		= edge_unthrottle,
+	.attach			= edge_startup,
+	.shutdown		= edge_shutdown,
+	.ioctl			= edge_ioctl,
+	.set_termios		= edge_set_termios,
+	.tiocmget		= edge_tiocmget,
+	.tiocmset		= edge_tiocmset,
+	.write			= edge_write,
+	.write_room		= edge_write_room,
+	.chars_in_buffer	= edge_chars_in_buffer,
+	.break_ctl		= edge_break,
+	.read_int_callback	= edge_interrupt_callback,
+	.read_bulk_callback	= edge_bulk_in_callback,
+	.write_bulk_callback	= edge_bulk_out_data_callback,
+};
+
 #endif
 
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
index f1804fd..e57fa11 100644
--- a/drivers/usb/serial/io_usbvend.h
+++ b/drivers/usb/serial/io_usbvend.h
@@ -30,6 +30,7 @@
 
 #define	USB_VENDOR_ID_ION	0x1608		// Our VID
 #define	USB_VENDOR_ID_TI	0x0451		// TI VID
+#define USB_VENDOR_ID_AXIOHM	0x05D9		/* Axiohm VID */
 
 //
 // Definitions of USB product IDs (PID)
@@ -334,6 +335,10 @@
 
 };
 
+#define EDGE_COMPATIBILITY_MASK0	0x0001
+#define EDGE_COMPATIBILITY_MASK1	0x3FFF
+#define EDGE_COMPATIBILITY_MASK2	0x0001
+
 struct edge_compatibility_descriptor
 {
 	__u8	Length;				// Descriptor Length (per USB spec)