Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Sep 2010 15:56:49 +0200
From:      Piet Skiet <tuksgig@gmail.com>
To:        Hans Petter Selasky <hselasky@c2i.net>
Cc:        freebsd-usb@freebsd.org
Subject:   Re: USB synchronous control transfers (for usb-to-serial)
Message-ID:  <AANLkTi=Erhq_2kYg5D5teQoOoOLE=3BSEF=yE==BoM-a@mail.gmail.com>
In-Reply-To: <201008310947.07460.hselasky@c2i.net>
References:  <AANLkTinr3KbWOqgQoKVvF84H2c02fHh9SQRDeUkSRn4B@mail.gmail.com> <201008302113.33960.hselasky@c2i.net> <AANLkTinuHO1-23K6BW%2Bta3xZ4a6ObKzPyRd6=xAmE0S2@mail.gmail.com> <201008310947.07460.hselasky@c2i.net>

index | next in thread | previous in thread | raw e-mail

[-- Attachment #1 --]
On Tue, Aug 31, 2010 at 9:47 AM, Hans Petter Selasky <hselasky@c2i.net> wrote:
> On Tuesday 31 August 2010 09:39:46 Piet Skiet wrote:
>> On Mon, Aug 30, 2010 at 9:13 PM, Hans Petter Selasky <hselasky@c2i.net>
> wrote:
>> > On Monday 30 August 2010 14:41:56 Piet Skiet wrote:
>> >> On Mon, Aug 30, 2010 at 10:51 AM, Piet Skiet <tuksgig@gmail.com> wrote:
>> >> > Hi,
>> >> >
>> >> > Can anyone clarify how to go about doing a synchronous usb control
>> >> > transfer, similar to Linux's usb_control_msg? I want to implement the
>> >> > TIOCMGET ioctl on a usb-to-serial converter. The Linux driver uses
>> >> > synchronous control messages to read DCE and CTS serial pin status,
>> >> > and I want to do something similar in FreeBSD. The usbdi(9) manpage
>> >> > talks about control transfers using usbd_transfer_submit, but they're
>> >> > not synchronous. What about using usbd_transfer_drain? Is there an
>> >> > example driver showing setting up and doing control transfers?
>> >> >
>> >> > The ucom driver seems to only implement TIOCSBRK and TIOCCBRK iotcls
>> >> > at the moment.
>> >> >
>> >> > Thanks
>> >>
>> >> Scanning through the ucom code, it seems to me that the
>> >> usbd_do_request_proc has somehting to do with blocking control
>> >> transfers. Am I on the right track here?
>> >
>> > Yes, this is correct. You have to re-format the do request information a
>> > little bit compared with Linux. This function is supposed to be called
>> > from a UCOM callback. Please also check recent changes in USB P4:
>> >
>> > http://p4web.freebsd.org/@md=d&cd=//depot/projects/usb/src/sys/dev/usb/co
>> > ntroller/&cdf=//depot/projects/usb/src/sys/dev/usb/serial/usb_serial.c&c=
>> > LJN@//depot/projects/usb/src/sys/dev/usb/serial/usb_serial.c?ac=22
>> >
>> > --HPS
>>
>> OK, but I'm still a bit confused. I'm not sure in which callback to
>> put the usb_do_request. For instance, the driver that I'm interested
>> in is the uslcom.c driver for the cp210x usb-to-serial converter. It
>> has two usb_config structs defined of type UE_BULK for read and write
>> transfers. Do I need to add a third usb_config struct for UE_CONTROL?
>> Should the usb_do_request then be called from the UE_CONTROL callback?
>
> Hi,
>
> The usb_config's are for asynchronous operation. The usbd_do_request_proc()
> function is synchronous. I.E. it completes when it returns. The reason we have
> this variant is to allow smooth exit and entry of the mutex which is locked
> when you get callbacks from UCOM. All UCOM callbacks are pre-locked, and if
> you don't exit the lock the kernel will complain.
>
>> The plan is to add a .ucom_ioctl to the uslcom.c driver and implement
>> the TIOCMGET directly in the driver ioctl using synchronous usb
>> transfers. I've already tested the .ucom_ioctl override and it works,
>> now I just need to figure out how to do the usb transfers.
>>
>> BTW, the FTDI driver (uftdi.c) does things differently. It updates the
>> msr (modem status register?) in the bulk read callback and then calls
>> ucom_status_change to update any changes to ucom.
>
> The FTDI driver uses another mechanism to transfer that information. Probably
> what you want to do is to add a timer/watchdog to poll that information
> regularly. Then you can use a single asynchronous control transfer, and set
> the interval of the transfer to 250ms, for example, so that you don't need to
> allocate a separate timer to do this. Or maybe 100ms is better. You need to
> test this.
>
> --HPS
>

Hi,

Attached is the patch adding a 250ms polling control transfer to
update the port status flags. I've also added CRTSCTS hardware flow
control. I did some tests with minicom and with gtkterm. Minicom works
perfectly, but gtkterm has problems at baud 115200 and also with
hardware flow control. I also testes the Prolific driver (uplcom) with
minicom and gtkterm, and gtkterm has the same problems at 115200 baud,
so I'm assuming it's  a gtkterm bug (not maintained anymore).

BTW, do the usb-to-serial drivers need the force_short_xfer usb
transfer flag? gtkterm seems to work slightly better without the
flag...

Anyway, thanks for the help so far

[-- Attachment #2 --]
67d66
< /* Request types */
71d69
< /* Request codes */
77,78d74
< #define USLCOM_RCTRL		0x08
< #define USLCOM_SET_FLOWCTRL	0x13
80d75
< /* USLCOM_UART values */
84d78
< /* USLCOM_CTRL/USLCOM_RCTRL values */
91d84
< #define USLCOM_CTRL_RI		0x0040
94d86
< /* USLCOM_BAUD_RATE values */
97d88
< /* USLCOM_DATA values */
99a91
> 
106d97
< /* USLCOM_BREAK values */
110,117d100
< /* USLCOM_SET_FLOWCTRL values - 1st word */
< #define USLCOM_FLOW_DTR_ON	0x00000001
< #define USLCOM_FLOW_CTS_HS	0x00000008 /* CTS handshake */
< #define USLCOM_FLOW_RESERVED	0xFFFFFF80
< /* USLCOM_SET_FLOWCTRL values - 2nd word */
< #define USLCOM_FLOW_RTS_ON	0x00000040
< #define USLCOM_FLOW_RTS_HS	0x00000080 /* RTS handshake */
< 
121d103
< 	USLCOM_CTRL_DT_RD,
143d124
< static usb_callback_t uslcom_control_callback;
166c147
< 		.flags = {.pipe_bof = 1,/*.force_short_xfer = 1,*/},
---
> 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
178,186d158
< 	[USLCOM_CTRL_DT_RD] = {
< 		.type = UE_CONTROL,
< 		.endpoint = 0x00,
< 		.direction = UE_DIR_ANY,
< 		.interval = 250, /* poll status every 250 ms */
< 		.bufsize = USLCOM_BULK_BUF_SIZE,
< 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
< 		.callback = &uslcom_control_callback,
< 	},
293d264
< 	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
338,339d308
< 	/* Start polling status */
< 	usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
348,350d316
< 	/* Stop polling status */
< 	usbd_transfer_stop(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
< 
425d390
< 	uint32_t flowctrl[4];
476,495d440
< 	
< 	if (t->c_cflag & CRTSCTS) {
< 		flowctrl[0] = USLCOM_FLOW_RESERVED | USLCOM_FLOW_DTR_ON | 
< 			USLCOM_FLOW_CTS_HS;
< 		flowctrl[1] = USLCOM_FLOW_RTS_HS;
< 	} else {
< 		flowctrl[0] = USLCOM_FLOW_RESERVED | USLCOM_FLOW_DTR_ON;
< 		flowctrl[1] = USLCOM_FLOW_RTS_ON;
< 	}
< 	req.bmRequestType = USLCOM_WRITE;
< 	req.bRequest = USLCOM_SET_FLOWCTRL;
< 	USETW(req.wValue, 0);
< 	USETW(req.wIndex, USLCOM_PORT_NO);
< 	USETW(req.wLength, sizeof(flowctrl));
< 
<         if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
< 	    &req, flowctrl, 0, 1000)) {
< 		DPRINTF("Set flowcontrol failed (ignored)\n");
< 	}
< 	
592,646d536
< uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
< {
< 	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
< 	struct usb_page_cache *pc;
< 	uint8_t buf;
< 	struct usb_device_request req;
< 	uint8_t msr = 0;
< 
< 	switch (USB_GET_STATE(xfer)) {
< 	case USB_ST_TRANSFERRED:
< 		pc = usbd_xfer_get_frame(xfer, 1);
< 		usbd_copy_out(pc, 0, &buf, sizeof(buf));
< 		if (buf & USLCOM_CTRL_CTS)
< 			msr |= SER_CTS;
< 		if (buf & USLCOM_CTRL_DSR)
< 			msr |= SER_DSR;
< 		if (buf & USLCOM_CTRL_RI)
< 			msr |= SER_RI;
< 		if (buf & USLCOM_CTRL_DCD)
< 			msr |= SER_DCD;
< 
< 		if (msr != sc->sc_msr) {
< 			DPRINTF("status change msr=0x%02x (was 0x%02x)\n", msr, sc->sc_msr);
< 			sc->sc_msr = msr;
< 			ucom_status_change(&sc->sc_ucom);
< 		}
< 		/* no break */
< 	case USB_ST_SETUP:
< tr_setup:		
< 		req.bmRequestType = USLCOM_READ;
< 		req.bRequest = USLCOM_RCTRL;
< 		USETW(req.wValue, 0);
< 		USETW(req.wIndex, 0);
< 		USETW(req.wLength, sizeof(buf));
< 		
< 		usbd_xfer_set_frames(xfer, 2);
< 		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
< 		usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
< 
< 		pc = usbd_xfer_get_frame(xfer, 0);
< 		usbd_copy_in(pc, 0, &req, sizeof(req));
< 		usbd_transfer_submit(xfer);
< 		return;
< 
< 	default:			/* Error */
< 		if (error != USB_ERR_CANCELLED) {
< 			/* try to clear stall first */
< 			usbd_xfer_set_stall(xfer);
< 			goto tr_setup;
< 		}
< 		return;
< 	}
< }
< 
< static void
help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?AANLkTi=Erhq_2kYg5D5teQoOoOLE=3BSEF=yE==BoM-a>