⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kl5kusb105.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * KLSI KL5KUSB105 chip RS232 converter driver * *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de> * *   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. * * All information about the device was acquired using SniffUSB ans snoopUSB * on Windows98. * It was written out of frustration with the PalmConnect USB Serial adapter * sold by Palm Inc. * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided * information that was not already available. * * It seems that KLSI bought some silicon-design information from ScanLogic,  * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI. * KLSI has firmware available for their devices; it is probable that the * firmware differs from that used by KLSI in their products. If you have an * original KLSI device and can provide some information on it, I would be  * most interested in adding support for it here. If you have any information  * on the protocol used (or find errors in my reverse-engineered stuff), please * let me know. * * The code was only tested with a PalmConnect USB adapter; if you * are adventurous, try it with any KLSI-based device and let me know how it * breaks so that I can fix it! *//* TODO: *	check modem line signals *	implement handshaking or decide that we do not support it *//* History: *   0.3a - implemented pools of write URBs *   0.3  - alpha version for public testing *   0.2  - TIOCMGET works, so autopilot(1) can be used! *   0.1  - can be used to to pilot-xfer -p /dev/ttyUSB0 -l * *   The driver skeleton is mainly based on mct_u232.c and various other  *   pieces of code shamelessly copied from the drivers/usb/serial/ directory. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/module.h>#include <asm/uaccess.h>#include <linux/usb.h>#include "usb-serial.h"#include "kl5kusb105.h"static int debug;/* * Version Information */#define DRIVER_VERSION "v0.3a"#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"/* * Function prototypes */static int  klsi_105_startup	         (struct usb_serial *serial);static void klsi_105_shutdown	         (struct usb_serial *serial);static int  klsi_105_open	         (struct usb_serial_port *port,					  struct file *filp);static void klsi_105_close	         (struct usb_serial_port *port,					  struct file *filp);static int  klsi_105_write	         (struct usb_serial_port *port,					  const unsigned char *buf,					  int count);static void klsi_105_write_bulk_callback (struct urb *urb, struct pt_regs *regs);static int  klsi_105_chars_in_buffer     (struct usb_serial_port *port);static int  klsi_105_write_room          (struct usb_serial_port *port);static void klsi_105_read_bulk_callback  (struct urb *urb, struct pt_regs *regs);static void klsi_105_set_termios         (struct usb_serial_port *port,					  struct termios * old);static int  klsi_105_ioctl	         (struct usb_serial_port *port,					  struct file * file,					  unsigned int cmd,					  unsigned long arg);static void klsi_105_throttle		 (struct usb_serial_port *port);static void klsi_105_unthrottle		 (struct usb_serial_port *port);/*static void klsi_105_break_ctl	         (struct usb_serial_port *port,					  int break_state ); */static int  klsi_105_tiocmget	         (struct usb_serial_port *port,					  struct file *file);static int  klsi_105_tiocmset	         (struct usb_serial_port *port,					  struct file *file, unsigned int set,					  unsigned int clear);/* * All of the device info needed for the KLSI converters. */static struct usb_device_id id_table [] = {	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },	{ USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },	{ }		/* Terminating entry */};MODULE_DEVICE_TABLE (usb, id_table);static struct usb_driver kl5kusb105d_driver = {	.owner =	THIS_MODULE,	.name =		"kl5kusb105d",	.probe =	usb_serial_probe,	.disconnect =	usb_serial_disconnect,	.id_table =	id_table,};static struct usb_serial_driver kl5kusb105d_device = {	.driver = {		.owner =	THIS_MODULE,		.name =		"kl5kusb105d",	},	.description =	     "KL5KUSB105D / PalmConnect",	.id_table =	     id_table,	.num_interrupt_in =  1,	.num_bulk_in =	     1,	.num_bulk_out =	     1,	.num_ports =	     1,	.open =		     klsi_105_open,	.close =	     klsi_105_close,	.write =	     klsi_105_write,	.write_bulk_callback = klsi_105_write_bulk_callback,	.chars_in_buffer =   klsi_105_chars_in_buffer,	.write_room =        klsi_105_write_room,	.read_bulk_callback =klsi_105_read_bulk_callback,	.ioctl =	     klsi_105_ioctl,	.set_termios =	     klsi_105_set_termios,	/*.break_ctl =	     klsi_105_break_ctl,*/	.tiocmget =          klsi_105_tiocmget,	.tiocmset =          klsi_105_tiocmset,	.attach =	     klsi_105_startup,	.shutdown =	     klsi_105_shutdown,	.throttle =	     klsi_105_throttle,	.unthrottle =	     klsi_105_unthrottle,};struct klsi_105_port_settings {	__u8	pktlen;		/* always 5, it seems */	__u8	baudrate;	__u8	databits;	__u8	unknown1;	__u8	unknown2;} __attribute__ ((packed));/* we implement a pool of NUM_URBS urbs per usb_serial */#define NUM_URBS			1#define URB_TRANSFER_BUFFER_SIZE	64struct klsi_105_private {	struct klsi_105_port_settings	cfg;	struct termios			termios;	unsigned long			line_state; /* modem line settings */	/* write pool */	struct urb *			write_urb_pool[NUM_URBS];	spinlock_t			lock;	unsigned long			bytes_in;	unsigned long			bytes_out;};/* * Handle vendor specific USB requests */#define KLSI_TIMEOUT	 5000 /* default urb timeout */static int klsi_105_chg_port_settings(struct usb_serial_port *port,				      struct klsi_105_port_settings *settings){	int rc;        rc = usb_control_msg(port->serial->dev,			     usb_sndctrlpipe(port->serial->dev, 0),			     KL5KUSB105A_SIO_SET_DATA,                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,			     0, /* value */			     0, /* index */			     settings,			     sizeof(struct klsi_105_port_settings),			     KLSI_TIMEOUT);	if (rc < 0)		err("Change port settings failed (error = %d)", rc);	info("%s - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d",	    __FUNCTION__,	    settings->pktlen,	    settings->baudrate, settings->databits,	    settings->unknown1, settings->unknown2);        return rc;} /* klsi_105_chg_port_settings *//* translate a 16-bit status value from the device to linux's TIO bits */static unsigned long klsi_105_status2linestate(const __u16 status){	unsigned long res = 0;	res =   ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)	      | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)	      ;	return res;}/*  * Read line control via vendor command and return result through * *line_state_p  *//* It seems that the status buffer has always only 2 bytes length */#define KLSI_STATUSBUF_LEN	2static int klsi_105_get_line_state(struct usb_serial_port *port,				   unsigned long *line_state_p){	int rc;	__u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1};	__u16 status;	info("%s - sending SIO Poll request", __FUNCTION__);        rc = usb_control_msg(port->serial->dev,			     usb_rcvctrlpipe(port->serial->dev, 0),			     KL5KUSB105A_SIO_POLL,                             USB_TYPE_VENDOR | USB_DIR_IN,			     0, /* value */			     0, /* index */			     status_buf, KLSI_STATUSBUF_LEN,			     10000			     );	if (rc < 0)		err("Reading line status failed (error = %d)", rc);	else {		status = status_buf[0] + (status_buf[1]<<8);		info("%s - read status %x %x", __FUNCTION__,		     status_buf[0], status_buf[1]);		*line_state_p = klsi_105_status2linestate(status);	}        return rc;}/* * Driver's tty interface functions */static int klsi_105_startup (struct usb_serial *serial){	struct klsi_105_private *priv;	int i;	/* check if we support the product id (see keyspan.c)	 * FIXME	 */	/* allocate the private data structure */	for (i=0; i<serial->num_ports; i++) {		int j;		priv = kmalloc(sizeof(struct klsi_105_private),						   GFP_KERNEL);		if (!priv) {			dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__);			return -ENOMEM;		}		/* set initial values for control structures */		priv->cfg.pktlen    = 5;		priv->cfg.baudrate  = kl5kusb105a_sio_b9600;		priv->cfg.databits  = kl5kusb105a_dtb_8;		priv->cfg.unknown1  = 0;		priv->cfg.unknown2  = 1;		priv->line_state    = 0;		priv->bytes_in	    = 0;		priv->bytes_out	    = 0;		usb_set_serial_port_data(serial->port[i], priv);		spin_lock_init (&priv->lock);		for (j=0; j<NUM_URBS; j++) {			struct urb* urb = usb_alloc_urb(0, GFP_KERNEL);			priv->write_urb_pool[j] = urb;			if (urb == NULL) {				err("No more urbs???");				continue;			}			urb->transfer_buffer = NULL;			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,							GFP_KERNEL);			if (!urb->transfer_buffer) {				err("%s - out of memory for urb buffers.", __FUNCTION__);				continue;			}		}		/* priv->termios is left uninitalized until port opening */		init_waitqueue_head(&serial->port[i]->write_wait);	}		return (0);} /* klsi_105_startup */static void klsi_105_shutdown (struct usb_serial *serial){	int i;		dbg("%s", __FUNCTION__);	/* stop reads and writes on all ports */	for (i=0; i < serial->num_ports; ++i) {		struct klsi_105_private *priv = usb_get_serial_port_data(serial->port[i]);		unsigned long flags;		if (priv) {			/* kill our write urb pool */			int j;			struct urb **write_urbs = priv->write_urb_pool;			spin_lock_irqsave(&priv->lock,flags);			for (j = 0; j < NUM_URBS; j++) {				if (write_urbs[j]) {					/* FIXME - uncomment the following					 * usb_kill_urb call when the host					 * controllers get fixed to set					 * urb->dev = NULL after the urb is					 * finished.  Otherwise this call					 * oopses. */					/* usb_kill_urb(write_urbs[j]); */					kfree(write_urbs[j]->transfer_buffer);					usb_free_urb (write_urbs[j]);				}			}			spin_unlock_irqrestore (&priv->lock, flags);			kfree(priv);			usb_set_serial_port_data(serial->port[i], NULL);		}	}} /* klsi_105_shutdown */static int  klsi_105_open (struct usb_serial_port *port, struct file *filp){	struct klsi_105_private *priv = usb_get_serial_port_data(port);	int retval = 0;	int rc;	int i;	unsigned long line_state;	struct klsi_105_port_settings cfg;	unsigned long flags;	dbg("%s port %d", __FUNCTION__, port->number);	/* force low_latency on so that our tty_push actually forces	 * the data through	 * port->tty->low_latency = 1; */	/* Do a defined restart:	 * Set up sane default baud rate and send the 'READ_ON'	 * vendor command. 	 * FIXME: set modem line control (how?)	 * Then read the modem line control and store values in	 * priv->line_state.	 */	cfg.pktlen   = 5;	cfg.baudrate = kl5kusb105a_sio_b9600;	cfg.databits = kl5kusb105a_dtb_8;	cfg.unknown1 = 0;	cfg.unknown2 = 1;	klsi_105_chg_port_settings(port, &cfg);		/* set up termios structure */	spin_lock_irqsave (&priv->lock, flags);	priv->termios.c_iflag = port->tty->termios->c_iflag;	priv->termios.c_oflag = port->tty->termios->c_oflag;	priv->termios.c_cflag = port->tty->termios->c_cflag;	priv->termios.c_lflag = port->tty->termios->c_lflag;	for (i=0; i<NCCS; i++)		priv->termios.c_cc[i] = port->tty->termios->c_cc[i];	priv->cfg.pktlen   = cfg.pktlen;	priv->cfg.baudrate = cfg.baudrate;	priv->cfg.databits = cfg.databits;	priv->cfg.unknown1 = cfg.unknown1;	priv->cfg.unknown2 = cfg.unknown2;	spin_unlock_irqrestore (&priv->lock, flags);	/* READ_ON and urb submission */	usb_fill_bulk_urb(port->read_urb, port->serial->dev, 		      usb_rcvbulkpipe(port->serial->dev,				      port->bulk_in_endpointAddress),		      port->read_urb->transfer_buffer,		      port->read_urb->transfer_buffer_length,		      klsi_105_read_bulk_callback,		      port);	rc = usb_submit_urb(port->read_urb, GFP_KERNEL);	if (rc) {		err("%s - failed submitting read urb, error %d", __FUNCTION__, rc);		retval = rc;		goto exit;	}	rc = usb_control_msg(port->serial->dev,			     usb_sndctrlpipe(port->serial->dev,0),			     KL5KUSB105A_SIO_CONFIGURE,			     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,			     KL5KUSB105A_SIO_CONFIGURE_READ_ON,			     0, /* index */			     NULL,			     0,			     KLSI_TIMEOUT);	if (rc < 0) {		err("Enabling read failed (error = %d)", rc);		retval = rc;	} else 		dbg("%s - enabled reading", __FUNCTION__);	rc = klsi_105_get_line_state(port, &line_state);	if (rc >= 0) {		spin_lock_irqsave (&priv->lock, flags);		priv->line_state = line_state;		spin_unlock_irqrestore (&priv->lock, flags);		dbg("%s - read line state 0x%lx", __FUNCTION__, line_state);		retval = 0;	} else		retval = rc;exit:	return retval;} /* klsi_105_open */static void klsi_105_close (struct usb_serial_port *port, struct file *filp){	struct klsi_105_private *priv = usb_get_serial_port_data(port);	int rc;	dbg("%s port %d", __FUNCTION__, port->number);	/* send READ_OFF */	rc = usb_control_msg (port->serial->dev,			      usb_sndctrlpipe(port->serial->dev, 0),			      KL5KUSB105A_SIO_CONFIGURE,			      USB_TYPE_VENDOR | USB_DIR_OUT,			      KL5KUSB105A_SIO_CONFIGURE_READ_OFF,			      0, /* index */			      NULL, 0,			      KLSI_TIMEOUT);	if (rc < 0)		    err("Disabling read failed (error = %d)", rc);	/* shutdown our bulk reads and writes */	usb_kill_urb(port->write_urb);	usb_kill_urb(port->read_urb);	/* unlink our write pool */	/* FIXME */	/* wgg - do I need this? I think so. */	usb_kill_urb(port->interrupt_in_urb);	info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);} /* klsi_105_close *//* We need to write a complete 64-byte data block and encode the * number actually sent in the first double-byte, LSB-order. That  * leaves at most 62 bytes of payload. */#define KLSI_105_DATA_OFFSET	2   /* in the bulk urb data block */static int klsi_105_write (struct usb_serial_port *port,			   const unsigned char *buf, int count){	struct klsi_105_private *priv = usb_get_serial_port_data(port);	int result, size;	int bytes_sent=0;	dbg("%s - port %d", __FUNCTION__, port->number);	while (count > 0) {		/* try to find a free urb (write 0 bytes if none) */		struct urb *urb = NULL;		unsigned long flags;		int i;		/* since the pool is per-port we might not need the spin lock !? */		spin_lock_irqsave (&priv->lock, flags);		for (i=0; i<NUM_URBS; i++) {			if (priv->write_urb_pool[i]->status != -EINPROGRESS) {				urb = priv->write_urb_pool[i];				dbg("%s - using pool URB %d", __FUNCTION__, i);				break;			}		}		spin_unlock_irqrestore (&priv->lock, flags);		if (urb==NULL) {			dbg("%s - no more free urbs", __FUNCTION__);			goto exit;		}		if (urb->transfer_buffer == NULL) {			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -