> On Fri, Jun 03, 2005 at 03:24:51PM +0800, Enzo Chen (?????h) wrote:
> >  
> >   If you'd like to check in detail, please feel free to contact me.
> 
> Sure, have a pointer to your whole driver?  It is released under the GPL, 
> right?
> 
> For what usb to serial device is this driver being written for?
> 
> thanks,
> 
> greg k-h

Dear Greg,

  Yes, it will released under the GPL.
  I list the code below.
  Please understand it is under development and so there should be many bugs in 
it.
  However, the point is why usb_unlink_urb() in mxu2s_close() function blocked?

  Thanks a lot~

Regards,
oct  

=============================================================

#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_flip.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/usb.h>


#ifdef CONFIG_USB_SERIAL_DEBUG
        static int debug = 1;
#else
        static int debug;
#endif

#include "usb-serial.h"
#include "mxu2s.h"

/*
 * Version Information
 */
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Enzo Chen <[EMAIL PROTECTED]>"
#define DRIVER_DESC "MOXA USB to Serial Hub Driver"


/*
 * Function prototypes
 */
static int  mxu2s_startup               (struct usb_serial *serial);
static void mxu2s_shutdown              (struct usb_serial *serial);
static int  mxu2s_open                  (struct usb_serial_port *port,
                                          struct file *filp);
static void mxu2s_close                 (struct usb_serial_port *port,
                                          struct file *filp);
static int  mxu2s_write                 (struct usb_serial_port *port,
                                          int from_user,
                                          const unsigned char *buf,
                                          int count);
static void mxu2s_write_bulk_callback   (struct urb *urb, struct pt_regs *regs);
static int  mxu2s_chars_in_buffer     (struct usb_serial_port *port);
static int  mxu2s_write_room            (struct usb_serial_port *port);
static void mxu2s_read_bulk_callback    (struct urb *urb, struct pt_regs *regs);
static void mxu2s_set_termios           (struct usb_serial_port *port,
                                          struct termios * old);
static int  mxu2s_ioctl                 (struct usb_serial_port *port,
                                          struct file * file,
                                          unsigned int cmd,
                                          unsigned long arg);

static int mxu2s_control_msg            (struct usb_serial_port *port,
                                         unsigned char request,
                                         unsigned char dirction,
                                         unsigned int value, void *buf,
                                         unsigned int buf_len);

static void mxu2s_wait_until_sent       (struct usb_serial_port *port,
                                         int timeout);

static int mxu2s_get_queue_value        (struct usb_serial_port *port);
/*
 * All of the device info needed for the KLSI converters.
 */
static struct usb_device_id mxu2s_idtable_combined [] = {
        { USB_DEVICE(MOXA_VID, NPort_1240_PID) },
        { }             /* Terminating entry */
};

static struct usb_device_id mxu2s_np1240_ids [] = {
        { USB_DEVICE(MOXA_VID, NPort_1240_PID) },
        { }                        /* Terminating entry */
};


MODULE_DEVICE_TABLE (usb, mxu2s_idtable_combined);

static struct usb_driver mxu2s_driver = {
        .owner =        THIS_MODULE,
        .name =         "mxu2sd",
        .probe =        usb_serial_probe,
        .disconnect =   usb_serial_disconnect,
        .id_table =     mxu2s_idtable_combined,
};

static struct usb_serial_device_type mxu2s_np1240_device = {
        .owner =             THIS_MODULE,
        .name =              "MOXA NPort 1240 Device",
        .short_name =        "moxa_nport_1240",
        .id_table =          mxu2s_np1240_ids,
        .num_interrupt_in =  0,
        .num_bulk_in =       1,
        .num_bulk_out =      1,
        .num_ports =         1,
        .open =              mxu2s_open,
        .close =             mxu2s_close,
        .write =             mxu2s_write,
        .write_bulk_callback = mxu2s_write_bulk_callback,
        .chars_in_buffer =   mxu2s_chars_in_buffer,
        .write_room =        mxu2s_write_room,
        .read_bulk_callback =mxu2s_read_bulk_callback,
        .ioctl =             mxu2s_ioctl,
        .set_termios =       mxu2s_set_termios,
        .attach =            mxu2s_startup,
        .shutdown =          mxu2s_shutdown,
};


struct mxu2s_port_settings {
        __u16   baudrate;
        __u8    databits;
        __u8    parity;
        __u8    stopbits;
        __u16   msr;
};

#define URB_TRANSFER_BUFFER_SIZE        64
struct mxu2s_private {
        struct usb_serial_port          *port;
        struct mxu2s_port_settings      cfg;
        struct termios                  termios;
        spinlock_t                      lock;
        unsigned long                   bytes_in;
        unsigned long                   bytes_out;
        struct urb                      *write_urb;
        struct urb                      *read_urb;
        unsigned char                   portnum;
        struct semaphore                sem;
        unsigned char                   chars[6];
        unsigned char                   setflow[16];
        unsigned int                    opened;
};



static int mxu2s_startup (struct usb_serial *serial)
{
        struct mxu2s_private *priv;
        int i;
        unsigned char ep_addr;

        /* allocate the private data structure */
        for (i=0; i<serial->num_ports; i++) {
                priv = kmalloc(sizeof(struct mxu2s_private), GFP_KERNEL);
                if (!priv) {
                        dbg("%s kmalloc for mxu2s_private failed.",
                                __FUNCTION__);
                        return -ENOMEM;
                }
                memset(priv, 0, sizeof(struct mxu2s_private));
                /* set initial values for control structures */
                priv->bytes_in = 0;
                priv->bytes_out = 0;
                priv->opened = 0;

  ep_addr = serial->interface->cur_altsetting->endpoint->desc.bEndpointAddress;
                if (ep_addr & 0x80)
                        priv->portnum = ((ep_addr & 0x0F) / 2) - 1;
                else
                        priv->portnum = ((ep_addr&0x0F) / 2);

                usb_set_serial_port_data(serial->port[i], priv);
                priv->port = serial->port[i];
                init_MUTEX(&priv->sem);
                spin_lock_init (&priv->lock);

        }

        return (0);
} /* mxu2s_startup */

static void mxu2s_shutdown (struct usb_serial *serial)
{
        int i;
        struct mxu2s_private *priv;

        dbg("%s", __FUNCTION__);

        // stop reads and writes on all ports
        for (i=0; i < serial->num_ports; ++i) {
                priv = usb_get_serial_port_data(serial->port[i]);

                kfree(priv);
                usb_set_serial_port_data(serial->port[i], NULL);
        }
        if(usb_get_serial_data(serial) != NULL)
                kfree (usb_get_serial_data(serial));
        usb_set_serial_data(serial, NULL);
} /* mxu2s_shutdown */

static int  mxu2s_open (struct usb_serial_port *port, struct file *filp)
{
        struct mxu2s_private *priv = usb_get_serial_port_data(port);
        int retval = 0;
        int rc;
        int i;

        dbg("%s port %d", __FUNCTION__, port->number);

        if(port_paranoia_check(port, __FUNCTION__))
                return -ENODEV;

        /* set up termios structure */
        priv->termios.c_iflag = port->tty->termios->c_iflag;
        priv->termios.c_oflag = port->tty->termios->c_oflag;
        priv->termios.c_cflag = port->tty->termios->c_cflag;
        priv->termios.c_lflag = port->tty->termios->c_lflag;
        for(i=0; i<NCCS; i++)
                priv->termios.c_cc[i] = port->tty->termios->c_cc[i];
        port->tty->termios->c_iflag &= ~(IXON | IXOFF);
        port->tty->termios->c_cflag = CREAD | B9600 | HUPCL | CLOCAL | CS8;

        priv->read_urb = port->read_urb;

        // reset device
        rc = mxu2s_control_msg(port, MXU2S_RESET, USB_DIR_OUT, 0, NULL, 0);
        if(rc < 0) {
                retval = rc;
                goto openerr;
        }

        // enable interface
        rc = mxu2s_control_msg(port, MXU2S_IFC_ENABLE, USB_DIR_OUT,
                        MXU2S_INTERFACE_ENABLE, NULL, 0);
        if(rc < 0) {
                retval = rc;
                goto openerr;
        }

        priv->cfg.baudrate = (__u16)MXU2S_BAUD_9600;
        priv->cfg.databits = (__u8)MXU2S_DATABITS_8;
        priv->cfg.parity = (__u8)MXU2S_PARITY_NONE;
        priv->cfg.stopbits = (__u8)MXU2S_STOPBITS_1;
        priv->cfg.msr = (__u16)(MXU2S_RTS_ON | MXU2S_DTR_ON);

        // purge
        rc = mxu2s_control_msg(port, MXU2S_PURGE, USB_DIR_OUT,
                (MXU2S_PURGE_IN | MXU2S_PURGE_OUT), NULL, 0);


        if(port->read_urb->status != -EINPROGRESS) {
                port->read_urb->dev = port->serial->dev;
                port->read_urb->transfer_buffer_length =
                        URB_TRANSFER_BUFFER_SIZE;

                usb_fill_bulk_urb(port->read_urb, port->serial->dev,
                        usb_rcvbulkpipe(port->serial->dev,
                                port->bulk_in_endpointAddress),
                        port->read_urb->transfer_buffer,
                        port->read_urb->transfer_buffer_length,
                        mxu2s_read_bulk_callback, port);
                rc = usb_submit_urb(port->read_urb, GFP_KERNEL);
                if(rc) {
                        err("%s - failed submitting read urb, error %d",
                                __FUNCTION__, rc);
                        retval = rc;
                        goto openerr;
                }

        priv->write_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (priv->write_urb == NULL) {
                err("No more urbs???");
        }

        priv->write_urb->transfer_buffer =
                        kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
        if (priv->write_urb->transfer_buffer == NULL) {
            err("%s - out of memory for urb buffers.", __FUNCTION__);
        }

        priv->opened = 1;

        return 0;

openerr:
        return retval;
} /* mxu2s_open */


static void mxu2s_close (struct usb_serial_port *port, struct file *filp)
{
        struct usb_serial *serial;
        struct mxu2s_private *priv = usb_get_serial_port_data(port);
        int rc;

        dbg("%s port %d", __FUNCTION__, port->number);

        serial = get_usb_serial (port, __FUNCTION__);

        if(!serial)
                return;

        mxu2s_wait_until_sent(port, CLOSE_WAIT);
        // set purge
        mxu2s_control_msg(port, MXU2S_PURGE, USB_DIR_OUT,
                        (MXU2S_PURGE_IN | MXU2S_PURGE_OUT), NULL, 0);
        if(port->tty->ldisc.flush_buffer)
                port->tty->ldisc.flush_buffer(port->tty);


        rc = mxu2s_control_msg(port, MXU2S_IFC_ENABLE, USB_DIR_OUT,
                       MXU2S_INTERFACE_DISABLE, NULL, 0);
        if (rc < 0)
                err("Disable Interface failed (error = %d)", rc);

        /* shutdown our bulk reads and writes */

        usb_unlink_urb(priv->write_urb);

        if(priv->write_urb) {
                if(priv->write_urb->transfer_buffer)
                        kfree(priv->write_urb->transfer_buffer);
                usb_free_urb (priv->write_urb);
        }

        if(serial->dev) {

                printk("usb_unlink_urb start\n");
                usb_unlink_urb(priv->read_urb);
                printk("usb_unlink_urb ok\n");
        }

        priv->opened = 0;
        info("mxu2s port stats: %ld bytes in, %ld bytes out",
                priv->bytes_in, priv->bytes_out);
} /* mxu2s_close */


static int mxu2s_write (struct usb_serial_port *port, int from_user,
                           const unsigned char *buf, int count)
{
        struct mxu2s_private *priv = usb_get_serial_port_data(port);
        int result, size;
        int bytes_sent=0;
        struct urb *urb = NULL;

        dbg("%s - port %d", __FUNCTION__, port->number);

        if (count > 0) {
                if (priv->write_urb->status != -EINPROGRESS) {
                        urb = priv->write_urb;
                }

                if (urb==NULL) {
                        dbg("%s - no more free urbs", __FUNCTION__);
                        goto write_exit;
                }
                if (urb->transfer_buffer == NULL) {
                        urb->transfer_buffer =
                                kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC);
                        if (urb->transfer_buffer == NULL) {
                                err("%s - no more kernel memory...",
                                        __FUNCTION__);
                                goto write_exit;
                        }
                }

                size = min (count, port->bulk_out_size);
                size = min (size, URB_TRANSFER_BUFFER_SIZE);

                if (from_user) {
                        if (copy_from_user(urb->transfer_buffer
                                           , buf, size)) {
                                return -EFAULT;
                        }
                } else {
                        memcpy (urb->transfer_buffer,
                                buf, size);
                }
                /* set up our urb */

                usb_fill_bulk_urb(urb, port->serial->dev,
                              usb_sndbulkpipe(port->serial->dev,
                                              port->bulk_out_endpointAddress),
                              urb->transfer_buffer,
                              size,
                              mxu2s_write_bulk_callback,
                              port);

                /* send the data out the bulk port */
                result = usb_submit_urb(urb, GFP_KERNEL);
                if (result) {
                        err("%s - failed submitting write urb, error %d",
                                __FUNCTION__, result);
                        goto write_exit;
                }
                buf += size;
                bytes_sent += size;
                count -= size;

        }
write_exit:
        /* lockless, but it's for debug info only... */
        priv->bytes_out+=bytes_sent;
        return bytes_sent;      /* that's how much we wrote */
} /* mxu2s_write */

static void mxu2s_write_bulk_callback ( struct urb *urb, struct pt_regs *regs)
{
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct usb_serial *serial = port->serial;

        dbg("%s - port %d", __FUNCTION__, port->number);

        if (!serial) {
                dbg("%s - bad serial pointer, exiting", __FUNCTION__);
                return;
        }

        if (urb->status) {
                dbg("%s - nonzero write bulk status received: %d",
                        __FUNCTION__, urb->status);
                return;
        }

        /* from generic_write_bulk_callback */
        schedule_work(&port->work);
} /* klsi_105_write_bulk_completion_callback */


/* return number of characters currently in the writing process */
static int mxu2s_chars_in_buffer (struct usb_serial_port *port)
{
        int chars = 0;
        struct mxu2s_private *priv = usb_get_serial_port_data(port);

        if (priv->write_urb->status == -EINPROGRESS) {
                chars += URB_TRANSFER_BUFFER_SIZE;
        }
        dbg("%s - returns %d", __FUNCTION__, chars);
        return (chars);
}

static int mxu2s_write_room (struct usb_serial_port *port)
{
        int room = 0;
        struct mxu2s_private *priv = usb_get_serial_port_data(port);

        if (priv->write_urb->status != -EINPROGRESS) {
                room += URB_TRANSFER_BUFFER_SIZE;
        }
        else {
                room = 0;
        }

        dbg("%s - returns %d", __FUNCTION__, room);
        return (room);
}

static void mxu2s_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct usb_serial *serial = port->serial;
        struct mxu2s_private *priv = usb_get_serial_port_data(port);
        struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
        int rc;
        int i;
        int bytes_sent;

        dbg("%s - port %d", __FUNCTION__, port->number);

        if (serial_paranoia_check (serial, __FUNCTION__)) {
                dbg("%s - paranoia_check error", __FUNCTION__);
                return;
        }

        /* The urb might have been killed. */
        if (urb->status) {
                dbg("%s - nonzero read bulk status received: %d", __FUNCTION__,
                    urb->status);
                if(urb->status != -EILSEQ)
                        return;
        }
        if (!serial) {
                dbg("%s - bad serial pointer, exiting", __FUNCTION__);
                return;
        }


        if (urb->actual_length != 0) {
                bytes_sent = urb->actual_length;

                tty = port->tty;

                usb_serial_debug_data (__FILE__, __FUNCTION__,
                                       urb->actual_length, data);
                for (i = 0; i < bytes_sent; i++) {
                        /* if we insert more than TTY_FLIPBUF_SIZE characters,
                         * we drop them. */
                        if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
                                tty_flip_buffer_push(tty);
                        }
                        /* this doesn't actually push the data through unless
                         * tty->low_latency is set */
                        tty_insert_flip_char(tty, ((__u8*) data)[i], 0);
                }
                tty_flip_buffer_push(tty);
                /* again lockless, but debug info only */
                priv->bytes_in += bytes_sent;
        /* Continue trying to always read  */
        }

        if(port->read_urb->status != -EINPROGRESS) {
                rc = usb_submit_urb(port->read_urb, GFP_ATOMIC);
                if (rc) {
                        err("%s - failed resubmitting read urb, error %d",
                                __FUNCTION__, rc);
                }
        }

} /* mxu2s_read_bulk_callback */


static void mxu2s_set_termios (struct usb_serial_port *port,
                                  struct termios *old_termios)
{
        struct mxu2s_private *priv = usb_get_serial_port_data(port);
        unsigned int iflag = priv->port->tty->termios->c_iflag;
        unsigned int cflag = priv->port->tty->termios->c_cflag;
        unsigned int old_cflag = old_termios->c_cflag;
        struct mxu2s_port_settings cfg;
        int rc;
        int i;

        /* lock while we are modifying the settings */

        /*
         * Update baud rate
         */
//      if( (cflag & CBAUD) != (old_cflag & CBAUD) ) {
        {
                /* reassert DTR and (maybe) RTS on transition from B0 */
                if( (old_cflag & CBAUD) == B0 ) {
                        dbg("%s: baud was B0", __FUNCTION__);
                }
                switch(cflag & CBAUD) {
                case B0: /* handled below */
                        priv->cfg.msr = (MXU2S_DTR_OFF|MXU2S_RTS_OFF);
                        rc = mxu2s_control_msg(port, MXU2S_SET_MHS,
                                        USB_DIR_OUT, priv->cfg.msr, NULL, 0);
                        if (rc < 0) {
                          err("Setting modem control failed (error = %d)", rc);
                        }
                        break;
                case B600: priv->cfg.baudrate = MXU2S_BAUD_600;
                        break;
                case B1200: priv->cfg.baudrate = MXU2S_BAUD_1200;
                        break;
                case B2400: priv->cfg.baudrate = MXU2S_BAUD_2400;
                        break;
                case B4800: priv->cfg.baudrate = MXU2S_BAUD_4800;
                        break;
                case B9600: priv->cfg.baudrate = MXU2S_BAUD_9600;
                        break;
                case B19200: priv->cfg.baudrate = MXU2S_BAUD_19200;
                        break;
                case B38400: priv->cfg.baudrate = MXU2S_BAUD_38400;
                        break;
                case B57600: priv->cfg.baudrate = MXU2S_BAUD_57600;
                        break;
                case B115200: priv->cfg.baudrate = MXU2S_BAUD_115200;
                        break;
                default:
                        err(" MOXA USB->Serial converter:"
                            " unsupported baudrate request, using default"
                            " of 9600");
                        priv->cfg.baudrate = MXU2S_BAUD_9600;
                        break;
                }
                old_termios->c_cflag &= ~CBAUD;
                if(priv->cfg.baudrate == MXU2S_BAUD_9600)
                        old_termios->c_cflag |= B9600;
                else
                        old_termios->c_cflag |= (cflag & CBAUD);
                rc = mxu2s_control_msg(port, MXU2S_SET_BAUDDIV, USB_DIR_OUT,
                                priv->cfg.baudrate, NULL, 0);

                if (rc < 0) {
                        err("Change baudrate failed (error = %d)", rc);
                    info("%s - baudrate %x",__FUNCTION__, priv->cfg.baudrate);
                }
                dbg("%s - baudrate %d", __FUNCTION__, priv->cfg.baudrate);
        }

//      if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
        {
                /* set the number of data bits */
                switch (cflag & CSIZE) {
                case CS5:
                        priv->cfg.databits = MXU2S_DATABITS_5;
                        return ;
                case CS6:
                        priv->cfg.databits = MXU2S_DATABITS_6;
                        return ;
                case CS7:
                        priv->cfg.databits = MXU2S_DATABITS_7;
                        break;
                case CS8:
                        priv->cfg.databits = MXU2S_DATABITS_8;
                        break;
                default:
                        err("CSIZE was not CS5-CS8, using default of 8");
                        priv->cfg.databits = MXU2S_DATABITS_8;
                        break;
                }
                old_termios->c_cflag &= ~CSIZE;
                old_termios->c_cflag |= (cflag & CSIZE);
        }

        /*
         * Update line control register (LCR)
         */
//      if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
//          || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) {
        {
                priv->cfg.parity = 0;

                if (cflag & PARENB) {
                        priv->cfg.parity |= (cflag & PARODD) ?
                                MXU2S_PARITY_ODD : MXU2S_PARITY_EVEN;
                }
                else {
                        priv->cfg.parity |= MXU2S_PARITY_NONE;
                }
                old_termios->c_cflag &= ~(PARENB|PARODD);
                old_termios->c_cflag |= (cflag & (PARENB|PARODD));
        }

//      if ((old_cflag & CSTOPB) != (cflag & CSTOPB) ) {
        {
                priv->cfg.stopbits |= (cflag & CSTOPB) ?
                        MXU2S_STOPBITS_2 : MXU2S_STOPBITS_1;

                old_termios->c_cflag &= ~CSTOPB;
                old_termios->c_cflag |= (cflag & CSTOPB);
        }

        rc = mxu2s_control_msg(port, MXU2S_SET_LINE_CTL, USB_DIR_OUT,
                (priv->cfg.databits<<8)|priv->cfg.parity|priv->cfg.stopbits,
                NULL,0);
        if (rc < 0) {
                err("Change port settings failed (error = %d)", rc);
                info("%s - databits %d, parity %d, stopbits %d", __FUNCTION__,
                    priv->cfg.databits, priv->cfg.parity, priv->cfg.stopbits);
                return;
        }

//      if((iflag & IXOFF) != (old_iflag & IXOFF)
//          || (iflag & IXON) != (old_iflag & IXON)
//          ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
        {

                for(i=0; i<16; i++)
                        priv->setflow[i] = 0;
                priv->setflow[3] = 0x80;
                priv->setflow[8] = 0x50;
                priv->setflow[12] = 0xC8;

                if(cflag & CRTSCTS) {
                        priv->setflow[0] |= 0x09;
                        priv->setflow[4] |= 0x40;
                        old_termios->c_cflag |= CRTSCTS;
                }
                else {
                        priv->setflow[0] |= 0x01;
                        priv->setflow[4] |= 0x40;
                        old_termios->c_cflag &= ~CRTSCTS;
                }

                if((iflag & IXON) || (iflag & IXOFF)) {
                        priv->setflow[11] = 0x30;
                        priv->setflow[15] = 0x30;
                }

                if(iflag & IXON) {
                        priv->setflow[4] |= 0x01;
                        old_termios->c_iflag |= IXON;
                }
                else {
                        priv->setflow[4] &= ~(0x01);
                        old_termios->c_iflag &= ~IXON;
                }

                if(iflag & IXOFF) {
                        priv->setflow[4] |= 0x02;
                        old_termios->c_iflag |= IXOFF;
                }
                else {
                        priv->setflow[4] &= ~(0x02);
                        old_termios->c_iflag &= ~IXOFF;
                }

                rc = mxu2s_control_msg(port, MXU2S_SET_FLOW, USB_DIR_OUT,
                                0, priv->setflow, 16);
                if(rc < 0) {
                        err("Setting flow control failed (error = %d)", rc);
                }

        }
        memcpy (&cfg, &priv->cfg, sizeof(cfg));

} /* mxu2s_set_termios */

static int mxu2s_ioctl (struct usb_serial_port *port, struct file * file,
                           unsigned int cmd, unsigned long arg)
{

        dbg("%scmd=0x%x", __FUNCTION__, cmd);

        /* Based on code from acm.c and others */
        switch (cmd) {
        case TIOCMIWAIT:
                /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
                /* TODO */
                dbg("%s - TIOCMIWAIT not handled", __FUNCTION__);
                return -ENOIOCTLCMD;

        case TIOCGICOUNT:
                /* return count of modemline transitions */
                /* TODO */
                dbg("%s - TIOCGICOUNT not handled", __FUNCTION__);
                return -ENOIOCTLCMD;
        default:
                dbg("%s: arg not supported - 0x%04x", __FUNCTION__,cmd);
                return(-ENOIOCTLCMD);
                break;
        }
        return 0;
} /* mxu2s_ioctl */

static int mxu2s_control_msg (struct usb_serial_port *port,
        unsigned char request, unsigned char direction,
        unsigned int value, void *buf, unsigned int buf_len)
{
        struct usb_serial *serial = port->serial;
        int rc;
        struct mxu2s_private *priv = usb_get_serial_port_data(port);

//      down(&priv->sem);

        if((direction & USB_DIR_IN)) {
                rc = usb_control_msg(serial->dev,
                        usb_rcvctrlpipe(serial->dev, 0), request,
                        direction | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                        value, priv->portnum, buf, buf_len, MOXA_TIMEOUT);
        }
        else {
                rc = usb_control_msg(serial->dev,
                        usb_sndctrlpipe(serial->dev, 0), request,
                        direction | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                        value, priv->portnum, buf, buf_len, MOXA_TIMEOUT);
        }

//      up(&priv->sem);

        if(rc < 0) {
                err("Send control message %d failed (error = %d)", request, rc);
        }

        return rc;
}


static void mxu2s_wait_until_sent (struct usb_serial_port *port, int timeout)
{
        int len,old_len=0;

        if(!timeout)
                timeout = MAX_SCHEDULE_TIMEOUT;

        len = mxu2s_get_queue_value(port);
        if(len <= 0)
                return;
        while(1) {
                if(len && len != old_len) {
                        timeout = jiffies + timeout;
                        old_len = len;
                }
                schedule_timeout(1);
                if(jiffies > timeout){
                        break;
                }
                len = mxu2s_get_queue_value(port);
                if(len <= 0)
                        break;
        }
        return;
}

static int mxu2s_get_queue_value (struct usb_serial_port *port)
{
        int rc;
        unsigned char comm[16];

        rc = mxu2s_control_msg(port, MXU2S_GET_COMM_STATUS,
                        USB_DIR_IN, 0, comm, 16);
        if(rc < 0) {
                err("Get comm status failed (error = %d)", rc);
                return rc;
        }

        rc = comm[12];

        return rc;
}

/*****************************************************************/

static int __init mxu2s_init (void)
{
        int retval;

        retval = usb_serial_register(&mxu2s_np1240_device);
        if(retval)
                goto failed_np1240_device_register;
        retval = usb_register(&mxu2s_driver);
        if(retval)
                goto failed_moxa_device_register;

        info(DRIVER_DESC " " DRIVER_VERSION);
        return 0;

failed_moxa_device_register:
        usb_serial_deregister(&mxu2s_np1240_device);
failed_np1240_device_register:

        return retval;
}

static void __exit mxu2s_exit (void)
{
        usb_deregister(&mxu2s_driver);
        usb_serial_deregister(&mxu2s_np1240_device);

}


module_init (mxu2s_init);
module_exit (mxu2s_exit);

MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");


MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "enable extensive debugging messages");






-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.  How far can you shotput
a projector? How fast can you ride your desk chair down the office luge track?
If you want to score the big prize, get to know the little guy.
Play to win an NEC 61" plasma display: http://www.necitguy.com/?r 
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to