📄 oti6858.c
字号:
/* * Ours Technology Inc. OTi-6858 USB to serial adapter driver. * * Copyleft (C) 2007 Kees Lemmens (adapted for kernel 2.6.20) * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail) * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * * Many thanks to the authors of pl2303 driver: all functions in this file * are heavily based on pl2303 code, buffering code is a 1-to-1 copy. * * Warning! You use this driver on your own risk! The only official * description of this device I have is datasheet from manufacturer, * and it doesn't contain almost any information needed to write a driver. * Almost all knowlegde used while writing this driver was gathered by: * - analyzing traffic between device and the M$ Windows 2000 driver, * - trying different bit combinations and checking pin states * with a voltmeter, * - receiving malformed frames and producing buffer overflows * to learn how errors are reported, * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE * CONNECTED TO IT! * * 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. * * See Documentation/usb/usb-serial.txt for more information on using this driver * * TODO: * - implement correct flushing for ioctls and oti6858_close() * - check how errors (rx overflow, parity error, framing error) are reported * - implement oti6858_break_ctl() * - implement more ioctls * - test/implement flow control * - allow setting custom baud rates */#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/serial.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/spinlock.h>#include <linux/usb.h>#include <linux/usb/serial.h>#include <asm/uaccess.h>#include "oti6858.h"#define OTI6858_DESCRIPTION \ "Ours Technology Inc. OTi-6858 USB to serial adapter driver"#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"#define OTI6858_VERSION "0.1"static struct usb_device_id id_table [] = { { USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) }, { }};MODULE_DEVICE_TABLE(usb, id_table);static struct usb_driver oti6858_driver = { .name = "oti6858", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, .no_dynamic_id = 1,};static int debug;/* buffering code, copied from pl2303 driver */#define PL2303_BUF_SIZE 1024#define PL2303_TMP_BUF_SIZE 1024struct pl2303_buf { unsigned int buf_size; char *buf_buf; char *buf_get; char *buf_put;};/* requests */#define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00)#define OTI6858_REQ_T_GET_STATUS 0x01#define OTI6858_REQ_SET_LINE (USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)#define OTI6858_REQ_T_SET_LINE 0x00#define OTI6858_REQ_CHECK_TXBUFF (USB_DIR_IN | USB_TYPE_VENDOR | 0x01)#define OTI6858_REQ_T_CHECK_TXBUFF 0x00/* format of the control packet */struct oti6858_control_pkt { u16 divisor; /* baud rate = 96000000 / (16 * divisor), LE */#define OTI6858_MAX_BAUD_RATE 3000000 u8 frame_fmt;#define FMT_STOP_BITS_MASK 0xc0#define FMT_STOP_BITS_1 0x00#define FMT_STOP_BITS_2 0x40 /* 1.5 stop bits if FMT_DATA_BITS_5 */#define FMT_PARITY_MASK 0x38#define FMT_PARITY_NONE 0x00#define FMT_PARITY_ODD 0x08#define FMT_PARITY_EVEN 0x18#define FMT_PARITY_MARK 0x28#define FMT_PARITY_SPACE 0x38#define FMT_DATA_BITS_MASK 0x03#define FMT_DATA_BITS_5 0x00#define FMT_DATA_BITS_6 0x01#define FMT_DATA_BITS_7 0x02#define FMT_DATA_BITS_8 0x03 u8 something; /* always equals 0x43 */ u8 control; /* settings of flow control lines */#define CONTROL_MASK 0x0c#define CONTROL_DTR_HIGH 0x08#define CONTROL_RTS_HIGH 0x04 u8 tx_status;#define TX_BUFFER_EMPTIED 0x09 u8 pin_state;#define PIN_MASK 0x3f#define PIN_RTS 0x20 /* output pin */#define PIN_CTS 0x10 /* input pin, active low */#define PIN_DSR 0x08 /* input pin, active low */#define PIN_DTR 0x04 /* output pin */#define PIN_RI 0x02 /* input pin, active low */#define PIN_DCD 0x01 /* input pin, active low */ u8 rx_bytes_avail; /* number of bytes in rx buffer */;};#define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt)#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \ ( ((a)->divisor == (priv)->pending_setup.divisor) \ && ((a)->control == (priv)->pending_setup.control) \ && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) )/* function prototypes */static int oti6858_open(struct usb_serial_port *port, struct file *filp);static void oti6858_close(struct usb_serial_port *port, struct file *filp);static void oti6858_set_termios(struct usb_serial_port *port, struct ktermios *old);static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg);static void oti6858_read_int_callback(struct urb *urb);static void oti6858_read_bulk_callback(struct urb *urb);static void oti6858_write_bulk_callback(struct urb *urb);static int oti6858_write(struct usb_serial_port *port, const unsigned char *buf, int count);static int oti6858_write_room(struct usb_serial_port *port);static void oti6858_break_ctl(struct usb_serial_port *port, int break_state);static int oti6858_chars_in_buffer(struct usb_serial_port *port);static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file);static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);static int oti6858_startup(struct usb_serial *serial);static void oti6858_shutdown(struct usb_serial *serial);/* functions operating on buffers */static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);static void pl2303_buf_free(struct pl2303_buf *pb);static void pl2303_buf_clear(struct pl2303_buf *pb);static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, unsigned int count);static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, unsigned int count);/* device info */static struct usb_serial_driver oti6858_device = { .driver = { .owner = THIS_MODULE, .name = "oti6858", }, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, .num_bulk_out = 1, .num_ports = 1, .open = oti6858_open, .close = oti6858_close, .write = oti6858_write, .ioctl = oti6858_ioctl, .break_ctl = oti6858_break_ctl, .set_termios = oti6858_set_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, .read_bulk_callback = oti6858_read_bulk_callback, .read_int_callback = oti6858_read_int_callback, .write_bulk_callback = oti6858_write_bulk_callback, .write_room = oti6858_write_room, .chars_in_buffer = oti6858_chars_in_buffer, .attach = oti6858_startup, .shutdown = oti6858_shutdown,};struct oti6858_private { spinlock_t lock; struct pl2303_buf *buf; struct oti6858_control_pkt status; struct { u8 read_urb_in_use; u8 write_urb_in_use; u8 termios_initialized; } flags; struct delayed_work delayed_write_work; struct { u16 divisor; u8 frame_fmt; u8 control; } pending_setup; u8 transient; u8 setup_done; struct delayed_work delayed_setup_work; wait_queue_head_t intr_wait; struct usb_serial_port *port; /* USB port with which associated */};#undef dbg/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg)static void setup_line(struct work_struct *work){ struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_setup_work.work); struct usb_serial_port *port = priv->port; struct oti6858_control_pkt *new_setup; unsigned long flags; int result; dbg("%s(port = %d)", __FUNCTION__, port->number); if ((new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__); /* we will try again */ schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); return; } result = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), OTI6858_REQ_T_GET_STATUS, OTI6858_REQ_GET_STATUS, 0, 0, new_setup, OTI6858_CTRL_PKT_SIZE, 100); if (result != OTI6858_CTRL_PKT_SIZE) { dev_err(&port->dev, "%s(): error reading status\n", __FUNCTION__); kfree(new_setup); /* we will try again */ schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); return; } spin_lock_irqsave(&priv->lock, flags); if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) { new_setup->divisor = priv->pending_setup.divisor; new_setup->control = priv->pending_setup.control; new_setup->frame_fmt = priv->pending_setup.frame_fmt; spin_unlock_irqrestore(&priv->lock, flags); result = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), OTI6858_REQ_T_SET_LINE, OTI6858_REQ_SET_LINE, 0, 0, new_setup, OTI6858_CTRL_PKT_SIZE, 100); } else { spin_unlock_irqrestore(&priv->lock, flags); result = 0; } kfree(new_setup); spin_lock_irqsave(&priv->lock, flags); if (result != OTI6858_CTRL_PKT_SIZE) priv->transient = 0; priv->setup_done = 1; spin_unlock_irqrestore(&priv->lock, flags); dbg("%s(): submitting interrupt urb", __FUNCTION__); port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __FUNCTION__, result); }}void send_data(struct work_struct *work){ struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work); struct usb_serial_port *port = priv->port; int count = 0, result; unsigned long flags; unsigned char allow; dbg("%s(port = %d)", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); if (priv->flags.write_urb_in_use) { spin_unlock_irqrestore(&priv->lock, flags); schedule_delayed_work(&priv->delayed_write_work, msecs_to_jiffies(2)); return; } priv->flags.write_urb_in_use = 1; count = pl2303_buf_data_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); if (count > port->bulk_out_size) count = port->bulk_out_size; if (count != 0) { result = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), OTI6858_REQ_T_CHECK_TXBUFF, OTI6858_REQ_CHECK_TXBUFF, count, 0, &allow, 1, 100); if (result != 1 || allow != 0) count = 0; } if (count == 0) { priv->flags.write_urb_in_use = 0; dbg("%s(): submitting interrupt urb", __FUNCTION__); port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __FUNCTION__, result); } return; } spin_lock_irqsave(&priv->lock, flags); pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, count); spin_unlock_irqrestore(&priv->lock, flags); port->write_urb->transfer_buffer_length = count; port->write_urb->dev = port->serial->dev; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result != 0) { dev_err(&port->dev, "%s(): usb_submit_urb() failed" " with error %d\n", __FUNCTION__, result); priv->flags.write_urb_in_use = 0; } usb_serial_port_softint(port);}static int oti6858_startup(struct usb_serial *serial){ struct usb_serial_port *port = serial->port[0]; struct oti6858_private *priv; int i; for (i = 0; i < serial->num_ports; ++i) { priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL); if (!priv) break; priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); if (priv->buf == NULL) { kfree(priv); break; } spin_lock_init(&priv->lock); init_waitqueue_head(&priv->intr_wait);// INIT_WORK(&priv->setup_work, setup_line, serial->port[i]);// INIT_WORK(&priv->write_work, send_data, serial->port[i]); priv->port = port; INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); usb_set_serial_port_data(serial->port[i], priv); } if (i == serial->num_ports) return 0; for (--i; i >= 0; --i) { priv = usb_get_serial_port_data(serial->port[i]); pl2303_buf_free(priv->buf); kfree(priv); usb_set_serial_port_data(serial->port[i], NULL); } return -ENOMEM;}static int oti6858_write(struct usb_serial_port *port, const unsigned char *buf, int count){ struct oti6858_private *priv = usb_get_serial_port_data(port); unsigned long flags; dbg("%s(port = %d, count = %d)", __FUNCTION__, port->number, count); if (!count) return count; spin_lock_irqsave(&priv->lock, flags); count = pl2303_buf_put(priv->buf, buf, count); spin_unlock_irqrestore(&priv->lock, flags); return count;}static int oti6858_write_room(struct usb_serial_port *port){ struct oti6858_private *priv = usb_get_serial_port_data(port); int room = 0; unsigned long flags; dbg("%s(port = %d)", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); room = pl2303_buf_space_avail(priv->buf); spin_unlock_irqrestore(&priv->lock, flags); return room;}static int oti6858_chars_in_buffer(struct usb_serial_port *port){ struct oti6858_private *priv = usb_get_serial_port_data(port); int chars = 0; unsigned long flags; dbg("%s(port = %d)", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -