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

📄 cypress_m8.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * USB Cypress M8 driver * * 	Copyright (C) 2004 * 	    Lonnie Mendez (dignome@gmail.com)  *	Copyright (C) 2003,2004 *	    Neil Whelchel (koyama@firstlight.net) * * 	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. * * See Documentation/usb/usb-serial.txt for more information on using this driver * * See http://geocities.com/i0xox0i for information on this driver and the * earthmate usb device. * *  Lonnie Mendez <dignome@gmail.com> *  4-29-2005 *	Fixed problem where setting or retreiving the serial config would fail with * 	EPIPE.  Removed CRTS toggling so the driver behaves more like other usbserial * 	adapters.  Issued new interval of 1ms instead of the default 10ms.  As a * 	result, transfer speed has been substantially increased.  From avg. 850bps to * 	avg. 3300bps.  initial termios has also been modified.  Cleaned up code and * 	formatting issues so it is more readable.  Replaced the C++ style comments. * *  Lonnie Mendez <dignome@gmail.com> *  12-15-2004 *	Incorporated write buffering from pl2303 driver.  Fixed bug with line *	handling so both lines are raised in cypress_open. (was dropping rts) *      Various code cleanups made as well along with other misc bug fixes. * *  Lonnie Mendez <dignome@gmail.com> *  04-10-2004 *	Driver modified to support dynamic line settings.  Various improvments *      and features. * *  Neil Whelchel *  10-2003 *	Driver first released. * *//* Thanks to Neil Whelchel for writing the first cypress m8 implementation for linux. *//* Thanks to cypress for providing references for the hid reports. *//* Thanks to Jiang Zhang for providing links and for general help. *//* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */#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 <linux/moduleparam.h>#include <linux/spinlock.h>#include <linux/usb.h>#include <linux/serial.h>#include <linux/delay.h>#include <asm/uaccess.h>#include "usb-serial.h"#include "cypress_m8.h"#ifdef CONFIG_USB_SERIAL_DEBUG	static int debug = 1;#else	static int debug;#endifstatic int stats;static int interval;/* * Version Information */#define DRIVER_VERSION "v1.09"#define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"#define DRIVER_DESC "Cypress USB to Serial Driver"/* write buffer size defines */#define CYPRESS_BUF_SIZE	1024#define CYPRESS_CLOSING_WAIT	(30*HZ)static struct usb_device_id id_table_earthmate [] = {	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },	{ }						/* Terminating entry */};static struct usb_device_id id_table_cyphidcomrs232 [] = {	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },	{ }						/* Terminating entry */};static struct usb_device_id id_table_combined [] = {	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },	{ USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB_LT20) },	{ USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },	{ }						/* Terminating entry */};MODULE_DEVICE_TABLE (usb, id_table_combined);static struct usb_driver cypress_driver = {	.name =		"cypress",	.probe =	usb_serial_probe,	.disconnect =	usb_serial_disconnect,	.id_table =	id_table_combined,};struct cypress_private {	spinlock_t lock;		   /* private lock */	int chiptype;			   /* identifier of device, for quirks/etc */	int bytes_in;			   /* used for statistics */	int bytes_out;			   /* used for statistics */	int cmd_count;			   /* used for statistics */	int cmd_ctrl;			   /* always set this to 1 before issuing a command */	struct cypress_buf *buf;	   /* write buffer */	int write_urb_in_use;		   /* write urb in use indicator */	int termios_initialized;	__u8 line_control;	   	   /* holds dtr / rts value */	__u8 current_status;	   	   /* received from last read - info on dsr,cts,cd,ri,etc */	__u8 current_config;	   	   /* stores the current configuration byte */	__u8 rx_flags;			   /* throttling - used from whiteheat/ftdi_sio */	int baud_rate;			   /* stores current baud rate in integer form */	int cbr_mask;			   /* stores current baud rate in masked form */	int isthrottled;		   /* if throttled, discard reads */	wait_queue_head_t delta_msr_wait;  /* used for TIOCMIWAIT */	char prev_status, diff_status;	   /* used for TIOCMIWAIT */	/* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */	struct termios tmp_termios; 	   /* stores the old termios settings */};/* write buffer structure */struct cypress_buf {	unsigned int	buf_size;	char		*buf_buf;	char		*buf_get;	char		*buf_put;};/* function prototypes for the Cypress USB to serial device */static int  cypress_earthmate_startup	(struct usb_serial *serial);static int  cypress_hidcom_startup	(struct usb_serial *serial);static void cypress_shutdown		(struct usb_serial *serial);static int  cypress_open		(struct usb_serial_port *port, struct file *filp);static void cypress_close		(struct usb_serial_port *port, struct file *filp);static int  cypress_write		(struct usb_serial_port *port, const unsigned char *buf, int count);static void cypress_send		(struct usb_serial_port *port);static int  cypress_write_room		(struct usb_serial_port *port);static int  cypress_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);static void cypress_set_termios		(struct usb_serial_port *port, struct termios * old);static int  cypress_tiocmget		(struct usb_serial_port *port, struct file *file);static int  cypress_tiocmset		(struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);static int  cypress_chars_in_buffer	(struct usb_serial_port *port);static void cypress_throttle		(struct usb_serial_port *port);static void cypress_unthrottle		(struct usb_serial_port *port);static void cypress_read_int_callback	(struct urb *urb, struct pt_regs *regs);static void cypress_write_int_callback	(struct urb *urb, struct pt_regs *regs);/* baud helper functions */static int	 mask_to_rate		(unsigned mask);static unsigned  rate_to_mask		(int rate);/* write buffer functions */static struct cypress_buf *cypress_buf_alloc(unsigned int size);static void 		  cypress_buf_free(struct cypress_buf *cb);static void		  cypress_buf_clear(struct cypress_buf *cb);static unsigned int	  cypress_buf_data_avail(struct cypress_buf *cb);static unsigned int	  cypress_buf_space_avail(struct cypress_buf *cb);static unsigned int	  cypress_buf_put(struct cypress_buf *cb, const char *buf, unsigned int count);static unsigned int	  cypress_buf_get(struct cypress_buf *cb, char *buf, unsigned int count);static struct usb_serial_driver cypress_earthmate_device = {	.driver = {		.owner =		THIS_MODULE,		.name =			"earthmate",	},	.description =			"DeLorme Earthmate USB",	.id_table =			id_table_earthmate,	.num_interrupt_in = 		1,	.num_interrupt_out =		1,	.num_bulk_in =			NUM_DONT_CARE,	.num_bulk_out =			NUM_DONT_CARE,	.num_ports =			1,	.attach =			cypress_earthmate_startup,	.shutdown =			cypress_shutdown,	.open =				cypress_open,	.close =			cypress_close,	.write =			cypress_write,	.write_room =			cypress_write_room,	.ioctl =			cypress_ioctl,	.set_termios =			cypress_set_termios,	.tiocmget =			cypress_tiocmget,	.tiocmset =			cypress_tiocmset,	.chars_in_buffer =		cypress_chars_in_buffer,	.throttle =		 	cypress_throttle,	.unthrottle =			cypress_unthrottle,	.read_int_callback =		cypress_read_int_callback,	.write_int_callback =		cypress_write_int_callback,};static struct usb_serial_driver cypress_hidcom_device = {	.driver = {		.owner =		THIS_MODULE,		.name =			"cyphidcom",	},	.description =			"HID->COM RS232 Adapter",	.id_table =			id_table_cyphidcomrs232,	.num_interrupt_in =		1,	.num_interrupt_out =		1,	.num_bulk_in =			NUM_DONT_CARE,	.num_bulk_out =			NUM_DONT_CARE,	.num_ports =			1,	.attach =			cypress_hidcom_startup,	.shutdown =			cypress_shutdown,	.open =				cypress_open,	.close =			cypress_close,	.write =			cypress_write,	.write_room =			cypress_write_room,	.ioctl =			cypress_ioctl,	.set_termios =			cypress_set_termios,	.tiocmget =			cypress_tiocmget,	.tiocmset =			cypress_tiocmset,	.chars_in_buffer =		cypress_chars_in_buffer,	.throttle =			cypress_throttle,	.unthrottle =			cypress_unthrottle,	.read_int_callback =		cypress_read_int_callback,	.write_int_callback =		cypress_write_int_callback,};/***************************************************************************** * Cypress serial helper functions *****************************************************************************//* This function can either set or retrieve the current serial line settings */static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_mask, int data_bits, int stop_bits,				   int parity_enable, int parity_type, int reset, int cypress_request_type){	int new_baudrate = 0, retval = 0, tries = 0;	struct cypress_private *priv;	__u8 feature_buffer[8];	unsigned long flags;	dbg("%s", __FUNCTION__);		priv = usb_get_serial_port_data(port);	switch(cypress_request_type) {		case CYPRESS_SET_CONFIG:			/*			 * The general purpose firmware for the Cypress M8 allows for a maximum speed 			 * of 57600bps (I have no idea whether DeLorme chose to use the general purpose			 * firmware or not), if you need to modify this speed setting for your own			 * project please add your own chiptype and modify the code likewise.  The			 * Cypress HID->COM device will work successfully up to 115200bps (but the			 * actual throughput is around 3kBps).			 */			if (baud_mask != priv->cbr_mask) {				dbg("%s - baud rate is changing", __FUNCTION__);				if ( priv->chiptype == CT_EARTHMATE ) {					/* 300 and 600 baud rates are supported under the generic firmware,					 * but are not used with NMEA and SiRF protocols */										if ( (baud_mask == B300) || (baud_mask == B600) ) {						err("%s - failed setting baud rate, unsupported speed",						    __FUNCTION__);						new_baudrate = priv->baud_rate;					} else if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {						err("%s - failed setting baud rate, unsupported speed",						    __FUNCTION__);						new_baudrate = priv->baud_rate;					}				} else if (priv->chiptype == CT_CYPHIDCOM) {					if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {						err("%s - failed setting baud rate, unsupported speed",						    __FUNCTION__);						new_baudrate = priv->baud_rate;					}				} else if (priv->chiptype == CT_GENERIC) {					if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {						err("%s - failed setting baud rate, unsupported speed",						    __FUNCTION__);						new_baudrate = priv->baud_rate;					}				} else {					info("%s - please define your chiptype", __FUNCTION__);					new_baudrate = priv->baud_rate;				}			} else {  /* baud rate not changing, keep the old */				new_baudrate = priv->baud_rate;			}			dbg("%s - baud rate is being sent as %d", __FUNCTION__, new_baudrate);						memset(feature_buffer, 0, 8);			/* fill the feature_buffer with new configuration */			*((u_int32_t *)feature_buffer) = new_baudrate;			feature_buffer[4] |= data_bits;   /* assign data bits in 2 bit space ( max 3 ) */			/* 1 bit gap */			feature_buffer[4] |= (stop_bits << 3);   /* assign stop bits in 1 bit space */			feature_buffer[4] |= (parity_enable << 4);   /* assign parity flag in 1 bit space */			feature_buffer[4] |= (parity_type << 5);   /* assign parity type in 1 bit space */			/* 1 bit gap */			feature_buffer[4] |= (reset << 7);   /* assign reset at end of byte, 1 bit space */							dbg("%s - device is being sent this feature report:", __FUNCTION__);			dbg("%s - %02X - %02X - %02X - %02X - %02X", __FUNCTION__, feature_buffer[0], feature_buffer[1],		            feature_buffer[2], feature_buffer[3], feature_buffer[4]);						do {			retval = usb_control_msg (port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),					  	  HID_REQ_SET_REPORT, USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,						  	  0x0300, 0, feature_buffer, 8, 500);				if (tries++ >= 3)					break;				if (retval == EPIPE)					usb_clear_halt(port->serial->dev, 0x00);			} while (retval != 8 && retval != ENODEV);			if (retval != 8)				err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);			else {				spin_lock_irqsave(&priv->lock, flags);				priv->baud_rate = new_baudrate;				priv->cbr_mask = baud_mask;				priv->current_config = feature_buffer[4];				spin_unlock_irqrestore(&priv->lock, flags);			}		break;		case CYPRESS_GET_CONFIG:			dbg("%s - retreiving serial line settings", __FUNCTION__);			/* set initial values in feature buffer */			memset(feature_buffer, 0, 8);			do {			retval = usb_control_msg (port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),						  HID_REQ_GET_REPORT, USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,							  0x0300, 0, feature_buffer, 8, 500);								if (tries++ >= 3)					break;				if (retval == EPIPE)					usb_clear_halt(port->serial->dev, 0x00);			} while (retval != 5 && retval != ENODEV);			if (retval != 5) {				err("%s - failed to retreive serial line settings - %d", __FUNCTION__, retval);				return retval;			} else {				spin_lock_irqsave(&priv->lock, flags);				/* store the config in one byte, and later use bit masks to check values */				priv->current_config = feature_buffer[4];				priv->baud_rate = *((u_int32_t *)feature_buffer);								if ( (priv->cbr_mask = rate_to_mask(priv->baud_rate)) == 0x40)					dbg("%s - failed setting the baud mask (not defined)", __FUNCTION__);				spin_unlock_irqrestore(&priv->lock, flags);			}	}	spin_lock_irqsave(&priv->lock, flags);	++priv->cmd_count;	spin_unlock_irqrestore(&priv->lock, flags);	return retval;} /* cypress_serial_control *//* given a baud mask, it will return integer baud on success */static int mask_to_rate (unsigned mask){	int rate;	switch (mask) {		case B0: rate = 0; break;		case B300: rate = 300; break;		case B600: rate = 600; break;		case B1200: rate = 1200; break;		case B2400: rate = 2400; break;		case B4800: rate = 4800; break;		case B9600: rate = 9600; break;		case B19200: rate = 19200; break;		case B38400: rate = 38400; break;		case B57600: rate = 57600; break;		case B115200: rate = 115200; break;		default: rate = -1;	}	return rate;}static unsigned rate_to_mask (int rate){	unsigned mask;	switch (rate) {		case 0: mask = B0; break;		case 300: mask = B300; break;		case 600: mask = B600; break;		case 1200: mask = B1200; break;		case 2400: mask = B2400; break;		case 4800: mask = B4800; break;		case 9600: mask = B9600; break;		case 19200: mask = B19200; break;		case 38400: mask = B38400; break;		case 57600: mask = B57600; break;		case 115200: mask = B115200; break;		default: mask = 0x40;	}	return mask;}/***************************************************************************** * Cypress serial driver functions *****************************************************************************/static int generic_startup (struct usb_serial *serial){	struct cypress_private *priv;	dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);	priv = kmalloc(sizeof (struct cypress_private), GFP_KERNEL);	if (!priv)		return -ENOMEM;	memset(priv, 0x00, sizeof (struct cypress_private));	spin_lock_init(&priv->lock);	priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);	if (priv->buf == NULL) {		kfree(priv);		return -ENOMEM;	}	init_waitqueue_head(&priv->delta_msr_wait);		usb_reset_configuration (serial->dev);		interval = 1;	priv->cmd_ctrl = 0;	priv->line_control = 0;	priv->termios_initialized = 0;	priv->rx_flags = 0;	priv->cbr_mask = B300;	usb_set_serial_port_data(serial->port[0], priv);		return 0;}static int cypress_earthmate_startup (struct usb_serial *serial){	struct cypress_private *priv;	dbg("%s", __FUNCTION__);	if (generic_startup(serial)) {		dbg("%s - Failed setting up port %d", __FUNCTION__,				serial->port[0]->number);		return 1;	}	priv = usb_get_serial_port_data(serial->port[0]);	priv->chiptype = CT_EARTHMATE;	return 0;} /* cypress_earthmate_startup */static int cypress_hidcom_startup (struct usb_serial *serial){	struct cypress_private *priv;	dbg("%s", __FUNCTION__);	if (generic_startup(serial)) {		dbg("%s - Failed setting up port %d", __FUNCTION__,				serial->port[0]->number);		return 1;	}	priv = usb_get_serial_port_data(serial->port[0]);	priv->chiptype = CT_CYPHIDCOM;		return 0;} /* cypress_hidcom_startup */static void cypress_shutdown (struct usb_serial *serial){	struct cypress_private *priv;	dbg ("%s - port %d", __FUNCTION__, serial->port[0]->number);	/* all open ports are closed at this point */	priv = usb_get_serial_port_data(serial->port[0]);	if (priv) {		cypress_buf_free(priv->buf);		kfree(priv);		usb_set_serial_port_data(serial->port[0], NULL);	}}static int cypress_open (struct usb_serial_port *port, struct file *filp){	struct cypress_private *priv = usb_get_serial_port_data(port);	struct usb_serial *serial = port->serial;	unsigned long flags;	int result = 0;	dbg("%s - port %d", __FUNCTION__, port->number);	/* clear halts before open */

⌨️ 快捷键说明

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