Re: Support for Quatech ESU2-100 USB 2.0 8-port serial adaptor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am Freitag, 19. Juni 2009 13:51:25 schrieb Richard Ash:

> In particular, the USB 2.0 series devices have only on bulk in and out
> endpoint, which is shared between the multiple ports using headers on
> each URB (as far as I can see based on the vendor driver code). I'm not
> clear what the best way of handling this is - do I need to initialise
> the endpoints / URBs, and if so, what function do it do it in? Will
> keeping the URBs in port 0's structure and then referencing them there
> as required be a good idea or a bad one?

In this case it is easiest to allocate the write URBs dynamically
and free them on completion.

	Regards
		Oliver

> +/* structure in which to keep all the messy stuff that this driver
> needs
> + * alongside the usb_serial_port structure */
> +struct quatech2_port {
> +	int	magic;
> +	char	active;		/* someone has this device open */
> +	unsigned char		*xfer_to_tty_buffer;
> +	wait_queue_head_t	wait;
> +	int	open_count;	/* number of times this	port has been opened */
> +	struct semaphore	sem;	/* locks this structure */
> +	__u8	shadowLCR;	/* last LCR value received */
> +	__u8	shadowMCR;	/* last MCR value received */
> +	__u8	shadowMSR;	/* last MSR value received */
> +	__u8	shadowLSR;	/* last LSR value received */
> +	char	open_ports;	/* ports open on whole device */
> +	char	RxHolding;
> +	char	Rcv_Flush;
> +	char	Xmit_Flush;
> +	char	closePending;
> +	char	fifo_empty_flag;
> +	int	xmit_pending_bytes;
> +	int	xmit_fifo_room_bytes;
> +	struct semaphore	pend_xmit_sem;	/* locks this structure */
> +	spinlock_t lock;
> +};

What do you need two semaphores for?

> +static int qt2_attach(struct usb_serial *serial)
> +{
> +	struct usb_serial_port *port;
> +	struct quatech2_port *qt2_port;
> +	int i;
> +
> +	/* Now setup per port private data, which replaces all the things
> +	* that quatech added to standard kernel structures in their driver */
> +	for (i = 0; i < serial->num_ports; i++) {
> +		port = serial->port[i];
> +		qt2_port = kzalloc(sizeof(*qt2_port), GFP_KERNEL);
> +		if (!qt2_port) {
> +			dbg("%s: kmalloc for quatech2_port (%d) failed!.",
> +			    __func__, i);
> +			return -ENOMEM;

memory leak in error case

> +		}
> +		spin_lock_init(&qt2_port->lock);
> +		if (i == 0)
> +			qt2_port->open_ports = 0; /* no ports */
> +		else
> +			qt2_port->open_ports = -1; /* unused */
> +
> +		usb_set_serial_port_data(port, qt2_port);
> +
> +	}
> +	/* switch on power to the hardware */
> +	if (qt2_boxpoweron(serial) < 0) {
> +		dbg("qt2_boxpoweron() failed");
> +		goto startup_error;
> +	}
> +	/* set all ports to RS232 mode */
> +	for (i = 0; i < serial->num_ports; ++i) {
> +		if (qt2_boxsetQMCR(serial, i, QU2BOX232) < 0) {
> +			dbg("qt2_boxsetQMCR() on port %d failed",
> +				i);
> +			goto startup_error;
> +		}
> +	}
> +
> +	return 0;
> +
> +startup_error:
> +	for (i = 0; i < serial->num_ports; i++) {
> +		port = serial->port[i];
> +		qt2_port = qt2_get_port_private(port);
> +		kfree(qt2_port);
> +		usb_set_serial_port_data(port, NULL);
> +	}
> +
> +	dbg("Exit fail %s\n", __func__);
> +	return -EIO;
> +}
> +
> +static void qt2_release(struct usb_serial *serial)
> +{
> +	struct usb_serial_port *port;
> +	struct quatech2_port *qt_port;
> +	int i;
> +
> +	dbg("enterting %s", __func__);
> +
> +	for (i = 0; i < serial->num_ports; i++) {
> +		port = serial->port[i];
> +		if (!port)
> +			continue;
> +
> +		qt_port = usb_get_serial_port_data(port);
> +		kfree(qt_port);
> +		usb_set_serial_port_data(port, NULL);
> +	}
> +}
> +/* This function is called once per serial port on the device.
> + * The tty_struct and the usb_serial_port belong to this port,
> + * i.e. there are multiple ones for a multi-port device.
> + * However the usb_serial_port structure has a back-pointer
> + * to the parent usb_serial structure which belongs to the device,
> + * so we can access either the device-wide information or
> + * any other port's information (because there are also forward
> + * pointers) via that pointer.
> + * This is most helpful if the device shares resources (e.g. end
> + * points) between different ports
> + */
> +int qt2_open(struct tty_struct *tty,
> +	    struct usb_serial_port *port, struct file *filp)
> +{
> +	struct usb_serial *serial;	/* device structure */
> +	struct usb_serial_port *port0;	/* first port structure on device */
> +	struct quatech2_port *port_extra;	/* extra data for this port */
> +	struct quatech2_port *port0_extra;	/* extra data for first port */
> +	struct qt2_status_data ChannelData;
> +	unsigned short default_divisor = QU2BOXSPD9600;
> +	unsigned char  default_LCR = SERIAL_8_DATA;
> +	int status;
> +
> +	if (port_paranoia_check(port, __func__))
> +		return -ENODEV;
> +
> +	dbg("%s - port %d\n", __func__, port->number);
> +
> +	serial = port->serial;	/* get the parent device structure */
> +	if (serial_paranoia_check(serial, __func__))
> +		return -ENODEV;
> +	port0 = serial->port[0]; /* get the first port's device structure */
> +
> +	port_extra = qt2_get_port_private(port);
> +	port0_extra = qt2_get_port_private(port0);
> +
> +	if (port_extra == NULL || port0_extra == NULL)
> +		return -ENODEV;
> +
> +	usb_clear_halt(serial->dev, port->write_urb->pipe);
> +	usb_clear_halt(serial->dev, port->read_urb->pipe);

Is this really needed?

> +	port0_extra->open_ports++;
> +
> +	/* FIXME: are these needed?  Does it even do anything useful? */
> +	/* get the modem and line status values from the UART */
> +	status = qt2_openboxchannel(serial, port->number,
> +			&ChannelData);
> +	if (status < 0) {
> +		dbg("qt2_openboxchannel on channel %d failed",
> +		    port->number);
> +		return status;
> +	}
> +	port_extra->shadowLSR = ChannelData.line_status &
> +			(SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE |
> +			SERIAL_LSR_BI);
> +
> +	port_extra->shadowMSR = ChannelData.modem_status &
> +			(SERIAL_MSR_CTS | SERIAL_MSR_DSR | SERIAL_MSR_RI |
> +			SERIAL_MSR_CD);
> +
> +	port_extra->fifo_empty_flag = true;
> +	dbg("qt2_openboxchannel on channel %d completed.",
> +	    port->number);
> +
> +	/* Set Baud rate to default and turn off flow control here */
> +	status = qt2_conf_uart(serial, port->number, default_divisor,
> +				default_LCR);
> +	if (status < 0) {
> +		dbg("qt2_conf_uart() failed on channel %d",
> +		    port->number);
> +		return status;
> +	}
> +	dbg("qt2_conf_uart() completed on channel %d",
> +		port->number);
> +
> +	dbg("port number is %d", port->number);
> +	dbg("serial number is %d", port->serial->minor);
> +
> +	/* We need to set up URBs and endpoints here. We only
> +	 * have one pair of endpoints per device, so in fact
> +	 * we only need to set up endpoints on the first time
> +	 * round, not subsequent ones.
> +	 * When we do a write to a port, we will use the same URBs
> +	 * regadless of the port, with a 5-byte header added on to
> +	 * tell the box which port it should eventually come out of,
> +	 * so the same URBs need to be visible to write calls
> +	 * regardless of which port is being written.
> +	 * To this end we actually keep the relevant endpoints and
> +	 * URBs in port 0's structure, because that's always there
> +	 * and avoids providing our own duplicate members in some
> +	 * user data structure for the same purpose.
> +	 */
> +	if (port0_extra->open_ports == 1) {
> +		/* this is first port to be opened */
> +	}

What's the purpose of this?
> +
> +	dbg("Bulkin endpoint is %d", port->bulk_in_endpointAddress);
> +	dbg("BulkOut endpoint is %d", port->bulk_out_endpointAddress);
> +	dbg("Interrupt endpoint is %d", port->interrupt_in_endpointAddress);
> +
> +	/* initialize our wait queues */
> +	init_waitqueue_head(&port_extra->wait);

Should be done in attach()

> +
> +	/* remember to store port_extra and port0 back again at end !*/
> +	qt2_set_port_private(port, port_extra);
> +	qt2_set_port_private(serial->port[0], port0_extra);

Where do you change them?
> +
> +	return 0;
> +}
> +
> +/* internal, private helper functions for the driver */
> +
> +/* Power up the FPGA in the box to get it working */
> +static int qt2_boxpoweron(struct usb_serial *serial)
> +{
> +	int result;
> +	__u8  Direcion;
> +	unsigned int pipe;
> +	Direcion = USBD_TRANSFER_DIRECTION_OUT;
> +	pipe = usb_rcvctrlpipe(serial->dev, 0);
> +	result = usb_control_msg(serial->dev, pipe, QT_SET_GET_DEVICE,
> +				Direcion, QU2BOXPWRON, 0x00, NULL, 0x00,
> +				5000);
> +	return result;
> +}
> +
> +/*
> + * qt2_boxsetQMCR Issue a QT_GET_SET_QMCR vendor-spcific request on the
> + * default control pipe. If successful return the number of bytes
> written,
> + * otherwise return a negative error number of the problem.
> + */
> +static int qt2_boxsetQMCR(struct usb_serial *serial, __u16 Uart_Number,
> +			  __u8 QMCR_Value)
> +{
> +	int result;
> +	__u16 PortSettings;
> +
> +	PortSettings = (__u16)(QMCR_Value);
> +
> +	dbg("%s(): Port = %d, PortSettings = 0x%x", __func__,
> +			Uart_Number, PortSettings);
> +
> +	result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
> +				QT_GET_SET_QMCR, 0x40, PortSettings,
> +				(__u16)Uart_Number, NULL, 0, 5000);
> +	return result;
> +}

> +static inline struct quatech2_port *qt2_get_port_private(struct
> usb_serial_port
> +		*port)
> +{
> +	return (struct quatech2_port *)usb_get_serial_port_data(port);
> +}
> +
> +static inline void qt2_set_port_private(struct usb_serial_port *port,
> +				       struct quatech2_port *data)
> +{
> +	usb_set_serial_port_data(port, (void *)data);
> +}

These two functions serve no purpose.


> +static void __exit quausb2_usb_exit(void)
> +{
> +	usb_deregister(&quausb2_usb_driver);
> +	usb_serial_deregister(&quatech2_device);
> +}

I suggest you first deregister the serial device.

> +
> +module_init(quausb2_usb_init);
> +module_exit(quausb2_usb_exit);
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");
> +
> +module_param(debug, bool, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(debug, "Debug enabled or not");
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux