hue2550 发表于 2015-11-25 09:27

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);

hue2550 发表于 2015-11-25 16:34

问题以解决,在probe函数中增加tty port与tty device的对应即可
页: [1]
查看完整版本: linux USB虚拟串口驱动在内核3.1以下版本正常,3.9版本使用崩溃