📄 cypress_m8.c
字号:
/* * 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 + -