/*
* Copyright 2007, Frank A Kingswood <frank@kingswood-consulting.co.uk>
*
* ch341.c implements a serial port driver for the Winchiphead CH341.
*
* The CH341 device can be used to implement an RS232 asynchronous
* serial port, an IEEE-1284 parallel printer port or a memory-like
* interface. In all cases the CH341 supports an I2C interface as well.
* This driver only supports the asynchronous serial interface.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
#define DEFAULT_BAUD_RATE 2400
#define DEFAULT_TIMEOUT 1000
static int debug;
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x4348, 0x5523) },
{ USB_DEVICE(0x1a86, 0x7523) },
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
struct ch341_private {
unsigned baud_rate;
u8 dtr;
u8 rts;
};
static int ch341_control_out(struct usb_device *dev, u8 request,
u16 value, u16 index)
{
int r;
dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
(int)request, (int)value, (int)index);
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
return r;
}
static int ch341_control_in(struct usb_device *dev,
u8 request, u16 value, u16 index,
char *buf, unsigned bufsize)
{
int r;
dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
(int)request, (int)value, (int)index, buf, (int)bufsize);
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
return r;
}
static int ch341_set_baudrate(struct usb_device *dev,
struct ch341_private *priv)
{
short a, b;
int r;
dbg("ch341_set_baudrate(%d)", priv->baud_rate);
switch (priv->baud_rate) {
case 2400:
a = 0xd901;
b = 0x0038;
break;
case 4800:
a = 0x6402;
b = 0x001f;
break;
case 9600:
a = 0xb202;
b = 0x0013;
break;
case 19200:
a = 0xd902;
b = 0x000d;
break;
case 38400:
a = 0x6403;
b = 0x000a;
break;
case 115200:
a = 0xcc03;
b = 0x0008;
break;
default:
return -EINVAL;
}
r = ch341_control_out(dev, 0x9a, 0x1312, a);
if (!r)
r = ch341_control_out(dev, 0x9a, 0x0f2c, b);
return r;
}
static int ch341_set_handshake(struct usb_device *dev,
struct ch341_private *priv)
{
dbg("ch341_set_handshake(%d,%d)", priv->dtr, priv->rts);
return ch341_control_out(dev, 0xa4,
~((priv->dtr?1<<5:0)|(priv->rts?1<<6:0)), 0);
}
static int ch341_get_status(struct usb_device *dev)
{
char *buffer;
int r;
const unsigned size = 8;
dbg("ch341_get_status()");
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size);
if (r < 0)
goto out;
/* Not having the datasheet for the CH341, we ignore the bytes returned
* from the device. Return error if the device did not respond in time.
*/
r = 0;
out: kfree(buffer);
return r;
}
/* -------------------------------------------------------------------------- */
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
char *buffer;
int r;
const unsigned size = 8;
dbg("ch341_configure()");
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
/* expect two bytes 0x27 0x00 */
r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0xa1, 0, 0);
if (r < 0)
goto out;
r = ch341_set_baudrate(dev, priv);
if (r < 0)
goto out;
/* expect two bytes 0x56 0x00 */
r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050);
if (r < 0)
goto out;
/* expect 0xff 0xee */
r = ch341_get_status(dev);
if (r < 0)
goto out;
r = ch341_control_out(dev, 0xa1, 0x501f, 0xd90a);
if (r < 0)
goto out;
r = ch341_set_baudrate(dev, priv);
if (r < 0)
goto out;
r = ch341_set_handshake(dev, priv);
if (r < 0)
goto out;
/* expect 0x9f 0xee */
r = ch341_get_status(dev);
out: kfree(buffer);
return r;
}
/* allocate private data */
static int ch341_attach(struct usb_serial *serial)
{
struct ch341_private *priv;
int r;
dbg("ch341_attach()");
/* private data */
priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->baud_rate = DEFAULT_BAUD_RATE;
priv->dtr = 1;
priv->rts = 1;
r = ch341_configure(serial->dev, priv);
if (r < 0)
goto error;
usb_set_serial_port_data(serial->port[0], priv);
return 0;
error: kfree(priv);
return r;
}
/* open this device, set default parameters */
static int ch341_open(struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
int r;
dbg("ch341_open()");
priv->baud_rate = DEFAULT_BAUD_RATE;
priv->dtr = 1;
priv->rts = 1;
r = ch341_configure(serial->dev, priv);
if (r)
goto out;
r = ch341_set_handshake(serial->dev, priv);
if (r)
goto out;
r = ch341_set_baudrate(serial->dev, priv);
if (r)
goto out;
r = usb_serial_generic_open(port, filp);
out: return r;
}
void tty_termios_copy_hw(struct termios *new, struct termios *old)
{
/* The bits a dumb device handles in software. Smart devices need
to always provide a set_termios method */
new->c_cflag &= HUPCL | CREAD | CLOCAL;
new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL);
// new->c_ispeed = old->c_ispeed;
// new->c_ospeed = old->c_ospeed;
}
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 230400, 460800,
500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
2500000, 3000000, 3500000, 4000000
};
static int n_baud_table = ARRAY_SIZE(baud_table);
void tty_encode_baud_rate(struct termios *termios, speed_t ibaud, speed_t obaud)
{
int i = 0;
int ifound = -1, ofound = -1;
int iclose = ibaud/50, oclose = obaud/50;
if (obaud == 0) /* CD dropped */
ibaud = 0; /* Clear ibaud to be sure */
// termios->c_ispeed = ibaud;
// termios->c_ospeed = obaud;
termios->c_cflag &= ~CBAUD;
/*
* Our goal is to find a close match to the standard baud rate
* returned. Walk the baud rate table and if we get a very close
* match then report back the speed as a POSIX Bxxxx value by
* preference
*/
do {
if (obaud - oclose <= baud_table[i] &&
obaud + oclose >= baud_table[i]) {
if (i > 15)
{
termios->c_cflag |= i - 15;
termios->c_cflag |= CBAUDEX;
}
else
{
termios->c_cflag |= i;
termios->c_cflag &= ~CBAUDEX;
}
ofound = i;
}
if (ibaud - iclose <= baud_table[i] &&
ibaud + iclose >= baud_table[i]) {
/* For the case input == output don't set IBAUD bits
if the user didn't do so */
if (ofound == i )
ifound = i;
}
} while (++i < n_baud_table);
/*
* If we found no match then use BOTHER if provided or warn
* the user their platform maintainer needs to wake up if not.
*/
if (ifound == -1 || ofound == -1) {
static int warned;
if (!warned++)
printk(KERN_WARNING "tty: Unable to return correct "
"speed data as your architecture needs updating.\n");
}
}
/* Old_termios contains the original termios settings and
* tty->termios contains the new setting to be used.
*/
static void ch341_set_termios(struct usb_serial_port *port,
struct termios *old_termios)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
struct tty_struct *tty = port->tty;
unsigned baud_rate;
dbg("ch341_set_termios()");
baud_rate = tty_get_baud_rate(tty);
switch (baud_rate) {
case 2400:
ca
usb转pci连接线的linux驱动程序
需积分: 9 111 浏览量
2008-10-12
18:07:41
上传
评论
收藏 3KB GZ 举报
lophyel
- 粉丝: 0
- 资源: 2
最新资源
- Screenshot_20240427_031602.jpg
- 网页PDF_2024年04月26日 23-46-14_QQ浏览器网页保存_QQ浏览器转格式(6).docx
- 直接插入排序,冒泡排序,直接选择排序.zip
- 在排序2的基础上,再次对快排进行优化,其次增加快排非递归,归并排序,归并排序非递归版.zip
- 实现了7种排序算法.三种复杂度排序.三种nlogn复杂度排序(堆排序,归并排序,快速排序)一种线性复杂度的排序.zip
- 冒泡排序 直接选择排序 直接插入排序 随机快速排序 归并排序 堆排序.zip
- 课设-内部排序算法比较 包括冒泡排序、直接插入排序、简单选择排序、快速排序、希尔排序、归并排序和堆排序.zip
- Python排序算法.zip
- C语言实现直接插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序、归并排序、计数排序,并带图详解.zip
- 常用工具集参考用于图像等数据处理
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈