linux USB虚拟串口驱动在内核3.1以下版本正常,3.9版本使用崩溃
本帖最后由 hue2550 于 2015-11-25 09:27 编辑最近有个项目的目标板提供USB接口,主机这边通过驱动模块将USB虚拟成了一个串口,在内核3.1以下版本可正常使用,在内核3.9及以上版本使用可以加载成功,但是一旦对ttyVCOM进行读写操作时就系统崩溃了。驱动模块及错误指示如下,求大神指点。
在3.9内核里面tty_io.c中执行tty_init_dev()操作是比之前的3.1版本多出了一个tty->port的操作,请问这个tty->port该在哪里初始化?
/*
* Linux Virtual COM Port Driver for XinCOMM
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#define VID 0x055f
#define PID 0x017B
//#define VID 0x046D
//#define PID 0x0A0C
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "Zhang"
#define DRIVER_DESC "Linux Virtual COM Driver For XinComm"
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
#define XIN_MAX_SUPPORTED_DEVICES 10
#define XIN_HID_INTERFACE_NUMBER 1
#define XIN_HID_EP_NUMBERS 1
#define XIN_TTY_MAJOR 240
#define XIN_TTY_MINORS 2
#define XIN_BUFFER_SIZE 2048
#define HID_REQ_GET_REPORT 0x01
#define HID_REQ_SET_REPORT 0x09
#define OUTPUT_REPORT 0x02
#define HID_INTERFACE_NUMBER 0x01
/* Our fake UART values */
#define MCR_DTR 0x01
#define MCR_RTS 0x02
#define MCR_LOOP 0x04
#define MSR_CTS 0x08
#define MSR_CD 0x10
#define MSR_RI 0x20
#define MSR_DSR 0x40
typedef unsigned short USHORT;
typedef unsigned char UCHAR;
typedef unsigned int UINT;
typedef struct __USB_HDR
{
USHORT magicNumber; //0x55AA
USHORT length;
USHORT m_type: 4;
USHORT sub_type: 12;
UCHAR inOutFlag; // 0: read; 1: write
UCHAR reserved;
}__attribute__((__packed__)) USB_HDR; // pay attention to ALIGNMENT
#define MAX_BUFFER_SIZE 2048
#define DATA_BUFFER_SIZE (MAX_BUFFER_SIZE - sizeof(USB_HDR))
#define MAX_REAL_DATA_SIZE DATA_BUFFER_SIZE
#define WRITE_PACKET_SIZE 60
#define READ_PACKET_SIZE (512 - 4)
//#define WRITE_PACKET_SIZE 60
//#define READ_PACKET_SIZE (2)
// for main type
#define MAIN_TYPE_IP 0
#define MAIN_TYPE_DEBUG 1
// for sub type
#define SUB_TYPE_IP 0
#define SUB_TYPE_MLOG 1
#define SUB_TYPE_Trace 2
#define SUB_TYPE_AtCommand 3
#define SUB_TYPE_ROM_UPDATE (256)
#define SUB_TYPE_INVALID 0xFF
// for in out flag
#define DIRECTION_READ0
#define DIRECTION_WRITE 1
// for magic number
#define MAGIC_NUMBER 0x55AA
typedef struct __USB_BUF
{
USB_HDR Hdr;
UCHAR Buffer;
} __attribute__((__packed__)) USB_BUF;// pay attention to ALIGNMENT
struct xin_serial {
struct tty_struct *tty; /* pointer to the tty for this device */
int open_count; /* number of times this port has been opened */
/* for tiocmget and tiocmset functions */
int msr; /* MSR shadow */
int mcr; /* MCR shadow */
void* xinhid;
};
struct xin_hid {
struct usb_device *usbdev;
// interrupt in xfer
struct urb *irq;
char * irq_buf;
dma_addr_t irq_dma;
// buffer for read hid data
USB_BUF read_buffer;
int read_buffer_total_length;
int read_buffer_index;
// buffer for write hid data
USB_BUF write_buffer;
char* send_buffer;
// mutex for this HID device
spinlock_t hid_lock;
// info for serial
struct xin_serial *xin_table; /* initially all NULL */
int hid_index; // the index for this HID device
// info about USB Port where this device is connected to
int xin_minor_index; // ((busnum * 1000) + (level * 100) + (portnum * 10))
int busnum;
int level;
int portnum;
};
static struct tty_driver *xin_tty_driver = NULL;
static struct xin_hid* hid_device_table = {NULL};
static DEFINE_MUTEX(global_mutex);
/********************
ttyVCOM0: AT command
ttyVCOM1:
*********************/
static int xin_get_subtype_by_tty(struct xin_hid* xinhid, struct xin_serial *xin)
{
int i = 0;
int subtype = SUB_TYPE_INVALID;
if(xinhid == NULL)
{
return SUB_TYPE_INVALID;
}
for(i = 0; i < XIN_TTY_MINORS; i++)
{
if(xin == xinhid->xin_table)
{
break;
}
}
if(i == XIN_TTY_MINORS)
{
return SUB_TYPE_INVALID;
}
if(i == 0)
{
subtype = SUB_TYPE_AtCommand;
}
else if(i == 1)
{
subtype = SUB_TYPE_ROM_UPDATE;
}
return subtype;
}
static struct tty_struct *xin_get_tty_by_subtype(struct xin_hid* xinhid, int subtype)
{
struct tty_struct *tty = NULL;
struct xin_serial * serial = NULL;
if(xinhid == NULL)
{
return NULL;
}
if(subtype == SUB_TYPE_AtCommand)
{
serial = xinhid->xin_table;
}
else if(subtype == SUB_TYPE_ROM_UPDATE)
{
serial = xinhid->xin_table;
}
if(!serial)
{
//printk(KERN_ERR "[%s] destination VCOM does not exist", __FUNCTION__);
goto exit;
}
if (!serial->open_count)
{
printk("[%s] no destination ttyVCOM is not opened", __FUNCTION__);
tty = NULL;
/* port was not opened */
goto exit;
}
tty = serial->tty;
exit:
return tty;
}
static int xin_open(struct tty_struct *tty, struct file *file)
{
struct xin_serial *xin = NULL;
int index = 0;
int ttyindex = 0;
int result = 0;
struct xin_hid * xinhid = NULL;
int hid_index = XIN_MAX_SUPPORTED_DEVICES;
int xin_minor_index = 0;
int i = 0;
/* initialize the pointer in case something fails */
tty->driver_data = NULL;
/* get the serial object associated with this tty pointer */
ttyindex = tty->index;
printk("[%s] open ttyVCOM%d", __FUNCTION__, ttyindex);
// get the hid index and serial index from the ttyindex
xin_minor_index = ttyindex / 10;
xin_minor_index = 10 * xin_minor_index;
printk("[%s] the xin minor index for this tty is: %d", __FUNCTION__, xin_minor_index);
for(i = 0; i < XIN_MAX_SUPPORTED_DEVICES; i++)
{
if((hid_device_table != NULL) && (hid_device_table->xin_minor_index == xin_minor_index))
{
hid_index = i;
break;
}
}
if(hid_index >= XIN_MAX_SUPPORTED_DEVICES)
{
printk("[%s] open ttyVCOM%d failed because the HID device cannot be found!!!", __FUNCTION__, ttyindex);
return -ENODEV;
}
xinhid = hid_device_table;
if(xinhid == NULL)
{
printk("[%s] open ttyVCOM%d failed because the hid device is not connected!!!", __FUNCTION__, ttyindex);
return -ENODEV;
}
index = ttyindex - xin_minor_index;
if(index >= XIN_TTY_MINORS)
{
printk("[%s] open ttyVCOM%d failed because of incorrect index, index = %d", __FUNCTION__, ttyindex, index);
return -ENODEV;
}
// open the deivce
spin_lock(&xinhid->hid_lock);
xin = xinhid->xin_table;
if (xin == NULL) {
printk("[%s] the first time to open ttyVCOM%d", __FUNCTION__, ttyindex );
/* first time accessing this device, let's create it */
xin = kmalloc(sizeof(*xin), GFP_KERNEL);
if (!xin)
{
printk("[%s] failed to allocate memory for ttyVCOM%d", __FUNCTION__ ,ttyindex);
result = -ENOMEM;
goto Exit;
}
xin->open_count = 0;
xin->xinhid = (void*)xinhid;
xinhid->xin_table = xin;
xin->msr = (MSR_CD | MSR_DSR | MSR_CTS);
}
/* save our structure within the tty structure */
tty->driver_data = xin;
xin->tty = tty;
++xin->open_count;
//tty_wakeup(tty);
printk("[%s] open ttyVCOM%d OK, count = %d", __FUNCTION__, ttyindex, xin->open_count);
Exit:
spin_unlock(&xinhid->hid_lock);
return result;
}
static void do_close(struct xin_serial *xin)
{
// the lock will be used in the function who call this function
// so no any protection in this function
if (!xin->open_count) {
/* port was never opened */
return;
}
--xin->open_count;
}
static void xin_close(struct tty_struct *tty, struct file *file)
{
struct xin_serial *xin = NULL;
//struct xin_hid* xinhid = NULL; // we should not handle xinhid here. Evevything will be done when disconnect!
if(tty == NULL)
{
printk("[%s] wrong parameter because tty is NULL !!!", __FUNCTION__);
return;
}
printk("[%s] close ttyVCOM%d", __FUNCTION__, tty->index);
xin = tty->driver_data;
if(xin == NULL)
{
printk("[%s] tty->driver_data is NULL!!!", __FUNCTION__);
return;
}
if (xin)
{
do_close(xin);
}
}
static int xin_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
struct xin_serial *xin = tty->driver_data;
struct xin_hid* xinhid = NULL;
int subtype = SUB_TYPE_INVALID;
int retval = -EINVAL;
int copylength = 0;
int copy_index = 0;
char* startptr = NULL;
printk("[%s] ttyVCOM%d write %d bytes", __FUNCTION__, tty->index, count);
if (!xin)
{
printk("[%s] ttyVCOM%d doesnot exist", __FUNCTION__, tty->index);
retval = -ENODEV;
return retval;
}
if(!count)
{
printk("[%s] data length is ZERO, so shortcut!", __FUNCTION__);
retval = 0;
return retval;
}
xinhid = (struct xin_hid*)xin->xinhid;
if(xinhid == NULL)
{
printk("[%s] xin hid device is not connected", __FUNCTION__);
retval = -ENODEV;
return retval;
}
spin_lock(&xinhid->hid_lock);
if (!xin->open_count)
{
printk("[%s] ttyVCOM%d is not opened", __FUNCTION__, tty->index);
/* port was not opened */
goto exit;
}
subtype = xin_get_subtype_by_tty(xinhid, xin);
if(subtype == SUB_TYPE_INVALID)
{
printk("[%s] cannot get subtype for ttyVCOM%d", __FUNCTION__, tty->index);
goto exit;
}
printk("[%s] send data from ttyVCOM%d to hid device- ", __FUNCTION__, tty->index);
copylength = count > MAX_REAL_DATA_SIZE ? MAX_REAL_DATA_SIZE : count;
memset(&xinhid->write_buffer, 0, sizeof(USB_BUF));
memcpy(&xinhid->write_buffer.Buffer, buffer, copylength);
xinhid->write_buffer.Hdr.magicNumber = MAGIC_NUMBER;
xinhid->write_buffer.Hdr.length = sizeof(USB_HDR) + copylength;
xinhid->write_buffer.Hdr.m_type = MAIN_TYPE_DEBUG;
xinhid->write_buffer.Hdr.inOutFlag = DIRECTION_WRITE;
xinhid->write_buffer.Hdr.sub_type = subtype;
copylength = 0;
copy_index = 0;
while(1)
{
if(xinhid->write_buffer.Hdr.length - copy_index >= WRITE_PACKET_SIZE)
{
copylength = WRITE_PACKET_SIZE;
}
else
{
copylength = xinhid->write_buffer.Hdr.length - copy_index;
}
startptr = ((char*)(&xinhid->write_buffer)) + copy_index;
memset(xinhid->send_buffer, 0, WRITE_PACKET_SIZE);
memcpy(xinhid->send_buffer, startptr, copylength);
retval = usb_control_msg(xinhid->usbdev, usb_sndctrlpipe(xinhid->usbdev, 0),
HID_REQ_SET_REPORT,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(OUTPUT_REPORT << 8) | 0,
HID_INTERFACE_NUMBER, xinhid->send_buffer, WRITE_PACKET_SIZE,
USB_CTRL_SET_TIMEOUT);
if(retval < 0)
{
printk("[%s] failed to write to HID device!, result = %d\n",__FUNCTION__, retval);
goto exit;
}
if(retval < WRITE_PACKET_SIZE)
{
printk("[%s] failed to write to HID device!, result = %d != %d\n", __FUNCTION__, retval, copylength);
}
copy_index += copylength;
if(copy_index >= xinhid->write_buffer.Hdr.length)
{
printk("[%s] write complete!\n", __FUNCTION__);
break;
}
}
retval = count;
exit:
spin_unlock(&xinhid->hid_lock);
return retval;
}
static int xin_write_room(struct tty_struct *tty)
{
struct xin_serial *xin = tty->driver_data;
int room = -EINVAL;
struct xin_hid *xinhid = NULL;
if (!xin)
return -ENODEV;
xinhid = (struct xin_hid*)xin->xinhid;
if (!xinhid)
return -ENODEV;
spin_lock(&xinhid->hid_lock);
if (!xin->open_count) {
/* port was not opened */
goto exit;
}
/* calculate how much room is left in the device */
room = XIN_BUFFER_SIZE;
exit:
spin_unlock(&xinhid->hid_lock);
return room;
}
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void xin_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
unsigned int cflag;
cflag = tty->termios.c_cflag;
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios.c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag))) {
printk(" - nothing to change...\n");
return;
}
}
/* get the byte size */
switch (cflag & CSIZE) {
case CS5:
printk(" - data bits = 5\n");
break;
case CS6:
printk(" - data bits = 6\n");
break;
case CS7:
printk(" - data bits = 7\n");
break;
default:
case CS8:
printk(" - data bits = 8\n");
break;
}
/* determine the parity */
if (cflag & PARENB)
if (cflag & PARODD)
printk(" - parity = odd\n");
else
printk(" - parity = even\n");
else
printk(" - parity = none\n");
/* figure out the stop bits requested */
if (cflag & CSTOPB)
printk(" - stop bits = 2\n");
else
printk(" - stop bits = 1\n");
/* figure out the hardware flow control settings */
if (cflag & CRTSCTS)
printk(" - RTS/CTS is enabled\n");
else
printk(" - RTS/CTS is disabled\n");
/* determine software flow control */
/* if we are implementing XON/XOFF, set the start and
* stop character in the device */
if (I_IXOFF(tty) || I_IXON(tty)) {
unsigned char stop_char= STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);
/* if we are implementing INBOUND XON/XOFF */
if (I_IXOFF(tty))
printk(" - INBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(" - INBOUND XON/XOFF is disabled");
/* if we are implementing OUTBOUND XON/XOFF */
if (I_IXON(tty))
printk(" - OUTBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(" - OUTBOUND XON/XOFF is disabled");
}
/* get the baud rate wanted */
printk(" - baud rate = %d", tty_get_baud_rate(tty));
}
static int xin_tiocmget(struct tty_struct *tty)
{
struct xin_serial *xin = tty->driver_data;
unsigned int result = 0;
unsigned int msr = xin->msr;
unsigned int mcr = xin->mcr;
result = ((mcr & MCR_DTR)? TIOCM_DTR: 0) | /* DTR is set */
((mcr & MCR_RTS)? TIOCM_RTS: 0) | /* RTS is set */
((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
((msr & MSR_CTS)? TIOCM_CTS: 0) | /* CTS is set */
((msr & MSR_CD) ? TIOCM_CAR: 0) | /* Carrier detect is set*/
((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
((msr & MSR_DSR)? TIOCM_DSR: 0); /* DSR is set */
return result;
}
static int xin_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
{
struct xin_serial *xin = tty->driver_data;
unsigned int mcr = xin->mcr;
if (set & TIOCM_RTS)
mcr |= MCR_RTS;
if (set & TIOCM_DTR)
mcr |= MCR_RTS;
if (clear & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (clear & TIOCM_DTR)
mcr &= ~MCR_RTS;
/* set the new MCR value in the device */
xin->mcr = mcr;
return 0;
}
static int xin_hid_alloc_mem(struct usb_device *dev, struct xin_hid *xinhid)
{
if (!(xinhid->irq = usb_alloc_urb(0, GFP_KERNEL)))
return -1;
//if (!(xinhid->irq_buf = usb_buffer_alloc(dev, READ_PACKET_SIZE, GFP_ATOMIC, &xinhid->irq_dma)))
if (!(xinhid->irq_buf = usb_alloc_coherent(dev, READ_PACKET_SIZE, GFP_KERNEL, &xinhid->irq_dma)))
return -1;
if (!(xinhid->send_buffer = kmalloc(WRITE_PACKET_SIZE, GFP_KERNEL)))
return -1;
return 0;
}
static void xin_hid_free_mem(struct usb_device *dev, struct xin_hid *xinhid)
{
usb_free_urb(xinhid->irq);
//usb_free_coherent(dev, READ_PACKET_SIZE, xinhid->irq_buf, xinhid->irq_dma);
usb_free_coherent(dev, READ_PACKET_SIZE, xinhid->irq_buf, xinhid->irq_dma);
kfree(xinhid->send_buffer);
}
static void xin_irq(struct urb *urb)
{
struct xin_hid *xinhid = urb->context;
int i;
int datalength = 0;
int copylength = 0;
char* startptr = NULL;
struct tty_struct *tty = NULL;
int subtype = SUB_TYPE_INVALID;
if(xinhid == NULL)
{
printk("[%s] xinhid is NULL\n", __FUNCTION__);
return;
}
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
printk("[%s] irq complete error in place 1, status: %d\n", __FUNCTION__, urb->status);
return;
/* -EPIPE:should clear the halt */
default: /* error */
printk("[%s] irq complete error in place 2, status: %d\n", __FUNCTION__, urb->status);
goto resubmit;
}
//
datalength = urb->actual_length;
//printk(KERN_ERR "[%s] get %d bytes from xin100 hid device\n", __FUNCTION__, datalength);
if(xinhid->read_buffer_total_length == 0)
{
copylength = datalength;
}
else if( datalength + xinhid->read_buffer_index >= xinhid->read_buffer_total_length)
{
copylength = xinhid->read_buffer_total_length - xinhid->read_buffer_index;
}
else
{
copylength = datalength;
}
startptr = ((char*)&xinhid->read_buffer) + xinhid->read_buffer_index;
memcpy(startptr, urb->transfer_buffer, copylength);
if(xinhid->read_buffer_total_length == 0)
{
xinhid->read_buffer_total_length = xinhid->read_buffer.Hdr.length;
if(xinhid->read_buffer_total_length > MAX_BUFFER_SIZE)
{
//device should not send much messages but AT responses.
/*printk(KERN_ERR "[%s] wrong length %d in header (should <= 2048 )\n", __FUNCTION__, xinhid->read_buffer_total_length);*/
xinhid->read_buffer_total_length = MAX_BUFFER_SIZE;
}
}
xinhid->read_buffer_index += copylength;
if(xinhid->read_buffer_index >= xinhid->read_buffer_total_length)
{
// get the whole data
//printk(KERN_ERR "[%s] get whole %d bytes from xin100 hid device\n", __FUNCTION__, xinhid->read_buffer_total_length);
// send the data to tty com port
if(xinhid->read_buffer.Hdr.magicNumber != MAGIC_NUMBER)
{
printk("[%s] wrong magic number: 0x%04x (should 0x55AA)\n", __FUNCTION__, xinhid->read_buffer.Hdr.magicNumber);
goto cleanbuffer;
}
if(xinhid->read_buffer.Hdr.m_type != MAIN_TYPE_DEBUG)
{
printk("[%s] wrong major type\n", __FUNCTION__);
goto cleanbuffer;
}
subtype = xinhid->read_buffer.Hdr.sub_type;
tty = xin_get_tty_by_subtype(xinhid, subtype);
if(tty != NULL)
{
tty_insert_flip_string(tty->port, &xinhid->read_buffer.Buffer, xinhid->read_buffer_total_length - sizeof(USB_HDR));
tty_flip_buffer_push(tty->port);
}
else
{
//printk(KERN_ERR "[%s] no available tty for the received data\n", __FUNCTION__);
goto cleanbuffer;
}
cleanbuffer:
memset(&xinhid->read_buffer, 0, sizeof(USB_BUF));
xinhid->read_buffer_index = 0;
xinhid->read_buffer_total_length = 0;
}
resubmit:
i = usb_submit_urb (urb, GFP_ATOMIC);
if (i)
printk("[%s] can't resubmit intr, status %d", __FUNCTION__, i);
}
static int xin_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
struct usb_device *dev = NULL;
struct usb_host_interface *interface = NULL;
struct usb_endpoint_descriptor *endpoint = NULL;
struct xin_hid *xinhid = NULL;
int pipe = 0, maxp = 0;
int error = -ENOMEM;
int i = 0;
int hid_index = XIN_MAX_SUPPORTED_DEVICES;
printk("[%s] xin_probe start...\n", __FUNCTION__);
if(iface == NULL)
{
printk("[%s] iface is NULL\n", __FUNCTION__);
return -ENODEV;
}
dev = interface_to_usbdev(iface);
interface = iface->cur_altsetting;
if ((interface->desc.bInterfaceNumber != XIN_HID_INTERFACE_NUMBER) || (interface->desc.bNumEndpoints != XIN_HID_EP_NUMBERS))
{
printk("[%s] Not HID Interface, because bInterfaceNumber or bNumEndpoints are wrong!\n", __FUNCTION__);
return -ENODEV;
}
endpoint = &interface->endpoint.desc;
if (!usb_endpoint_is_int_in(endpoint))
{
printk("[%s] endpoint type for HID interface is wrong! (should be interrupt in)!\n", __FUNCTION__);
return -ENODEV;
}
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
xinhid = kzalloc(sizeof(struct xin_hid), GFP_KERNEL);
if (!xinhid)
{
printk("[%s] failed to allocate memory for struct xinhid!\n", __FUNCTION__);
goto fail1;
}
if (xin_hid_alloc_mem(dev, xinhid))
{
printk("[%s] failed to allocate memory for urb and buffer!\n", __FUNCTION__);
goto fail2;
}
spin_lock_init(&xinhid->hid_lock);
xinhid->read_buffer_total_length = 0;
xinhid->read_buffer_index = 0;
xinhid->usbdev = dev;
xinhid->busnum = dev->bus->busnum;
xinhid->level = dev->level;
xinhid->portnum = dev->portnum;
xinhid->xin_minor_index = (xinhid->busnum * 1000) + (xinhid->level * 100) + (xinhid->portnum * 10);
printk("[%s] xin_minor_index: %d, busnum: %d, level: %d, portnum: %d", __FUNCTION__, xinhid->xin_minor_index, xinhid->busnum, xinhid->level, xinhid->portnum);
// find the available serial index for this hid device
mutex_lock(&global_mutex);
for(i = 0; i < XIN_MAX_SUPPORTED_DEVICES; i++)
{
if(hid_device_table == NULL)
{
hid_index = i;
break;
}
}
if(hid_index >= XIN_MAX_SUPPORTED_DEVICES)
{
printk("[%s] too many devices are inserted!!!", __FUNCTION__);
mutex_unlock(&global_mutex);
goto fail2;
}
xinhid->hid_index = hid_index;
hid_device_table = xinhid;
for (i = 0; i < XIN_TTY_MINORS; i++)
{
printk("[%s] register ttyVCOM%d\n", __FUNCTION__, xinhid->xin_minor_index + i);
tty_register_device(xin_tty_driver, xinhid->xin_minor_index + i, NULL);
}
mutex_unlock(&global_mutex);
// init the interrupt in urb and submit the first urb
usb_fill_int_urb(xinhid->irq, dev, pipe,
xinhid->irq_buf, (maxp > 1024 ? 1024 : maxp),
xin_irq, xinhid, endpoint->bInterval);
xinhid->irq->transfer_dma = xinhid->irq_dma;
xinhid->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
if (usb_submit_urb(xinhid->irq, GFP_ATOMIC))
{
error = -EIO;
printk("[%s] failed to submit interrupt in urb!\n", __FUNCTION__);
goto fail3;
}
usb_set_intfdata(iface, xinhid);
printk("[%s] xin_probe success!\n", __FUNCTION__);
return 0;
fail3:
mutex_lock(&global_mutex);
for (i = 0; i < XIN_TTY_MINORS; ++i)
{
tty_unregister_device(xin_tty_driver, xinhid->xin_minor_index + i);
}
hid_device_table = NULL;
mutex_unlock(&global_mutex);
fail2:
xin_hid_free_mem(dev, xinhid);
fail1:
kfree(xinhid);
return error;
}
static void xin_disconnect(struct usb_interface *intf)
{
struct xin_hid *xinhid = usb_get_intfdata (intf);
int i = 0;
struct xin_serial *xin = NULL;
int hid_index = 0;
printk("[%s] xin_disconnect\n", __FUNCTION__);
if(xinhid == NULL)
{
printk("[%s] no available hid device in disconnect!!!\n", __FUNCTION__);
return;
}
hid_index = xinhid->hid_index;
printk("[%s] before unregister VCOM device\n", __FUNCTION__);
mutex_lock(&global_mutex);
for (i = 0; i < XIN_TTY_MINORS; ++i)
{
printk("[%s] unregister ttyVCOM%d device\n", __FUNCTION__, xinhid->xin_minor_index + i);
tty_unregister_device(xin_tty_driver, xinhid->xin_minor_index + i);
}
hid_device_table = NULL;
mutex_unlock(&global_mutex);
printk("[%s] shut down hid device\n", __FUNCTION__);
spin_lock(&xinhid->hid_lock);
/* shut down all serial device and free the memory */
for (i = 0; i < XIN_TTY_MINORS; ++i) {
xin = xinhid->xin_table;
if (xin) {
/* close the port */
while (xin->open_count)
do_close(xin);
/* free the memory */
kfree(xin);
xinhid->xin_table = NULL;
}
}
usb_set_intfdata(intf, NULL);
usb_kill_urb(xinhid->irq);
xin_hid_free_mem(interface_to_usbdev(intf), xinhid);
spin_unlock(&xinhid->hid_lock);
kfree(xinhid);
xinhid = NULL;
printk(KERN_ERR "[%s] xin_disconnect success\n", __FUNCTION__);
}
static struct tty_operations serial_ops = {
.open = xin_open,
.close = xin_close,
.write = xin_write,
.write_room = xin_write_room,
.set_termios = xin_set_termios,
.tiocmget = xin_tiocmget,
.tiocmset = xin_tiocmset,
};
static const struct usb_device_id products [] = {
#define XIN100_HID_INTERFACE \
.bInterfaceClass = 0xff, \
.bInterfaceSubClass = 0xff, \
.bInterfaceProtocol = 0xff
/*
* Xin100 USB HID uses just one USB interface.
*/
{
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = VID,
.idProduct = PID,
XIN100_HID_INTERFACE
},{ }, // END
};
MODULE_DEVICE_TABLE(usb, products);
static struct usb_driver xin_hid_driver = {
.name = "ttyxin",
.id_table = products,
.probe = xin_probe,
.disconnect = xin_disconnect,
};
static int __init xin_init(void)
{
int retval;
int i = 0;
int max_tty_index = 0;
printk("[%s] init ttyxin driver", __FUNCTION__);
for(i = 0; i < XIN_MAX_SUPPORTED_DEVICES; i++)
{
hid_device_table = NULL;
}
/* allocate the tty driver */
max_tty_index = 0xFFFF;
xin_tty_driver = alloc_tty_driver(max_tty_index);
if (!xin_tty_driver)
{
printk("[%s] failed to alloc tty driver", __FUNCTION__);
return -ENOMEM;
}
//printk(KERN_ERR "[%s] xin_tty_driver is 0x%08x", __FUNCTION__, (unsigned int)xin_tty_driver);
/* initialize the tty driver */
xin_tty_driver->owner = THIS_MODULE;
xin_tty_driver->driver_name = "ttyxin";
xin_tty_driver->name = "ttyVCOM";
xin_tty_driver->major = XIN_TTY_MAJOR,
xin_tty_driver->minor_start = 0,
xin_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
xin_tty_driver->subtype = SERIAL_TYPE_NORMAL,
xin_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
xin_tty_driver->init_termios = tty_std_termios;
xin_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
xin_tty_driver->init_termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ECHOE);
tty_set_operations(xin_tty_driver, &serial_ops);
/* register the tty driver */
retval = tty_register_driver(xin_tty_driver);
if (retval) {
printk("[%s] failed to register xin tty driver", __FUNCTION__);
put_tty_driver(xin_tty_driver);
return retval;
}
retval = usb_register(&xin_hid_driver);
if(retval)
{
printk("[%s] failed to register xin hid driver", __FUNCTION__);
put_tty_driver(xin_tty_driver);
tty_unregister_driver(xin_tty_driver);
return retval;
}
printk(KERN_ERR DRIVER_DESC " " DRIVER_VERSION);
return retval;
}
static void __exit xin_exit(void)
{
printk("[%s] uninit xin com driver", __FUNCTION__);
usb_deregister(&xin_hid_driver);
tty_unregister_driver(xin_tty_driver);
xin_tty_driver = NULL;
printk("[%s] uninit xin com driver success", __FUNCTION__);
}
module_init(xin_init);
module_exit(xin_exit);
问题以解决,在probe函数中增加tty port与tty device的对应即可
页:
[1]