Hi again,

I changed some return values in the ioctl() and open() method.
The allocated memory for the write_urb is now freed in the close() method.
(Thanks Alan Cox for pointing out these errors)

Discard the urb ststus check in the write function.

Please comment.

Best regards,

        Thomas
diff -Naur -X dontdiff linux-2.4.18/Documentation/Configure.help linux-2.4.18-thomas/Documentation/Configure.help
--- linux-2.4.18/Documentation/Configure.help	Mon Feb 25 20:37:51 2002
+++ linux-2.4.18-thomas/Documentation/Configure.help	Thu Nov  7 15:50:52 2002
@@ -13164,6 +13164,17 @@
   The module will be called pl2303.o.  If you want to compile it as
   a module, say M here and read <file:Documentation/modules.txt>.
 
+USB KOBIL chipcard reader
+CONFIG_USB_SERIAL_KOBIL_SCT
+  Say Y here if you want to use one of the following KOBIL USB chipcard 
+  readers: TWIN, KAAN Standard Plus, SecOVID Reader Plus, B1 PRO, KAAN PRO
+
+  Note that you need a current CT-API.
+  This code is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called kobil_sct.o. If you want to compile it as
+  a module, say M here and read <file:Documentation/modules.txt>.
+
 USB REINER SCT cyberJack pinpad/e-com chipcard reader
 CONFIG_USB_SERIAL_CYBERJACK
   Say Y here if you want to use a cyberJack pinpad/e-com USB chipcard
diff -Naur -X dontdiff linux-2.4.18/drivers/usb/serial/Config.in linux-2.4.18-thomas/drivers/usb/serial/Config.in
--- linux-2.4.18/drivers/usb/serial/Config.in	Mon Feb 25 20:38:07 2002
+++ linux-2.4.18-thomas/drivers/usb/serial/Config.in	Tue Oct 29 10:04:44 2002
@@ -30,6 +30,7 @@
    dep_mbool '    USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W $CONFIG_USB_SERIAL_KEYSPAN
 dep_tristate '  USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB KL5KUSB105 (Palmconnect) Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KLSI $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
+dep_tristate '  USB KOBIL chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_KOBIL_SCT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Prolific 2303 Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PL2303 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
diff -Naur -X dontdiff linux-2.4.18/drivers/usb/serial/Makefile linux-2.4.18-thomas/drivers/usb/serial/Makefile
--- linux-2.4.18/drivers/usb/serial/Makefile	Mon Feb 25 20:38:07 2002
+++ linux-2.4.18-thomas/drivers/usb/serial/Makefile	Wed Oct  9 14:23:31 2002
@@ -24,6 +24,7 @@
 obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
 obj-$(CONFIG_USB_SERIAL_IR)			+= ir-usb.o
 obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
+obj-$(CONFIG_USB_SERIAL_KOBIL_SCT)		+= kobil_sct.o
 
 # Objects that export symbols.
 export-objs	:= usbserial.o
diff -Naur -X dontdiff linux-2.4.18/drivers/usb/serial/kobil_sct.c linux-2.4.18-thomas/drivers/usb/serial/kobil_sct.c
--- linux-2.4.18/drivers/usb/serial/kobil_sct.c	Thu Jan  1 01:00:00 1970
+++ linux-2.4.18-thomas/drivers/usb/serial/kobil_sct.c	Mon Nov 11 15:32:05 2002
@@ -0,0 +1,722 @@
+/*
+ *  KOBIL USB Smart Card Terminal Driver
+ *
+ *  Copyright (C) 2002  KOBIL Systems GmbH 
+ *  Author: Thomas Wahrenbruch
+ *
+ *  Contact: [EMAIL PROTECTED]
+ *
+ *  This program is largely derived from work by the linux-usb group
+ *  and associated source files.  Please see the usb/serial files for
+ *  individual credits and copyrights.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Thanks to Greg Kroah-Hartman ([EMAIL PROTECTED]) for his help and
+ *  patience.
+ *
+ * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
+ * (Adapter K), B1 Professional and KAAN Professional (Adapter B)
+ * 
+ * TODO: High baudrates
+ *
+ * (11/11/2002) tw
+ *      Changed return-values in ioctl and write if an error occurs.
+ *      Free port->write_urb->transfer_buffer in close().
+ *
+ * (11/07/2002) tw
+ *      Initial version.
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+
+//#include <linux/ioctl.h>
+#include <linux/slab.h>
+//#include <linux/tty.h>
+//#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+
+#include "kobil_sct.h"
+//#include "usb-debug.c"
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	static int debug = 1;
+#else
+	static int debug;
+#endif
+
+#include "usb-serial.h"
+
+#define DRIVER_VERSION "11/11/2002"
+#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com";
+#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
+
+#define KOBIL_VENDOR_ID	           0x0D46
+#define KOBIL_ADAPTER_B_PRODUCT_ID 0x2011
+#define KOBIL_ADAPTER_K_PRODUCT_ID 0x2012
+#define KOBIL_USBTWIN_PRODUCT_ID   0x0078
+
+#define KOBIL_TIMEOUT    500
+#define KOBIL_BUF_LENGTH 300
+
+
+/* Function prototypes */
+static int  kobil_startup (struct usb_serial *serial);
+static void kobil_shutdown (struct usb_serial *serial);
+static int  kobil_open (struct usb_serial_port *port, struct file *filp);
+static void kobil_close (struct usb_serial_port *port, struct file *filp);
+static int  kobil_write (struct usb_serial_port *port, int from_user, 
+			 const unsigned char *buf, int count);
+static int  kobil_write_room(struct usb_serial_port *port);
+static int  kobil_ioctl(struct usb_serial_port *port, struct file *file,
+			unsigned int cmd, unsigned long arg);
+static void kobil_read_int_callback( struct urb *urb );
+static void kobil_write_callback( struct urb *purb );
+
+
+static __devinitdata struct usb_device_id id_table [] = {
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
+	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
+	{ }			/* Terminating entry */
+};
+
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+struct usb_serial_device_type kobil_device = {
+	name:			"KOBIL USB smart card terminal",
+	id_table:		id_table,
+	needs_interrupt_in:	MUST_HAVE,
+	needs_bulk_in:		MUST_HAVE_NOT,
+	needs_bulk_out:		MUST_HAVE_NOT,
+	num_interrupt_in:	NUM_DONT_CARE,
+	num_bulk_in:		0,
+	num_bulk_out:		0,
+	num_ports:		1,
+	startup:		kobil_startup,
+	shutdown:		kobil_shutdown,
+	ioctl:			kobil_ioctl,
+	open:			kobil_open,
+	close:			kobil_close,
+	write:			kobil_write,
+	write_room:             kobil_write_room,
+	read_int_callback:	kobil_read_int_callback,
+};
+
+
+struct kobil_private {
+	int write_int_endpoint_address;
+	int read_int_endpoint_address;
+	unsigned char buf[KOBIL_BUF_LENGTH]; // buffer for the APDU to send
+	int filled;  // index of the last char in buf
+	int cur_pos; // index of the next char to send in buf
+	__u16 device_type;
+	int line_state;
+	struct termios internal_termios;
+};
+
+
+static int kobil_startup (struct usb_serial *serial)
+{
+	int i;
+	struct kobil_private *priv;
+	struct usb_device *pdev;
+	struct usb_config_descriptor *actconfig;
+	struct usb_interface *interface;
+	struct usb_interface_descriptor *altsetting;
+	struct usb_endpoint_descriptor *endpoint;
+
+	serial->port->private = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
+	if (!serial->port->private){
+		return (-1);
+	}
+ 
+	priv = (struct kobil_private *) serial->port->private;
+	priv->filled = 0;
+	priv->cur_pos = 0;
+	priv->device_type = serial->product;
+	priv->line_state = 0;
+	
+	switch (priv->device_type){
+	case KOBIL_ADAPTER_B_PRODUCT_ID:
+		printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n");
+		break;
+	case KOBIL_ADAPTER_K_PRODUCT_ID:
+		printk(KERN_DEBUG "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
+		break;
+	case KOBIL_USBTWIN_PRODUCT_ID:
+		printk(KERN_DEBUG "KOBIL USBTWIN detected\n");
+		break;
+	}
+
+	// search for the neccessary endpoints
+	pdev = serial->dev;
+	actconfig = pdev->actconfig;
+	interface = actconfig->interface;
+	altsetting = interface->altsetting;
+	endpoint = altsetting->endpoint;
+  
+	for (i = 0; i < altsetting->bNumEndpoints; i++) {
+		endpoint = &altsetting->endpoint[i];
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && 
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+			dbg(__FUNCTION__ " Found interrupt out endpoint. Address: %d", endpoint->bEndpointAddress);
+			priv->write_int_endpoint_address = endpoint->bEndpointAddress;
+		}
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && 
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+			dbg(__FUNCTION__ " Found interrupt in endpoint. Address: %d", endpoint->bEndpointAddress);
+			priv->read_int_endpoint_address = endpoint->bEndpointAddress;
+		}
+	}
+	return( 0 );
+}
+
+
+static void kobil_shutdown (struct usb_serial *serial)
+{
+	int i;
+	dbg( __FUNCTION__ );
+  
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			kobil_close (&serial->port[i], NULL);
+		}
+		if (serial->port[i].private) {
+			kfree(serial->port[i].private);
+		}
+	} 
+}
+
+
+static int kobil_open (struct usb_serial_port *port, struct file *filp)
+{
+	int i, result = 0;
+	struct kobil_private *priv;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+	int write_urb_transfer_buffer_length = 8;
+	
+	dbg( __FUNCTION__ );
+	priv = (struct kobil_private *) port->private;
+	priv->line_state = 0;
+
+	if (port_paranoia_check (port, __FUNCTION__)) {
+		return -ENODEV;
+	}
+
+	down (&port->sem);
+	// someone sets the dev to 0 if the close method has been called
+	port->interrupt_in_urb->dev = port->serial->dev;
+
+	++(port->open_count);
+	
+	if (!port->active) {
+		port->active = 1;
+		
+		/* force low_latency on so that our tty_push actually forces
+		 * the data through, otherwise it is scheduled, and with high
+		 * data rates (like with OHCI) data can get lost.
+		 */
+		port->tty->low_latency = 1;
+	}
+	up (&port->sem);
+	
+	// without this, every push_tty_char is echoed :-(  
+	port->tty->termios->c_lflag = 0;
+	port->tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
+	port->tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
+	port->tty->termios->c_oflag &= ~ONLCR; // do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D)
+	
+	// set up internal termios structure 
+	priv->internal_termios.c_iflag = port->tty->termios->c_iflag;
+	priv->internal_termios.c_oflag = port->tty->termios->c_oflag;
+	priv->internal_termios.c_cflag = port->tty->termios->c_cflag;
+	priv->internal_termios.c_lflag = port->tty->termios->c_lflag;
+	
+	for (i=0; i<NCCS; i++) {
+		priv->internal_termios.c_cc[i] = port->tty->termios->c_cc[i];
+	}
+	
+	// allocate memory for transfer buffer
+	transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);  
+	if (! transfer_buffer) {
+		return -1;
+	} else {
+		memset(transfer_buffer, 0, transfer_buffer_length);
+	}
+	
+	// allocate write_urb
+	if (!port->write_urb) { 
+		dbg( __FUNCTION__  " Allocating port->write_urb");
+		port->write_urb = usb_alloc_urb(0);  
+		if (!port->write_urb) {
+			dbg( __FUNCTION__ " usb_alloc_urb failed");
+			return -1;
+		}
+	}
+ 
+	// allocate memory for write_urb transfer buffer
+	port->write_urb->transfer_buffer = (unsigned char *) kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
+	if (! port->write_urb->transfer_buffer) {
+		return -1;
+	} 
+
+	// get hardware version
+	result = usb_control_msg( port->serial->dev, 
+				  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+				  SUSBCRequest_GetMisc,
+				  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+				  SUSBCR_MSC_GetHWVersion,
+				  0,
+				  transfer_buffer,
+				  transfer_buffer_length,
+				  KOBIL_TIMEOUT
+		);
+	dbg( __FUNCTION__ " Send get_HW_version URB returns: %i", result);
+	dbg("Harware version: %i.%i.%i", transfer_buffer[0], transfer_buffer[1], transfer_buffer[2] );
+	
+	// get firmware version
+	result = usb_control_msg( port->serial->dev, 
+				  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+				  SUSBCRequest_GetMisc,
+				  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+				  SUSBCR_MSC_GetFWVersion,
+				  0,
+				  transfer_buffer,
+				  transfer_buffer_length,
+				  KOBIL_TIMEOUT
+		);
+	dbg( __FUNCTION__ " Send get_FW_version URB returns: %i", result);
+	dbg("Firmware version: %i.%i.%i", transfer_buffer[0], transfer_buffer[1], transfer_buffer[2] );
+
+	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
+		// Setting Baudrate, Parity and Stopbits
+		result = usb_control_msg( port->serial->dev, 
+					  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+					  SUSBCRequest_SetBaudRateParityAndStopBits,
+					  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+					  SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit,
+					  0,
+					  transfer_buffer,
+					  0,
+					  KOBIL_TIMEOUT
+			);
+		dbg( __FUNCTION__ " Send set_baudrate URB returns: %i", result);
+		
+		// reset all queues
+		result = usb_control_msg( port->serial->dev, 
+					  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+					  SUSBCRequest_Misc,
+					  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+					  SUSBCR_MSC_ResetAllQueues,
+					  0,
+					  transfer_buffer,
+					  0,
+					  KOBIL_TIMEOUT
+			);
+		dbg( __FUNCTION__ " Send reset_all_queues URB returns: %i", result);
+	}
+
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID) {
+		// start reading
+		result = usb_submit_urb( port->interrupt_in_urb ); 
+		dbg( __FUNCTION__ " Send read URB returns: %i", result);
+	}
+
+	kfree(transfer_buffer);
+	return 0;
+}
+
+
+static void kobil_close (struct usb_serial_port *port, struct file *filp)
+{
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	--(port->open_count);
+	if (port->open_count <= 0) {
+		if (port->write_urb){
+			usb_unlink_urb( port->write_urb );
+			kfree( port->write_urb->transfer_buffer );
+			usb_free_urb( port->write_urb );
+			port->write_urb = 0;
+		}
+		if (port->interrupt_in_urb){
+			usb_unlink_urb (port->interrupt_in_urb);
+		}
+		port->active = 0;
+		port->open_count = 0;
+	}
+}
+
+
+static void kobil_read_int_callback( struct urb *purb )
+{  
+	int i;
+	struct usb_serial_port *port = (struct usb_serial_port *) purb->context;
+	struct tty_struct *tty;
+	unsigned char *data = purb->transfer_buffer;
+
+	dbg( __FUNCTION__ " - port %d", port->number);
+
+	if (purb->status) {
+		dbg(__FUNCTION__ " - read int status not zero: %d", purb->status);
+		return;
+	}
+	
+	tty = port->tty; 
+	if (purb->actual_length) {
+		for (i = 0; i < purb->actual_length; ++i) {
+			// if we insert more than TTY_FLIPBUF_SIZE characters, we drop them.
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			// this doesn't actually push the data through unless tty->low_latency is set
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+	}
+}
+
+
+static void kobil_write_callback( struct urb *purb )
+{
+}
+
+
+static int kobil_write (struct usb_serial_port *port, int from_user, 
+			const unsigned char *buf, int count)
+{
+	int length = 0;
+	int result = 0;
+	int todo = 0;
+	struct kobil_private * priv;
+
+	priv = (struct kobil_private *) port->private;
+
+	if (count == 0) {
+		dbg( __FUNCTION__ " write request of 0 bytes");
+		return 0;
+	}
+	
+	// check, if we have enough memory left
+	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
+		dbg( __FUNCTION__ " Error: write request bigger than buffer size");
+		return -ENOMEM;
+	}
+
+	down (&port->sem);
+
+	// Copy data to buffer
+	if (from_user) {
+		if (copy_from_user(priv->buf + priv->filled, buf, count)) {
+			up (&port->sem);
+			return -EFAULT;
+		}
+	} else {
+		memcpy (priv->buf + priv->filled, buf, count);
+	}
+
+	priv->filled = priv->filled + count;
+  
+
+	// only send complete block. TWIN and adapter K use the same protocol.
+	if ( ((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || 
+	     ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4))) ) {
+		
+		// stop reading (except TWIN)
+		if (priv->device_type != KOBIL_USBTWIN_PRODUCT_ID) {
+			usb_unlink_urb( port->interrupt_in_urb );
+		}
+
+		todo = priv->filled - priv->cur_pos;
+
+		while(todo > 0) {
+			// max 8 byte in one urb (endpoint size)
+			length = (todo < 8) ? todo : 8;
+			// copy data to transfer buffer
+			memcpy(port->write_urb->transfer_buffer, priv->buf + priv->cur_pos, length );
+			
+			FILL_BULK_URB( port->write_urb,
+				       port->serial->dev, 
+				       usb_sndbulkpipe( port->serial->dev, priv->write_int_endpoint_address),
+				       port->write_urb->transfer_buffer,
+				       length,
+				       kobil_write_callback,
+				       port
+				);
+			priv->cur_pos = priv->cur_pos + length;
+			result = usb_submit_urb( port->write_urb ); 
+			dbg( __FUNCTION__ " %d Send write URB returns: %i", port->number, result);
+			todo = priv->filled - priv->cur_pos;
+
+			if (todo > 0) {
+				//mdelay(16);
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(24 * HZ / 1000);
+			}
+
+		} // end while
+		
+		priv->filled = 0;
+		priv->cur_pos = 0;
+				
+		// start reading (except TWIN)	
+		if (priv->device_type != KOBIL_USBTWIN_PRODUCT_ID) {
+			// someone sets the dev to 0 if the close method has been called
+			port->interrupt_in_urb->dev = port->serial->dev;
+			
+			// start reading
+			result = usb_submit_urb( port->interrupt_in_urb ); 
+			dbg( __FUNCTION__ " %d Send read URB returns: %i", port->number, result);
+		}
+		
+	}
+	up(&port->sem);
+	return count;  
+}
+
+
+static int kobil_write_room (struct usb_serial_port *port)
+{
+	//dbg(__FUNCTION__ " - port %d", port->number);
+	return 8;
+}
+
+
+static int  kobil_ioctl(struct usb_serial_port *port, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct kobil_private * priv;
+	int mask;
+	int result;
+	unsigned short urb_val = 0;
+	unsigned char *transfer_buffer;
+	int transfer_buffer_length = 8;
+	char *settings;
+
+	priv = (struct kobil_private *) port->private;
+	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID) {
+		// This device doesn't support ioctl calls
+		return 0;
+	}
+
+	switch (cmd) {
+	case TCGETS:   // 0x5401
+		result = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct termios));
+		if (result) {
+			dbg( __FUNCTION__ " Error in verify_area");
+			return(result);
+		}
+		kernel_termios_to_user_termios((struct termios *)arg, &priv->internal_termios);
+		return 0;
+
+	case TCSETS:   // 0x5402
+		if (! &port->tty->termios) {
+			dbg( __FUNCTION__ " Error: port->tty->termios is NULL\n");
+			return -ENOTTY;
+		}
+		result = verify_area(VERIFY_READ, (void *)arg, sizeof(struct termios));
+		if (result) {
+			dbg( __FUNCTION__ " Error in verify_area");
+			return result;
+		}
+		user_termios_to_kernel_termios( &priv->internal_termios, (struct termios *)arg);
+		
+		settings = (unsigned char *) kmalloc(50, GFP_KERNEL);  
+		if (! settings) {
+			return -ENOBUFS;
+		}
+		memset(settings, 0, 50);
+
+		switch (priv->internal_termios.c_cflag & CBAUD) {
+		case B1200:
+			urb_val = SUSBCR_SBR_1200;
+			strcat(settings, "1200 ");
+			break;
+		case B9600:
+		default:
+			urb_val = SUSBCR_SBR_9600;
+			strcat(settings, "9600 ");
+			break;
+		}
+
+		urb_val |= (priv->internal_termios.c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : SUSBCR_SPASB_1StopBit;
+		strcat(settings, (priv->internal_termios.c_cflag & CSTOPB) ? "2 StopBits " : "1 StopBit ");
+		
+		if (priv->internal_termios.c_cflag & PARENB) {
+			if  (priv->internal_termios.c_cflag & PARODD) {
+				urb_val |= SUSBCR_SPASB_OddParity;
+				strcat(settings, "Odd Parity");
+			} else {
+				urb_val |= SUSBCR_SPASB_EvenParity;
+				strcat(settings, "Even Parity");
+			}
+		} else {
+			urb_val |= SUSBCR_SPASB_NoParity;
+			strcat(settings, "No Parity");
+		}
+		dbg( __FUNCTION__ " setting port %d to: %s", port->number, settings );
+
+		result = usb_control_msg( port->serial->dev, 
+					  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+					  SUSBCRequest_SetBaudRateParityAndStopBits,
+					  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+					  urb_val,
+					  0,
+					  settings,
+					  0,
+					  KOBIL_TIMEOUT
+			);
+		
+		dbg( __FUNCTION__ " Send set_baudrate URB returns: %i", result);
+		kfree(settings);
+		return 0;
+    
+	case TCFLSH:   // 0x540B
+		transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+		if (! transfer_buffer) {
+		 	return -ENOBUFS;
+		}
+
+		result = usb_control_msg( port->serial->dev, 
+		 			  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+					  SUSBCRequest_Misc,
+					  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+					  SUSBCR_MSC_ResetAllQueues,
+					  0,
+					  NULL,//transfer_buffer,
+					  0,
+					  KOBIL_TIMEOUT
+			);
+		
+		dbg( __FUNCTION__ " %d Send reset_all_queues (FLUSH) URB returns: %i", port->number, result);
+		 
+		kfree(transfer_buffer);
+		return ((result < 0) ? -EFAULT : 0);
+
+	case TIOCMGET: // 0x5415
+		// allocate memory for transfer buffer
+		transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);  
+		if (! transfer_buffer) {
+			return -ENOBUFS;
+		} else {
+			memset(transfer_buffer, 0, transfer_buffer_length);
+		}
+
+		result = usb_control_msg( port->serial->dev, 
+					  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+					  SUSBCRequest_GetStatusLineState,
+					  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
+					  0,
+					  0,
+					  transfer_buffer,
+					  transfer_buffer_length,
+					  KOBIL_TIMEOUT
+			);
+	
+		dbg( __FUNCTION__ " %d Send get_status_line_state (TIOCMGET) URB returns: %i. Statusline: %02x", 
+		     port->number, result, transfer_buffer[0]);
+	
+		if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0) {
+			priv->line_state |= TIOCM_DSR;
+		} else {
+			priv->line_state &= ~TIOCM_DSR; 
+		}
+		
+		kfree(transfer_buffer);
+		return put_user(priv->line_state, (unsigned long *) arg);
+		
+	case TIOCMSET: // 0x5418
+		if (get_user(mask, (unsigned long *) arg)){
+			return -EFAULT;
+		}
+		// allocate memory for transfer buffer
+		transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length, GFP_KERNEL);
+		if (! transfer_buffer) {
+			return -ENOBUFS;
+		} else {
+			memset(transfer_buffer, 0, transfer_buffer_length);
+		}
+
+		if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
+			if ((mask & TIOCM_DTR) != 0){
+				dbg("Setting DTR - port %d", port->number);
+			} else {
+				dbg("Clearing DTR - port %d", port->number);
+			} 
+			result = usb_control_msg( port->serial->dev, 
+						  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+						  SUSBCRequest_SetStatusLinesOrQueues,
+						  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+						  ( ((mask & TIOCM_DTR) != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
+						  0,
+						  transfer_buffer,
+						  0,
+						  KOBIL_TIMEOUT
+				);
+			
+		} else {
+			if ((mask & TIOCM_RTS) != 0){
+				dbg("Setting RTS - port %d", port->number);
+			} else {
+				dbg("Clearing RTS - port %d", port->number);
+			}
+			result = usb_control_msg( port->serial->dev, 
+						  usb_rcvctrlpipe(port->serial->dev, 0 ), 
+						  SUSBCRequest_SetStatusLinesOrQueues,
+						  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+						  (((mask & TIOCM_RTS) != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
+						  0,
+						  transfer_buffer,
+						  0,
+						  KOBIL_TIMEOUT
+				);
+		}
+		dbg( __FUNCTION__ " %d Send set_status_line (TIOCMSET) URB returns: %i", port->number, result);
+	
+		kfree(transfer_buffer);
+		return ((result < 0) ? -EFAULT : 0);
+	}
+	return 0;
+}
+
+
+static int __init kobil_init (void)
+{
+	usb_serial_register (&kobil_device);
+
+	info(DRIVER_VERSION " " DRIVER_AUTHOR);
+	info(DRIVER_DESC);
+
+	return 0;
+}
+
+
+static void __exit kobil_exit (void)
+{
+	usb_serial_deregister (&kobil_device);
+}
+
+module_init(kobil_init);
+module_exit(kobil_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE( "GPL" );
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff -Naur -X dontdiff linux-2.4.18/drivers/usb/serial/kobil_sct.h linux-2.4.18-thomas/drivers/usb/serial/kobil_sct.h
--- linux-2.4.18/drivers/usb/serial/kobil_sct.h	Thu Jan  1 01:00:00 1970
+++ linux-2.4.18-thomas/drivers/usb/serial/kobil_sct.h	Tue Nov  5 12:20:52 2002
@@ -0,0 +1,60 @@
+#define SUSBCRequest_SetBaudRateParityAndStopBits       1
+#define SUSBCR_SBR_MASK				0xFF00
+#define SUSBCR_SBR_1200				0x0100
+#define SUSBCR_SBR_9600				0x0200
+#define SUSBCR_SBR_19200			0x0400
+#define SUSBCR_SBR_28800			0x0800
+#define SUSBCR_SBR_38400			0x1000
+#define SUSBCR_SBR_57600			0x2000
+#define SUSBCR_SBR_115200			0x4000
+
+#define SUSBCR_SPASB_MASK			0x0070
+#define SUSBCR_SPASB_NoParity			0x0010
+#define SUSBCR_SPASB_OddParity			0x0020
+#define SUSBCR_SPASB_EvenParity			0x0040
+
+#define SUSBCR_SPASB_STPMASK			0x0003
+#define SUSBCR_SPASB_1StopBit			0x0001
+#define SUSBCR_SPASB_2StopBits			0x0002
+
+#define SUSBCRequest_SetStatusLinesOrQueues	2
+#define SUSBCR_SSL_SETRTS			0x0001
+#define SUSBCR_SSL_CLRRTS			0x0002
+#define SUSBCR_SSL_SETDTR			0x0004
+#define SUSBCR_SSL_CLRDTR			0x0010
+
+#define SUSBCR_SSL_PURGE_TXABORT		0x0100  // Kill the pending/current writes to the comm port.
+#define SUSBCR_SSL_PURGE_RXABORT		0x0200  // Kill the pending/current reads to the comm port.
+#define SUSBCR_SSL_PURGE_TXCLEAR		0x0400  // Kill the transmit queue if there.
+#define SUSBCR_SSL_PURGE_RXCLEAR		0x0800  // Kill the typeahead buffer if there.
+
+#define SUSBCRequest_GetStatusLineState		4
+#define SUSBCR_GSL_RXCHAR			0x0001  // Any Character received
+#define SUSBCR_GSL_TXEMPTY			0x0004  // Transmitt Queue Empty
+#define SUSBCR_GSL_CTS				0x0008  // CTS changed state
+#define SUSBCR_GSL_DSR				0x0010  // DSR changed state
+#define SUSBCR_GSL_RLSD				0x0020  // RLSD changed state
+#define SUSBCR_GSL_BREAK			0x0040  // BREAK received
+#define SUSBCR_GSL_ERR				0x0080  // Line status error occurred
+#define SUSBCR_GSL_RING				0x0100  // Ring signal detected
+
+#define SUSBCRequest_Misc			8
+#define SUSBCR_MSC_ResetReader			0x0001	// use a predefined reset sequence
+#define SUSBCR_MSC_ResetAllQueues	        0x0002	// use a predefined sequence to reset the internal queues
+
+#define SUSBCRequest_GetMisc			0x10
+#define SUSBCR_MSC_GetFWVersion			0x0001	/* get the firmware version from device,
+							   coded like this 0xHHLLBBPP
+							   with HH = Firmware Version High Byte
+							   LL = Firmware Version Low Byte
+							   BB = Build Number
+							   PP = Further Attributes
+							*/
+
+#define SUSBCR_MSC_GetHWVersion			0x0002	/* get the hardware version from device
+							   coded like this 0xHHLLPPRR
+							   with HH = Software Version High Byte
+							   LL = Software Version Low Byte
+							   PP = Further Attributes
+							   RR = Reserved for the hardware ID 
+							*/

Reply via email to