📄 pl2303.c
字号:
/* * Prolific PL2303 USB to serial adaptor driver * * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * * Original driver for 2.2.x by anonymous * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * See Documentation/usb/usb-serial.txt for more information on using this driver * */#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 <asm/uaccess.h>#include <linux/usb.h>#include <linux/usb/serial.h>#include "pl2303.h"/* * Version Information */#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"static int debug;#define PL2303_CLOSING_WAIT (30*HZ)#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;};static struct usb_device_id id_table [] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) }, { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, { USB_DEVICE(HL340_VENDOR_ID, HL340_PRODUCT_ID) }, { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(usb, id_table);static struct usb_driver pl2303_driver = { .name = "pl2303", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, .no_dynamic_id = 1,};#define SET_LINE_REQUEST_TYPE 0x21#define SET_LINE_REQUEST 0x20#define SET_CONTROL_REQUEST_TYPE 0x21#define SET_CONTROL_REQUEST 0x22#define CONTROL_DTR 0x01#define CONTROL_RTS 0x02#define BREAK_REQUEST_TYPE 0x21#define BREAK_REQUEST 0x23 #define BREAK_ON 0xffff#define BREAK_OFF 0x0000#define GET_LINE_REQUEST_TYPE 0xa1#define GET_LINE_REQUEST 0x21#define VENDOR_WRITE_REQUEST_TYPE 0x40#define VENDOR_WRITE_REQUEST 0x01#define VENDOR_READ_REQUEST_TYPE 0xc0#define VENDOR_READ_REQUEST 0x01#define UART_STATE 0x08#define UART_STATE_TRANSIENT_MASK 0x74#define UART_DCD 0x01#define UART_DSR 0x02#define UART_BREAK_ERROR 0x04#define UART_RING 0x08#define UART_FRAME_ERROR 0x10#define UART_PARITY_ERROR 0x20#define UART_OVERRUN_ERROR 0x40#define UART_CTS 0x80enum pl2303_type { type_0, /* don't know the difference between type 0 and */ type_1, /* type 1, until someone from prolific tells us... */ HX, /* HX version of the pl2303 chip */};struct pl2303_private { spinlock_t lock; struct pl2303_buf *buf; int write_urb_in_use; wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; u8 termios_initialized; enum pl2303_type type;};/* * pl2303_buf_alloc * * Allocate a circular buffer and all associated memory. */static struct pl2303_buf *pl2303_buf_alloc(unsigned int size){ struct pl2303_buf *pb; if (size == 0) return NULL; pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); if (pb == NULL) return NULL; pb->buf_buf = kmalloc(size, GFP_KERNEL); if (pb->buf_buf == NULL) { kfree(pb); return NULL; } pb->buf_size = size; pb->buf_get = pb->buf_put = pb->buf_buf; return pb;}/* * pl2303_buf_free * * Free the buffer and all associated memory. */static void pl2303_buf_free(struct pl2303_buf *pb){ if (pb) { kfree(pb->buf_buf); kfree(pb); }}/* * pl2303_buf_clear * * Clear out all data in the circular buffer. */static void pl2303_buf_clear(struct pl2303_buf *pb){ if (pb != NULL) pb->buf_get = pb->buf_put; /* equivalent to a get of all data available */}/* * pl2303_buf_data_avail * * Return the number of bytes of data available in the circular * buffer. */static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb){ if (pb == NULL) return 0; return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);}/* * pl2303_buf_space_avail * * Return the number of bytes of space available in the circular * buffer. */static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb){ if (pb == NULL) return 0; return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);}/* * pl2303_buf_put * * Copy data data from a user buffer and put it into the circular buffer. * Restrict to the amount of space available. * * Return the number of bytes copied. */static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, unsigned int count){ unsigned int len; if (pb == NULL) return 0; len = pl2303_buf_space_avail(pb); if (count > len) count = len; if (count == 0) return 0; len = pb->buf_buf + pb->buf_size - pb->buf_put; if (count > len) { memcpy(pb->buf_put, buf, len); memcpy(pb->buf_buf, buf+len, count - len); pb->buf_put = pb->buf_buf + count - len; } else { memcpy(pb->buf_put, buf, count); if (count < len) pb->buf_put += count; else /* count == len */ pb->buf_put = pb->buf_buf; } return count;}/* * pl2303_buf_get * * Get data from the circular buffer and copy to the given buffer. * Restrict to the amount of data available. * * Return the number of bytes copied. */static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, unsigned int count){ unsigned int len; if (pb == NULL) return 0; len = pl2303_buf_data_avail(pb); if (count > len) count = len; if (count == 0) return 0; len = pb->buf_buf + pb->buf_size - pb->buf_get; if (count > len) { memcpy(buf, pb->buf_get, len); memcpy(buf+len, pb->buf_buf, count - len); pb->buf_get = pb->buf_buf + count - len; } else { memcpy(buf, pb->buf_get, count); if (count < len) pb->buf_get += count; else /* count == len */ pb->buf_get = pb->buf_buf; } return count;}static int pl2303_startup(struct usb_serial *serial){ struct pl2303_private *priv; enum pl2303_type type = type_0; int i; if (serial->dev->descriptor.bDeviceClass == 0x02) type = type_0; else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) type = HX; else if (serial->dev->descriptor.bDeviceClass == 0x00) type = type_1; else if (serial->dev->descriptor.bDeviceClass == 0xFF) type = type_1; dbg("device type: %d", type); for (i = 0; i < serial->num_ports; ++i) { priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL); if (!priv) goto cleanup; spin_lock_init(&priv->lock); priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); if (priv->buf == NULL) { kfree(priv); goto cleanup; } init_waitqueue_head(&priv->delta_msr_wait); priv->type = type; usb_set_serial_port_data(serial->port[i], priv); } return 0;cleanup: 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 set_control_lines(struct usb_device *dev, u8 value){ int retval; retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, value, 0, NULL, 0, 100); dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval); return retval;}static void pl2303_send(struct usb_serial_port *port){ int count, result; struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); if (priv->write_urb_in_use) { spin_unlock_irqrestore(&priv->lock, flags); return; } count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, port->bulk_out_size); if (count == 0) { spin_unlock_irqrestore(&priv->lock, flags); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -