Page 1 of 1

OHCI descriptor lists

Posted: Sun Sep 08, 2013 12:03 pm
by SpyderTL
Anyone here know enough about OHCI to answer this question for me??

According to the specs, each endpoint descriptor (ED) has a "head" transfer descriptor (TD) field and a "tail" TD field, which are supposed to be filled with the addresses of the first and the last TD for that ED. The specs state that if the "head" TD and "tail" TD fields have the same value, the TD list for that ED is empty. Makes sense so far.

So, I'm initializing my Head TD and Tail TD fields to zero. Now when I want to add a TD to the list, I'm not sure what to do. If I set both the Head and Tail TD fields to the address of my TD, the controller should treat the list as empty. So I'm thinking that either the Head or Tail field should be left at zero, but which one?

Also, the TD structure has a NextTD field that points to the next TD in the list. In my case, I only have one TD, so what should the NextTD be set to? Zero? Itself?

I'm just trying to figure out what the controller is expecting, and the documentation that I've got isn't clear about what the fields should be set to in this scenario.

Thanks for the help.

- Joshua

Re: OHCI descriptor lists

Posted: Sun Sep 08, 2013 12:35 pm
by h0bby1
you need to add some td at the end, like zero length read or write empty packet to tail the transfer

Re: OHCI descriptor lists

Posted: Sun Sep 08, 2013 1:11 pm
by SpyderTL
Now that you mention it, I guess you would never have just a single packet, since you have to request a status result packet after every command packet.

Alright then, never mind. Forget I said anything. :)

Re: OHCI descriptor lists

Posted: Sun Sep 08, 2013 11:31 pm
by h0bby1
status and data packet might go in a separate transfer list, i had issue before by putting the setup/data and status in the same transfer list, normally you should have 3 seperate transfer list for setup / data / status, and both setup and status can often be a single packet, other drivers often seem to do this as well, but i'm not sure, doc is never very clear about that, but some say they must be 3 totally different stage, and internally each of the three transfer can get some sort of 'internal' status thing as well

my routine to create a control transfer for ohci looks like that


Code: Select all


usb_td_ptr_t	 create_control_status	(unsigned int dir)
{
	usb_td_ptr_t		td;

	td					=	get_new_td();


	td->token			=	0;
	td->token			=	write_bits(td->token  ,0						  ,18  ,1);	//buffer rounding
	td->token			=	write_bits(td->token  ,dir						  ,19  ,2);	//direction
	td->token			=	write_bits(td->token  ,7						  ,21  ,3);	//delay int
	td->token			=	write_bits(td->token  ,1						  ,24  ,1);	//data toggle
	td->token			=	write_bits(td->token  ,1						  ,25  ,1);	//data toggle TD
	td->token			=	write_bits(td->token  ,0						  ,26  ,2); //n errors
	td->token			=	write_bits(td->token  ,0						  ,28  ,4);	// status of the last attempted transaction

	td->buffer_phy		=	PTR_NULL;
	td->end_buffer_phy	=	PTR_NULL;
	
	return td;
}


int do_usb_ctrl_transfer			(struct usb_device *dev,usb_setup_packet_t	*setup_packet,mem_ptr data,mem_size size,unsigned int packet_size,int *ret_code)
{
	usb_td_ptr_t			setup_td;
	usb_td_ptr_t			data_td;
	usb_td_ptr_t			status_td;
	usb_td_ptr_t			dummy_td;
	struct td_chain_t		chain;
	int						ret;

	//kernel_log		(kernel_log_id,"control transfer setup \n");

	dummy_td				=	create_control_status		(TD_TOKEN_IN);
	dummy_td->link_phy		=	PTR_NULL;

	setup_td				=	create_setup_td						(setup_packet);
	setup_td->link_phy		=	dummy_td;

	build_td_chain			(&chain,setup_td);

	ret						=	controller_usb_ctrl_transfer		(dev->addr,dev->speed,dev->device_desc.bMaxPacketSize,setup_td,dummy_td);
	*ret_code				=	((setup_td->token)>>28);

	remove_used_td			(&chain);

	if(!ret)
	{
		kernel_log		(kernel_log_id,"control transfer failed in setup stage, return code : ");
		writeint		(*ret_code,16);
		writestr		("\n");

		return 0;
	}	

	if(size>0)
	{
		//kernel_log		(kernel_log_id,"control transfer data \n");
		dummy_td				=	create_control_status		(TD_TOKEN_IN);
		dummy_td->link_phy		=	PTR_NULL;

		if(setup_packet->bmRequestType&128)
			data_td					=	create_transfer				(dev,data,size,packet_size,TD_TOKEN_IN ,&dev->toggle_control,dummy_td);
		else
			data_td					=	create_transfer				(dev,data,size,packet_size,TD_TOKEN_OUT,&dev->toggle_control,dummy_td);


		build_td_chain			(&chain,data_td);

		ret						=	controller_usb_ctrl_transfer		(dev->addr,dev->speed,dev->device_desc.bMaxPacketSize,data_td,dummy_td);
		*ret_code				=	((data_td->token)>>28);
		if(!ret)
		{
			kernel_log		(kernel_log_id,"control transfer failed in data stage, return code : ");
			writeint		(*ret_code,16);
			writestr		("\n");
			return 0;
		}	

		remove_used_td			(&chain);
	}

	//kernel_log		(kernel_log_id,"control transfer status \n");
	dummy_td				=	create_control_status		(TD_TOKEN_IN);
	dummy_td->link_phy		=	PTR_NULL;

	if(setup_packet->bmRequestType&128)
		status_td				=	create_control_status		(TD_TOKEN_OUT);
	else
		status_td				=	create_control_status		(TD_TOKEN_IN);

	status_td->link_phy		=	dummy_td;


	build_td_chain			(&chain,status_td);

	ret						=	controller_usb_ctrl_transfer		(dev->addr,dev->speed,dev->device_desc.bMaxPacketSize,status_td,dummy_td);
	*ret_code				=	((status_td->token)>>28);

	remove_used_td			(&chain);
	if(!ret)
	{
		kernel_log		(kernel_log_id,"control transfer failed in status stage, return code : ");
		writeint(*ret_code,16);
		writestr("\n");
		return 0;
	}	

	//kernel_log		(kernel_log_id,"control transfer end \n");
	
	return 1;
}

it's not interupt driven transfer, there is a loop in 'controller_usb_ctrl_transfer' after the control list is enabled with the head/tail set that check the 'done_head' to determine when the transfer is finished or stall, control transfer are always synchronous with this system, it just catch the interupt to reset the done_head field and set the status register