📄 kl5kusb105.c
字号:
/* * 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/sched.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/slab.h>/*#include <linux/fcntl.h>*/#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/module.h>/*#include <linux/spinlock.h>*/#include <linux/usb.h>#ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1;#else static int debug;#endif#include "usb-serial.h"#include "kl5kusb105.h"/* * 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, int from_user, const unsigned char *buf, int count);static void klsi_105_write_bulk_callback (struct urb *urb);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);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 ); *//* * All of the device info needed for the MCT USB-RS232 converter. */static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) }, { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) }, { } /* Terminating entry */};static __devinitdata struct usb_device_id palmconnect_table [] = { { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) }, { } /* Terminating entry */};static __devinitdata struct usb_device_id kl5kusb105d_table [] = { { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE (usb, id_table_combined);static struct usb_serial_device_type palmconnect_device = { name: "PalmConnect USB Serial", id_table: palmconnect_table, needs_interrupt_in: MUST_HAVE, /* 1 interrupt-in endpoints */ needs_bulk_in: MUST_HAVE, /* 1 bulk-in endpoint */ needs_bulk_out: MUST_HAVE, /* 1 bulk-out endpoint */ 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,*/ startup: klsi_105_startup, shutdown: klsi_105_shutdown, throttle: klsi_105_throttle, unthrottle: klsi_105_unthrottle,};static struct usb_serial_device_type kl5kusb105d_device = { name: "generic KL5KUSB105D USB->Serial", id_table: kl5kusb105d_table, needs_interrupt_in: MUST_HAVE, /* 1 interrupt-in endpoints */ needs_bulk_in: MUST_HAVE, /* 1 bulk-in endpoint */ needs_bulk_out: MUST_HAVE, /* 1 bulk-out endpoint */ 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,*/ startup: 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 write_urb_pool_lock; unsigned long bytes_in; unsigned long bytes_out;};/* * Handle vendor specific USB requests */#define KLSI_TIMEOUT (HZ * 5 ) /* default urb timeout */static int klsi_105_chg_port_settings(struct usb_serial *serial, struct klsi_105_port_settings *settings){ int rc; rc = usb_control_msg(serial->dev, usb_sndctrlpipe(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(__FUNCTION__ " - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d", 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 *serial, unsigned long *line_state_p){ int rc; __u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1}; __u16 status; info(__FUNCTION__ " - sending SIO Poll request"); rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), KL5KUSB105A_SIO_POLL, USB_TYPE_VENDOR | USB_DIR_IN, 0, /* value */ 0, /* index */ status_buf, KLSI_STATUSBUF_LEN, 10*HZ ); if (rc < 0) err("Reading line status failed (error = %d)", rc); else { status = status_buf[0] + (status_buf[1]<<8); info(__FUNCTION__ " - read status %x %x", 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++) { serial->port[i].private = kmalloc(sizeof(struct klsi_105_private), GFP_KERNEL); if (!serial->port[i].private) { dbg(__FUNCTION__ "kmalloc for klsi_105_private failed."); return (-1); /* error */ } priv = (struct klsi_105_private *)serial->port[i].private; /* 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; spin_lock_init (&priv->write_urb_pool_lock); for (i=0; i<NUM_URBS; i++) { struct urb* urb = usb_alloc_urb(0); priv->write_urb_pool[i] = 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 (__FUNCTION__ " - out of memory for urb buffers."); 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 (__FUNCTION__); /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { struct klsi_105_private *priv = (struct klsi_105_private*) serial->port[i].private; unsigned long flags; while (serial->port[i].open_count > 0) { klsi_105_close (&serial->port[i], NULL); } if (priv) { /* kill our write urb pool */ int j; struct urb **write_urbs = priv->write_urb_pool; spin_lock_irqsave(&priv->write_urb_pool_lock,flags); for (j = 0; j < NUM_URBS; j++) { if (write_urbs[j]) { /* FIXME - uncomment the following * usb_unlink_urb call when the host * controllers get fixed to set * urb->dev = NULL after the urb is * finished. Otherwise this call
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -