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

📄 oti6858.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -