⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb-uhci.c

📁 linux客户机函数定义的实际例子
💻 C
📖 第 1 页 / 共 5 页
字号:
		unlink_td (s, td, 0); // no physical unlink
		delete_desc (s, td);
	}

	delete_desc (s, qh);
	
	return 0;
}
/*-------------------------------------------------------------------*/
_static void clean_td_chain (uhci_t *s, uhci_desc_t *td)
{
	struct list_head *p;
	uhci_desc_t *td1;

	if (!td)
		return;
	
	while ((p = td->horizontal.next) != &td->horizontal) {
		td1 = list_entry (p, uhci_desc_t, horizontal);
		delete_desc (s, td1);
	}
	
	delete_desc (s, td);
}

/*-------------------------------------------------------------------*/
_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
{
	td->hw.td.status = cpu_to_le32(status);
	td->hw.td.info = cpu_to_le32(info);
	td->hw.td.buffer = cpu_to_le32(buffer);
}
/*-------------------------------------------------------------------*/
// Removes ALL qhs in chain (paranoia!)
_static void cleanup_skel (uhci_t *s)
{
	unsigned int n;
	uhci_desc_t *td;

	dbg("cleanup_skel");

	clean_descs(s,1);

	
	if (s->td32ms) {
	
		unlink_td(s,s->td32ms,1);
		delete_desc(s, s->td32ms);
	}

	for (n = 0; n < 8; n++) {
		td = s->int_chain[n];
		clean_td_chain (s, td);
	}

	if (s->iso_td) {
		for (n = 0; n < 1024; n++) {
			td = s->iso_td[n];
			clean_td_chain (s, td);
		}
		kfree (s->iso_td);
	}

	if (s->framelist)
		pci_free_consistent(s->uhci_pci, PAGE_SIZE,
				    s->framelist, s->framelist_dma);

	if (s->control_chain) {
		// completed init_skel?
		struct list_head *p;
		uhci_desc_t *qh, *qh1;

		qh = s->control_chain;
		while ((p = qh->horizontal.next) != &qh->horizontal) {
			qh1 = list_entry (p, uhci_desc_t, horizontal);
			delete_qh (s, qh1);
		}

		delete_qh (s, qh);
	}
	else {
		if (s->ls_control_chain)
			delete_desc (s, s->ls_control_chain);
		if (s->control_chain)
			delete_desc (s, s->control_chain);
		if (s->bulk_chain)
			delete_desc (s, s->bulk_chain);
		if (s->chain_end)
			delete_desc (s, s->chain_end);
	}

	if (s->desc_pool) {
		pci_pool_destroy(s->desc_pool);
		s->desc_pool = NULL;
	}

	dbg("cleanup_skel finished");	
}
/*-------------------------------------------------------------------*/
// allocates framelist and qh-skeletons
// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
_static int init_skel (uhci_t *s)
{
	int n, ret;
	uhci_desc_t *qh, *td;
	
	dbg("init_skel");
	
	s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE,
					    &s->framelist_dma);

	if (!s->framelist)
		return -ENOMEM;

	memset (s->framelist, 0, 4096);

	dbg("creating descriptor pci_pool");

	s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci,
				       sizeof(uhci_desc_t), 16, 0,
				       GFP_DMA | GFP_ATOMIC);	
	if (!s->desc_pool)
		goto init_skel_cleanup;

	dbg("allocating iso desc pointer list");
	s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
	
	if (!s->iso_td)
		goto init_skel_cleanup;

	s->ls_control_chain = NULL;
	s->control_chain = NULL;
	s->bulk_chain = NULL;
	s->chain_end = NULL;

	dbg("allocating iso descs");
	for (n = 0; n < 1024; n++) {
	 	// allocate skeleton iso/irq-tds
		if (alloc_td (s, &td, 0))
			goto init_skel_cleanup;

		s->iso_td[n] = td;
		s->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
	}

	dbg("allocating qh: chain_end");
	if (alloc_qh (s, &qh))	
		goto init_skel_cleanup;
				
	s->chain_end = qh;

	if (alloc_td (s, &td, 0))
		goto init_skel_cleanup;
	
	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
	insert_td (s, qh, td, 0);
	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit
	s->td1ms=td;

	dbg("allocating qh: bulk_chain");
	if (alloc_qh (s, &qh))
		goto init_skel_cleanup;
	
	insert_qh (s, s->chain_end, qh, 0);
	s->bulk_chain = qh;

	dbg("allocating qh: control_chain");
	ret = alloc_qh (s, &qh);
	if (ret)
		goto init_skel_cleanup;
	
	insert_qh (s, s->bulk_chain, qh, 0);
	s->control_chain = qh;

#ifdef	CONFIG_USB_UHCI_HIGH_BANDWIDTH
	// disabled reclamation loop
	set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
#endif

	dbg("allocating qh: ls_control_chain");
	if (alloc_qh (s, &qh))
		goto init_skel_cleanup;
	
	insert_qh (s, s->control_chain, qh, 0);
	s->ls_control_chain = qh;

	for (n = 0; n < 8; n++)
		s->int_chain[n] = 0;

	dbg("allocating skeleton INT-TDs");
	
	for (n = 0; n < 8; n++) {
		uhci_desc_t *td;

		if (alloc_td (s, &td, 0))
			goto init_skel_cleanup;

		s->int_chain[n] = td;
		if (n == 0) {
			set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH);
		}
		else {
			set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr);
		}
	}

	dbg("Linking skeleton INT-TDs");
	
	for (n = 0; n < 1024; n++) {
		// link all iso-tds to the interrupt chains
		int m, o;
		dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n]));
		if ((n&127)==127) 
			((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr);
		else 
			for (o = 1, m = 2; m <= 128; o++, m += m)
				if ((n & (m - 1)) == ((m - 1) / 2))
					set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr);
	}

	if (alloc_td (s, &td, 0))
		goto init_skel_cleanup;
	
	fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
	s->td32ms=td;

	insert_td_horizontal (s, s->int_chain[5], td);

	mb();
	dbg("init_skel exit");
	return 0;

      init_skel_cleanup:
	cleanup_skel (s);
	return -ENOMEM;
}

/*-------------------------------------------------------------------*/
//                         LOW LEVEL STUFF
//          assembles QHs und TDs for control, bulk and iso
/*-------------------------------------------------------------------*/
_static int uhci_submit_control_urb (struct urb *urb)
{
	uhci_desc_t *qh, *td;
	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
	urb_priv_t *urb_priv = urb->hcpriv;
	unsigned long destination, status;
	int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
	unsigned long len;
	char *data;
	int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method

	dbg("uhci_submit_control start");
	if (alloc_qh (s, &qh))		// alloc qh for this request
		return -ENOMEM;

	if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))		// get td for setup stage
	{
		delete_qh (s, qh);
		return -ENOMEM;
	}

	/* The "pipe" thing contains the destination in bits 8--18 */
	destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;

	/* 3 errors */
	status = TD_CTRL_ACTIVE
		| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
		| (3 << 27);
	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	/*  Build the TD for the control request, try forever, 8 bytes of data */
	fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);

	insert_td (s, qh, td, 0);	// queue 'setup stage'-td in qh
#if 0
	{
		char *sp=urb->setup_packet;
		dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
		    sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
	}
	//uhci_show_td(td);
#endif

	len = urb->transfer_buffer_length;
	data = urb->transfer_buffer;

	/* If direction is "send", change the frame from SETUP (0x2D)
	   to OUT (0xE1). Else change it from SETUP to IN (0x69). */

	destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);

	while (len > 0) {
		int pktsze = len;

		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
			goto fail_unmap_enomem;

		if (pktsze > maxsze)
			pktsze = maxsze;

		destination ^= 1 << TD_TOKEN_TOGGLE;	// toggle DATA0/1

		// Status, pktsze bytes of data
		fill_td (td, status, destination | ((pktsze - 1) << 21),
			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));

		insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue 'data stage'-td in qh

		data += pktsze;
		len -= pktsze;
	}

	/* Build the final TD for control status */
	/* It's only IN if the pipe is out AND we aren't expecting data */

	destination &= ~UHCI_PID;

	if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
		destination |= USB_PID_IN;
	else
		destination |= USB_PID_OUT;

	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */

	if (alloc_td (s, &td, UHCI_PTR_DEPTH))
		goto fail_unmap_enomem;

	status &=~TD_CTRL_SPD;

	/* no limit on errors on final packet , 0 bytes of data */
	fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
		 0);

	insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);	// queue status td

	list_add (&qh->desc_list, &urb_priv->desc_list);

	queue_urb (s, urb);	// queue before inserting in desc chain

	qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);

	/* Start it up... put low speed first */
	if (urb->dev->speed == USB_SPEED_LOW)
		insert_qh (s, s->control_chain, qh, 0);
	else
		insert_qh (s, s->bulk_chain, qh, 0);

	dbg("uhci_submit_control end");
	return 0;

fail_unmap_enomem:
	delete_qh(s, qh);
	return -ENOMEM;
}
/*-------------------------------------------------------------------*/
// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!

_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
{
	uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
	urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
	uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
	unsigned long destination, status;
	char *data;
	unsigned int pipe = urb->pipe;
	int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
	int info, len, last;
	int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method

	if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
		return -EPIPE;

	queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
		  urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);

	upriv = (urb_priv_t*)urb->hcpriv;

	if (!bulk_urb) {
		if (alloc_qh (s, &qh))		// get qh for this request
			return -ENOMEM;

		if (urb->transfer_flags & USB_QUEUE_BULK) {
			if (alloc_qh(s, &nqh)) // placeholder for clean unlink
			{
				delete_desc (s, qh);
				return -ENOMEM;
			}
			upriv->next_qh = nqh;
			queue_dbg("new next qh %p",nqh);
		}
	}
	else { 
		bpriv = (urb_priv_t*)bulk_urb->hcpriv;
		qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
		nqh = bpriv->next_qh;
		upriv->next_qh=nqh;	
		upriv->prev_queued_urb=bulk_urb;
	}

	if (urb->transfer_flags & USB_QUEUE_BULK) {
		if (alloc_qh (s, &bqh))  // "bottom" QH
		{
			if (!bulk_urb) { 
				delete_desc(s, qh);
				delete_desc(s, nqh);
			}
			return -ENOMEM;
		}
		set_qh_element(bqh, UHCI_PTR_TERM);
		set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
		upriv->bottom_qh = bqh;
	}
	queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);

	/* The "pipe" thing contains the destination in bits 8--18. */
	destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);

	/* 3 errors */
	status = TD_CTRL_ACTIVE
		| ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD)
		| (3 << 27);
	if (urb->dev->speed == USB_SPEED_LOW)
		status |= TD_CTRL_LS;

	/* Build the TDs for the bulk request */
	len = urb->transfer_buffer_length;
	data = urb->transfer_buffer;
	
	do {					// TBD: Really allow zero-length packets?
		int pktsze = len;

		if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
		{
			delete_qh (s, qh);
			return -ENOMEM;
		}

		if (pktsze > maxsze)
			pktsze = maxsze;

		// pktsze bytes of data 
		info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
			(usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);

		fill_td (td, status, info,
			 urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));

		data += pktsze;
		len -= pktsze;
		// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
		last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));

		if (last)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -