RE: usb printer support

Pham, QuoiX (quoix.pham@intel.com)
Tue, 5 Oct 1999 09:48:36 -0700


This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

------_=_NextPart_000_01BF0F51.7BA1B5F5
Content-Type: text/plain;
charset="windows-1252"

I heard that the uhci in Kernel v2.3.18 is broken, which affected the usb
printer support not to print. Attached is a diff file of Kernel v2.3.12 vs
Kernel v2.3.18 of uhci.c.

thx,
-quoi

-----Original Message-----
From: Pavel Machek [mailto:pavel@atrey.karlin.mff.cuni.cz]
Sent: Tuesday, October 05, 1999 2:52 AM
To: Pham, QuoiX
Subject: Re: usb printer support

Hi!

> I finally got the usb printer support working. However, it's on Kernel
> v2.3.12 not Kernel v2.3.18. Could anyone tell me what is wrong in Kernel
> v2.3.18 for usb printer support code.

Can you mail me and linux-kernel@vger.rutgers.edu a diff -u?

Pavel

-- 
The best software in life is free (not shareware)!		Pavel
GCM d? s-: !g p?:+ au- a--@ w+ v- C++@ UL+++ L++ N++ E++ W--- M- Y- R+

------_=_NextPart_000_01BF0F51.7BA1B5F5 Content-Type: text/plain; name="k12v18.txt" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="k12v18.txt"

--- uhci.c Mon Jul 26 16:05:45 1999 +++ /usr/src/linux-2.3.18/drivers/usb/uhci.h Fri Aug 27 15:03:07 1999 @@ -1,1827 +1,293 @@ -/* - * Universal Host Controller Interface driver for USB. - * - * (C) Copyright 1999 Linus Torvalds - * - * Intel documents this fairly well, and as far as I know there - * are no royalties or anything like that, but even so there are - * people who decided that they want to do the same thing in a - * completely different way. - * - * Oh, well. The intel version is the more common by far. As such, - * that's the one I care about right now. - * - * WARNING! The USB documentation is downright evil. Most of it - * is just crap, written by a committee. You're better off ignoring - * most of it, the important stuff is: - * - the low-level protocol (fairly simple but lots of small details) - * - working around the horridness of the rest - */ - -/* 4/4/1999 added data toggle for interrupt pipes -keryan */ -/* 5/16/1999 added global toggles for bulk and control */ -/* 6/25/1999 added fix for data toggles on bidirectional bulk = endpoints */=20 - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/malloc.h> -#include <linux/smp_lock.h> -#include <linux/errno.h> -#include <linux/unistd.h> - -#include <asm/uaccess.h> -#include <asm/spinlock.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> - -#include "uhci.h" - -#ifdef CONFIG_APM -#include <linux/apm_bios.h> -static int handle_apm_event(apm_event_t event); -static int apm_resume =3D 0; -#endif +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H =20 -static int uhci_debug =3D 1; +#include <linux/list.h> =20 -#define compile_assert(x) do { switch (0) { case 1: case !(x): } } = while (0) +#include "usb.h" =20 -static DECLARE_WAIT_QUEUE_HEAD(uhci_configure); - -/* - * Map status to standard result codes - */ -static int uhci_map_status(int status, int dir_out) -{ - if (!status) - return USB_ST_NOERROR; - if (status & 0x02) /* Bitstuff error*/ - return USB_ST_BITSTUFF; - if (status & 0x04) { /* CRC/Timeout */ - if (dir_out) - return USB_ST_NORESPONSE; - else - return USB_ST_CRC; - } - if (status & 0x08) /* NAK */ - return USB_ST_TIMEOUT; - if (status & 0x10) /* Babble */ - return USB_ST_STALL; - if (status & 0x20) /* Buffer error */ - return USB_ST_BUFFERUNDERRUN; - if (status & 0x40) /* Stalled */ - return USB_ST_STALL; - if (status & 0x80) /* Active */ - return USB_ST_NOERROR; - return USB_ST_INTERNALERROR; -} /* - * Return the result of a TD.. + * Universal Host Controller Interface data structures and defines */ -static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, = unsigned long *rval) -{ - unsigned int status; - struct uhci_td *tmp =3D td->first; - - if(rval) - *rval =3D 0; - - /* locate the first failing td, if any */ - - do { - status =3D (tmp->status >> 16) & 0xff; - if (status) { - /* must reset the toggle on first error */ - if (uhci_debug) { - printk("Set toggle from %x rval %ld\n", (unsigned int)tmp, rval = ? *rval : 0); - } - usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), = usb_pipeout(tmp->info), (tmp->info >> 19) & 1); - break; - } else { - if(rval) - *rval +=3D (tmp->status & 0x3ff) + 1; - } - if ((tmp->link & 1) || (tmp->link & 2)) - break; - tmp =3D bus_to_virt(tmp->link & ~0xF); - } while (1); - - - if (!status) - return USB_ST_NOERROR; - - /* Some debugging code */ - if (uhci_debug /* && (!usb_pipeendpoint(tmp->info) || !(status & = 0x08))*/ ) { - int i =3D 10; - - tmp =3D td->first; - printk("uhci_td_result() failed with status %x\n", status); - //show_status(dev->uhci); - do { - show_td(tmp); - if ((tmp->link & 1) || (tmp->link & 2)) - break; - tmp =3D bus_to_virt(tmp->link & ~0xF); - if (!--i) - break; - } while (1); - } - - if (status & 0x40) { - /* endpoint has stalled - mark it halted */ - - usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info)); - return USB_ST_STALL; - - } - - if (status =3D=3D 0x80) { - /* still active */ - if (!rval) - return USB_ST_DATAUNDERRUN; - } - return uhci_map_status(status, usb_pipeout(tmp->info)); =09 -} =20 -/* - * Inserts a td into qh list at the top. - * - * Careful about atomicity: even on UP this - * requires a locked access due to the concurrent - * DMA engine. - * - * NOTE! This assumes that first->last is a valid - * list of TD's with the proper backpointers set - * up and all.. - */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td = *first, struct uhci_td *last) -{ - unsigned int link =3D qh->element; - unsigned int new =3D 4 | virt_to_bus(first); - - for (;;) { - unsigned char success; - - last->link =3D link; - first->backptr =3D &qh->element; - asm volatile("lock ; cmpxchg %4,%2 ; sete %0" - :"=3Dq" (success), "=3Da" (link) - :"m" (qh->element), "1" (link), "r" (new) - :"memory"); - if (success) { - /* Was there a successor entry? Fix it's backpointer.. */ - if ((link & 1) =3D=3D 0) { - struct uhci_td *next =3D bus_to_virt(link & ~15); - next->backptr =3D &last->link; - } - break; - } - } -} - -static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct = uhci_td *td) -{ - uhci_insert_tds_in_qh(qh, td, td); -} - -static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh) -{ - newqh->link =3D qh->link; - qh->link =3D virt_to_bus(newqh) | 2; -} - -static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh) -{ - unsigned int remphys =3D virt_to_bus(remqh); - struct uhci_qh *lqh =3D qh; - - while ((lqh->link & ~0xF) !=3D remphys) { - if (lqh->link & 1) - break; - - lqh =3D bus_to_virt(lqh->link & ~0xF); - } - - if (lqh->link & 1) { - printk("couldn't find qh in chain!\n"); - return; - } +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 =3D 32, 1 =3D 64) */ =20 - lqh->link =3D remqh->link; -} +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI = problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the = scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ =20 -/* - * Removes td from qh if present. - * - * NOTE! We keep track of both forward and back-pointers, - * so this should be trivial, right? - * - * Wrong. While all TD insert/remove operations are synchronous - * on the CPU, the UHCI controller can (and does) play with the - * very first forward pointer. So we need to validate the backptr - * before we change it, so that we don't by mistake reset the QH - * head to something old. - */ -static void uhci_remove_td(struct uhci_td *td) -{ - unsigned int *backptr =3D td->backptr; - unsigned int link =3D td->link; - unsigned int me; - - if (!backptr) - return; - - td->backptr =3D NULL; - - /* - * This is the easy case: the UHCI will never change "td->link", - * so we can always just look at that and fix up the backpointer - * of any next element.. - */ - if (!(link & 1)) { - struct uhci_td *next =3D bus_to_virt(link & ~15); - next->backptr =3D backptr; - } - - /* - * The nasty case is "backptr->next", which we need to - * update to "link" _only_ if "backptr" still points - * to us (it may not: maybe backptr is a QH->element - * pointer and the UHCI has changed the value). - */ - me =3D virt_to_bus(td) | (0xe & *backptr); - asm volatile("lock ; cmpxchg %0,%1" - : - :"r" (link), "m" (*backptr), "a" (me) - :"memory"); -} - -static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev) -{ - struct uhci_qh *qh; - int inuse; - - qh =3D dev->qh; - for (; (inuse =3D test_and_set_bit(0, &qh->inuse)) !=3D 0 && qh < = &dev->qh[UHCI_MAXQH]; qh++) - ; - - if (!inuse) - return(qh); - - printk("ran out of qh's for dev %p\n", dev); - return(NULL); -} - -static void uhci_qh_deallocate(struct uhci_qh *qh) -{ -// if (qh->element !=3D 1) -// printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element); - - qh->element =3D 1; - qh->link =3D 1; - - clear_bit(0, &qh->inuse); -} - -static struct uhci_td *uhci_td_allocate(struct uhci_device *dev) -{ - struct uhci_td *td; - int inuse; - - td =3D dev->td; - for (; (inuse =3D test_and_set_bit(0, &td->inuse)) !=3D 0 && td < = &dev->td[UHCI_MAXTD]; td++) - ; - - if (!inuse) { - td->inuse =3D 1; - return(td); - } - - printk("ran out of td's for dev %p\n", dev); - return(NULL); -} +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ =20 -/* - * This MUST only be called when it has been removed from a QH already = (or - * the QH has been removed from the skeleton - */ -static void uhci_td_deallocate(struct uhci_td *td) -{ - td->link =3D 1; +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 =20 - clear_bit(0, &td->inuse); -} +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device = present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ =20 -/* - * UHCI interrupt list operations.. - */ -static spinlock_t irqlist_lock =3D SPIN_LOCK_UNLOCKED; +#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ =20 -static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, = usb_device_irq completed, void *dev_id) -{ - unsigned long flags; - - td->completed =3D completed; - td->dev_id =3D dev_id; - - spin_lock_irqsave(&irqlist_lock, flags); - list_add(&td->irq_list, &uhci->interrupt_list); - spin_unlock_irqrestore(&irqlist_lock, flags); -} - -static void uhci_remove_irq_list(struct uhci_td *td) -{ - unsigned long flags; - - spin_lock_irqsave(&irqlist_lock, flags); - list_del(&td->irq_list); - spin_unlock_irqrestore(&irqlist_lock, flags); -} +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 =20 +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be = scheduled */ =20 -/* - * Request a interrupt handler.. - * - * Returns: a "handle pointer" that release_irq can use to stop this - * interrupt. (It's really a pointer to the TD). - */ -static void* uhci_request_irq(struct usb_device *usb_dev, unsigned int = pipe, usb_device_irq handler, int period, void *dev_id) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci_device *root_hub=3Dusb_to_uhci(dev->uhci->bus->root_hub); - struct uhci_td *td =3D uhci_td_allocate(dev); - struct uhci_qh *interrupt_qh =3D uhci_qh_allocate(dev); - - unsigned int destination, status; - - /* Destination: pipe destination with INPUT */ - destination =3D (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); - - /* Status: slow/fast, Interrupt, Active, Short Packet = Detect Infinite Errors */ - status =3D (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) = | (0 << 27); - - if(interrupt_qh->element !=3D 1) - printk("interrupt_qh->element =3D 0x%x\n",=20 - interrupt_qh->element); - - td->link =3D 1; - td->status =3D status; - td->info =3D destination | ((usb_maxpacket(usb_dev, pipe) - 1) << 21) = | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) = << 19); - td->buffer =3D virt_to_bus(dev->data); - td->first =3D td; - td->qh =3D interrupt_qh; - td->dev =3D usb_dev; - - /* if period 0, insert into fast q */ - - if (period =3D=3D 0) { - td->inuse |=3D 2; - interrupt_qh->skel =3D &root_hub->skel_int2_qh; - } else - interrupt_qh->skel =3D &root_hub->skel_int8_qh; - - uhci_add_irq_list(dev->uhci, td, handler, dev_id); +struct uhci_td; =20 - uhci_insert_td_in_qh(interrupt_qh, td); +struct uhci_qh { + /* Hardware fields */ + __u32 link; /* Next queue */ + __u32 element; /* Queue element pointer */ =20 - /* Add it into the skeleton */ - uhci_insert_qh(interrupt_qh->skel, interrupt_qh); + /* Software fields */ + atomic_t refcnt; /* Reference counting */ + struct uhci_device *dev; /* The owning device */ + struct uhci_qh *skel; /* Skeleton head */ =20 - return (void*)td; -} + struct uhci_td *first; /* First TD in the chain */ =20 -/* - * Remove running irq td from queues - * - * This function is not used anymore. - */ -#if 0 -static int uhci_remove_irq(struct usb_device *usb_dev, unsigned int = pipe, usb_device_irq handler, int period, void *dev_id) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci_device = *root_hub=3Dusb_to_uhci(dev->uhci->bus->root_hub); - struct uhci_td *td; - struct uhci_qh *interrupt_qh; - unsigned long flags; - struct list_head *head =3D &dev->uhci->interrupt_list; - struct list_head *tmp; - - spin_lock_irqsave(&irqlist_lock, flags); - - /* find the TD in the interrupt list */ - - tmp =3D head->next; - while (tmp !=3D head) { - td =3D list_entry(tmp, struct uhci_td, irq_list); - if (td->dev_id =3D=3D dev_id && td->completed =3D=3D handler) { - - /* found the right one - let's remove it */ - - /* notify removal */ - - td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); - - /* this is DANGEROUS - not sure whether this is right */ - - list_del(&td->irq_list); - uhci_remove_td(td); - interrupt_qh =3D td->qh; - uhci_remove_qh(interrupt_qh->skel, interrupt_qh); - uhci_td_deallocate(td); - uhci_qh_deallocate(interrupt_qh); - spin_unlock_irqrestore(&irqlist_lock, flags); - return USB_ST_NOERROR; - } - } - spin_unlock_irqrestore(&irqlist_lock, flags); - return USB_ST_INTERNALERROR; -} -#endif - -/* - * Release an interrupt handler previously allocated using - * uhci_request_irq. This function does no validity checking, so make - * sure you're not releasing an already released handle as it may be - * in use by something else.. - * - * This function can NOT be called from an interrupt. - */ -int uhci_release_irq(void* handle) -{ - struct uhci_td *td; - struct uhci_qh *interrupt_qh; - unsigned long flags; - -#ifdef UHCI_DEBUG - printk("usb-uhci: Releasing irq handle %p\n", handle); -#endif - - td =3D (struct uhci_td*)handle; - if (td =3D=3D NULL) - return USB_ST_INTERNALERROR; - - /* Remove it from the internal irq_list */ - spin_lock_irqsave(&irqlist_lock, flags); - list_del(&td->irq_list); - spin_unlock_irqrestore(&irqlist_lock, flags); - - /* Remove the interrupt TD and QH */ - uhci_remove_td(td); - interrupt_qh =3D td->qh; - uhci_remove_qh(interrupt_qh->skel, interrupt_qh); - - if (td->completed !=3D NULL) - td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); - - /* Free the TD and QH */ - uhci_td_deallocate(td); - uhci_qh_deallocate(interrupt_qh); - - return USB_ST_NOERROR; -} /* uhci_release_irq() */ + wait_queue_head_t wakeup; +} __attribute__((aligned(16))); =20 +struct uhci_framelist { + __u32 frame[UHCI_NUMFRAMES]; +} __attribute__((aligned(4096))); =20 /* - * Isochronous thread operations + * for TD <status>: */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 = */ =20 -static int uhci_compress_isochronous(struct usb_device *usb_dev, void = *_isodesc) -{ - struct uhci_iso_td *isodesc =3D (struct uhci_iso_td *)_isodesc; - char *data =3D isodesc->data; - int i, totlen =3D 0; - - for (i =3D 0; i < isodesc->num; i++) { - char *cdata =3D bus_to_virt(isodesc->td[i].buffer & ~0xF); - int n =3D (isodesc->td[i].status + 1) & 0x7FF; - - if ((cdata !=3D data) && (n)) - memmove(data, cdata, n); - -#if 0 -if (n && n !=3D 960) - printk("underrun: %d %d\n", i, n); -#endif -if ((isodesc->td[i].status >> 16) & 0xFF) - printk("error: %d %X\n", i, (isodesc->td[i].status >> 16)); - - data +=3D n; - totlen +=3D n; - } - - return totlen; -} - -static int uhci_unschedule_isochronous(struct usb_device *usb_dev, = void *_isodesc) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci *uhci =3D dev->uhci; - struct uhci_iso_td *isodesc =3D (struct uhci_iso_td *)_isodesc; - int i; - - if ((isodesc->frame < 0) || (isodesc->frame > 1023)) - return 1; - - /* Remove from previous frames */ - for (i =3D 0; i < isodesc->num; i++) { - /* Turn off Active and IOC bits */ - isodesc->td[i].status &=3D ~(3 << 23); - uhci->fl->frame[(isodesc->frame + i) % 1024] =3D = isodesc->td[i].link; - } - - isodesc->frame =3D -1; - - return 0; -} -=09 -/* td points to the one td we allocated for isochronous transfers */ -static int uhci_schedule_isochronous(struct usb_device *usb_dev, void = *_isodesc, void *_pisodesc) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci *uhci =3D dev->uhci; - struct uhci_iso_td *isodesc =3D (struct uhci_iso_td *)_isodesc; - struct uhci_iso_td *pisodesc =3D (struct uhci_iso_td *)_pisodesc; - int frame, i; - - if (isodesc->frame !=3D -1) { - printk("isoc queue not removed\n"); - uhci_unschedule_isochronous(usb_dev, isodesc); - } - - /* Insert TD into list */ - if (!pisodesc) { - frame =3D inw(uhci->io_addr + USBFRNUM) % 1024; - /* HACK: Start 2 frames from now */ - frame =3D (frame + 2) % 1024; - } else - frame =3D (pisodesc->endframe + 1) % 1024; - -#if 0 -printk("scheduling first at frame %d\n", frame); -#endif - - for (i =3D 0; i < isodesc->num; i++) { - /* Active */ - isodesc->td[i].status |=3D (1 << 23); - isodesc->td[i].backptr =3D &uhci->fl->frame[(frame + i) % 1024]; - isodesc->td[i].link =3D uhci->fl->frame[(frame + i) % 1024]; - uhci->fl->frame[(frame + i) % 1024] =3D = virt_to_bus(&isodesc->td[i]); - } - -#if 0 -printk("last at frame %d\n", (frame + i - 1) % 1024); -#endif - - /* Interrupt */ - isodesc->td[i - 1].status |=3D (1 << 24); +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) =20 - isodesc->frame =3D frame; - isodesc->endframe =3D (frame + isodesc->num - 1) % 1024; +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & = TD_CTRL_ACTLEN_MASK) /* 1-based */ =20 -#if 0 - return uhci_td_result(dev, td[num - 1]); -#endif - return 0; -} +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) =20 /* - * Initialize isochronous queue + * for TD <flags>: */ -static void *uhci_allocate_isochronous(struct usb_device *usb_dev, = unsigned int pipe, void *data, int len, int maxsze, usb_device_irq = completed, void *dev_id) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - unsigned long destination, status; - struct uhci_td *td; - struct uhci_iso_td *isodesc; - int i; - - isodesc =3D kmalloc(sizeof(*isodesc), GFP_KERNEL); - if (!isodesc) { - printk("Couldn't allocate isodesc!\n"); - return NULL; - } - memset(isodesc, 0, sizeof(*isodesc)); - - /* Carefully work around the non contiguous pages */ - isodesc->num =3D (len / PAGE_SIZE) * (PAGE_SIZE / maxsze); - isodesc->td =3D kmalloc(sizeof(struct uhci_td) * isodesc->num, = GFP_KERNEL); - isodesc->frame =3D isodesc->endframe =3D -1; - isodesc->data =3D data; - isodesc->maxsze =3D maxsze; -=09 - if (!isodesc->td) { - printk("Couldn't allocate td's\n"); - kfree(isodesc); - return NULL; - } - - isodesc->frame =3D isodesc->endframe =3D -1; - - /* - * Build the DATA TD's - */ - i =3D 0; - do { - /* Build the TD for control status */ - td =3D &isodesc->td[i]; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination =3D (pipe & PIPE_DEVEP_MASK) - | usb_packetid (pipe); /* add IN or OUT */ - - /* Status: slow/fast, Active, Isochronous */ - status =3D (pipe & (1 << 26)) | (1 << 23) | (1 << 25); - - /* - * Build the TD for the control request - */ - td->status =3D status; - td->info =3D destination | ((maxsze - 1) << 21); - td->buffer =3D virt_to_bus(data); - td->first =3D td; - td->backptr =3D NULL; - - i++; - - data +=3D maxsze; - - if (((int)data % PAGE_SIZE) + maxsze >=3D PAGE_SIZE) - data =3D (char *)(((int)data + maxsze) & ~(PAGE_SIZE - 1)); - =09 - len -=3D maxsze; - } while (i < isodesc->num); - - /* IOC on the last TD */ - td->status |=3D (1 << 24); - uhci_add_irq_list(dev->uhci, td, completed, dev_id); - - return isodesc; -} - -static void uhci_delete_isochronous(struct usb_device *usb_dev, void = *_isodesc) -{ - struct uhci_iso_td *isodesc =3D (struct uhci_iso_td *)_isodesc; - - /* If it's still scheduled, unschedule them */ - if (isodesc->frame) - uhci_unschedule_isochronous(usb_dev, isodesc); - - /* Remove it from the IRQ list */ - uhci_remove_irq_list(&isodesc->td[isodesc->num - 1]); - - kfree(isodesc->td); - kfree(isodesc); -} +#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ =20 /* - * Control thread operations: we just mark the last TD - * in a control thread as an interrupt TD, and wake up - * the front-end on completion. - * - * We need to remove the TD from the lists (both interrupt - * list and TD lists) by hand if something bad happens! + * for TD <info>: (a.k.a. Token) */ -static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); - -static int uhci_control_completed(int status, void *buffer, int len, = void *dev_id) -{ - wake_up(&control_wakeup); - return 0; /* Don't re-instate */ -} - -/* td points to the last td in the list, which interrupts on = completion */ -static int uhci_run_control(struct uhci_device *dev, struct uhci_td = *first, struct uhci_td *last) -{ - DECLARE_WAITQUEUE(wait, current); - struct uhci_qh *ctrl_qh =3D uhci_qh_allocate(dev); - struct uhci_td *curtd; - struct uhci_device *root_hub=3Dusb_to_uhci(dev->uhci->bus->root_hub); - current->state =3D TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); - - uhci_add_irq_list(dev->uhci, last, uhci_control_completed, NULL); -=09 - /* FIXME: This is kinda kludged */ - /* Walk the TD list and update the QH pointer */ - { - int maxcount =3D 100; - - curtd =3D first; - do { - curtd->qh =3D ctrl_qh; - if (curtd->link & 1) - break; - - curtd =3D bus_to_virt(curtd->link & ~0xF); - if (!--maxcount) { - printk("runaway tds!\n"); - break; - } - } while (1); - } - - uhci_insert_tds_in_qh(ctrl_qh, first, last); - - /* Add it into the skeleton */ - uhci_insert_qh(&root_hub->skel_control_qh, ctrl_qh); - -// control should be full here...=09 -// printk("control\n"); -// show_status(dev->uhci); -// show_queues(dev->uhci); - - schedule_timeout(HZ*5); - -// control should be empty here...=09 -// show_status(dev->uhci); -// show_queues(dev->uhci); +#define TD_TOKEN_TOGGLE 19 =20 - remove_wait_queue(&control_wakeup, &wait); +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) !=3D USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) =3D=3D USB_PID_IN) =20 - /* Clean up in case it failed.. */ - uhci_remove_irq_list(last); - -#if 0 - printk("Looking for tds [%p, %p]\n", dev->control_td, td); -#endif - - /* Remove it from the skeleton */ - uhci_remove_qh(&root_hub->skel_control_qh, ctrl_qh); - - uhci_qh_deallocate(ctrl_qh); - - return uhci_td_result(dev, last, NULL); -} =20 /* - * Send or receive a control message on a pipe. - * - * Note that the "pipe" structure is set up to map - * easily to the uhci destination fields. + * The documentation says "4 words for hardware, 4 words for = software". * - * A control message is built up from three parts: - * - The command itself - * - [ optional ] data phase - * - Status complete phase + * That's silly, the hardware doesn't care. The hardware only cares = that + * the hardware words are 16-byte aligned, and we can have any amount = of + * sw space after the TD entry as far as I can tell. * - * The data phase can be an arbitrary number of TD's - * although we currently had better not have more than - * 29 TD's here (we have 31 TD's allocated for control - * operations, and two of them are used for command and - * status). + * But let's just go with the documentation, at least for 32-bit = machines. + * On 64-bit machines we probably want to take advantage of the fact = that + * hw doesn't really care about the size of the sw-only area. * - * 29 TD's is a minimum of 232 bytes worth of control - * information, that's just ridiculously high. Most - * control messages have just a few bytes of data. + * Alas, not anymore, we have more than 4 words for software, woops */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int = pipe, devrequest *cmd, void *data, int len) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci_td *first, *td, *prevtd; - unsigned long destination, status; - int ret; - int maxsze =3D usb_maxpacket(usb_dev, pipe); - - if (len > maxsze * 29) - printk("Warning, too much data for a control packet, crashing\n"); - - first =3D td =3D uhci_td_allocate(dev); - - /* The "pipe" thing contains the destination in bits 8--18, 0x2D is = SETUP */ - destination =3D (pipe & PIPE_DEVEP_MASK) | 0x2D; - - /* Status: slow/fast, Active, Short Packet Detect = Three Errors */ - status =3D (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | = (3 << 27); - - /* - * Build the TD for the control request - */ - td->status =3D status; /* Try forever */ - td->info =3D destination | (7 << 21); /* 8 bytes of data */ - td->buffer =3D virt_to_bus(cmd); - td->first =3D td; - - /* - * If direction is "send", change the frame from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69) - */ - destination ^=3D (0x2D ^ 0x69); /* SETUP -> IN */ - if (usb_pipeout(pipe)) - destination ^=3D (0xE1 ^ 0x69); /* IN -> OUT */ - - prevtd =3D td; - td =3D uhci_td_allocate(dev); - prevtd->link =3D 4 | virt_to_bus(td); - - /* - * Build the DATA TD's - */ - while (len > 0) { - /* Build the TD for control status */ - int pktsze =3D len; - - if (pktsze > maxsze) - pktsze =3D maxsze; - - /* Alternate Data0/1 (start with Data1) */ - destination ^=3D 1 << 19; -=09 - td->status =3D status; /* Status */ - td->info =3D destination | ((pktsze-1) << 21); /* pktsze bytes of = data */ - td->buffer =3D virt_to_bus(data); - td->first =3D first; - td->backptr =3D &prevtd->link; - - data +=3D pktsze; - len -=3D pktsze; - - prevtd =3D td; - td =3D uhci_td_allocate(dev); - prevtd->link =3D 4 | virt_to_bus(td); /* Update previous TD */ - - } - - /* - * Build the final TD for control status=20 - */ - destination ^=3D (0xE1 ^ 0x69); /* OUT -> IN */ - destination |=3D 1 << 19; /* End in Data1 */ - - td->backptr =3D &prevtd->link; - td->status =3D (status /* & ~(3 << 27) */) | (1 << 24); /* no limit = on final packet */ - td->info =3D destination | (UHCI_NULL_DATA_SIZE << 21); /* 0 bytes of = data */ - td->buffer =3D 0; - td->first =3D first; - td->link =3D 1; /* Terminate */ - - - /* Start it up.. */ - ret =3D uhci_run_control(dev, first, td); - - { - int maxcount =3D 100; - struct uhci_td *curtd =3D first; - unsigned int nextlink; - - do { - nextlink =3D curtd->link; - uhci_remove_td(curtd); - uhci_td_deallocate(curtd); - if (nextlink & 1) /* Tail? */ - break; - - curtd =3D bus_to_virt(nextlink & ~0xF); - if (!--maxcount) { - printk("runaway td's!?\n"); - break; - } - } while (1); - } - - if (uhci_debug && ret) { - __u8 *p =3D (__u8 *)cmd; - - printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n", - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); - } - return ret; -} +struct uhci_td { + /* Hardware fields */ + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; =20 + /* Software fields */ + unsigned int *backptr; /* Where to remove this from.. */ + struct list_head irq_list; /* Active interrupt list.. */ =20 -/* - * Bulk thread operations: we just mark the last TD - * in a bulk thread as an interrupt TD, and wake up - * the front-end on completion. - * - * We need to remove the TD from the lists (both interrupt - * list and TD lists) by hand if something bad happens! - */ -static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); + usb_device_irq completed; /* Completion handler routine */ + void *dev_id; =20 -static int uhci_bulk_completed(int status, void *buffer, int len, void = *dev_id) -{ - wake_up(&bulk_wakeup); - return 0; /* Don't re-instate */ -} - -/* td points to the last td in the list, which interrupts on = completion */ -static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td = *first, struct uhci_td *last, unsigned long *rval) -{ - DECLARE_WAITQUEUE(wait, current); - struct uhci_qh *bulk_qh =3D uhci_qh_allocate(dev); - struct uhci_td *curtd; - struct uhci_device *root_hub=3Dusb_to_uhci(dev->uhci->bus->root_hub); - - current->state =3D TASK_UNINTERRUPTIBLE; - add_wait_queue(&bulk_wakeup, &wait); - - uhci_add_irq_list(dev->uhci, last, uhci_bulk_completed, NULL); -=09 - /* FIXME: This is kinda kludged */ - /* Walk the TD list and update the QH pointer */ - { - int maxcount =3D 100; - - curtd =3D first; - do { - curtd->qh =3D bulk_qh; - if (curtd->link & 1) - break; - - curtd =3D bus_to_virt(curtd->link & ~0xF); - if (!--maxcount) { - printk("runaway tds!\n"); - break; - } - } while (1); - } - - uhci_insert_tds_in_qh(bulk_qh, first, last); - - /* Add it into the skeleton */ - uhci_insert_qh(&root_hub->skel_bulk0_qh, bulk_qh); - -// now we're in the queue... but don't ask WHAT is in there ;-( -// printk("bulk\n"); -// show_status(dev->uhci); -// show_queues(dev->uhci); - - schedule_timeout(HZ*5); -// show_status(dev->uhci); -// show_queues(dev->uhci); + atomic_t refcnt; /* Reference counting */ + struct uhci_device *dev; /* The owning device */ + struct uhci_qh *qh; /* QH this TD is a part of (ignored for = Isochronous) */ + int flags; /* Remove, etc */ + int isoc_td_number; /* 0-relative number within a usb_isoc_desc. */ +} __attribute__((aligned(16))); =20 - //show_queue(first->qh); - remove_wait_queue(&bulk_wakeup, &wait); =20 - /* Clean up in case it failed.. */ - uhci_remove_irq_list(last); +/* + * Note the alignment requirements of the entries + * + * Each UHCI device has pre-allocated QH and TD entries. + * You can use more than the pre-allocated ones, but I + * don't see you usually needing to. + */ +struct uhci; =20 #if 0 - printk("Looking for tds [%p, %p]\n", dev->control_td, td); -#endif +#define UHCI_MAXTD 64 =20 - /* Remove it from the skeleton */ - uhci_remove_qh(&root_hub->skel_bulk0_qh, bulk_qh); +#define UHCI_MAXQH 16 +#endif =20 - uhci_qh_deallocate(bulk_qh); +/* The usb device part must be first! Not anymore -jerdfelt */ +struct uhci_device { + struct usb_device *usb; =20 - return uhci_td_result(dev, last, rval); -} + atomic_t refcnt; =20 -/* - * Send or receive a bulk message on a pipe. - * - * Note that the "pipe" structure is set up to map - * easily to the uhci destination fields. - * - * A bulk message is only built up from - * the data phase - * - * The data phase can be an arbitrary number of TD's - * although we currently had better not have more than - * 31 TD's here. - * - * 31 TD's is a minimum of 248 bytes worth of bulk - * information. - */ -static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int = pipe, void *data, int len, unsigned long *rval) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - struct uhci_td *first, *td, *prevtd; - unsigned long destination, status; - int ret; - int maxsze =3D usb_maxpacket(usb_dev, pipe); - - if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe)) && - usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) - return USB_ST_STALL; - - if (len > maxsze * 31) - printk("Warning, too much data for a bulk packet, crashing = (%d/%d)\n", len, maxsze); - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination =3D (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); - - /* Status: slow/fast, Active, Short Packet Detect = Three Errors */ - status =3D (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | = (3 << 27); - - /* - * Build the TDs for the bulk request - */ - first =3D td =3D uhci_td_allocate(dev); - prevtd =3D first; //This is fake, but at least it's not NULL - while (len > 0) { - /* Build the TD for control status */ - int pktsze =3D len; - - if (pktsze > maxsze) - pktsze =3D maxsze; - - td->status =3D status; /* Status */ - td->info =3D destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) = << 19); /* pktsze bytes of data */ - td->buffer =3D virt_to_bus(data); - td->backptr =3D &prevtd->link; - td->first =3D first; - - data +=3D maxsze; - len -=3D maxsze; - - if (len > 0) { - prevtd =3D td; - td =3D uhci_td_allocate(dev); - prevtd->link =3D 4 | virt_to_bus(td); /* Update previous TD */ - } - - /* Alternate Data0/1 (start with Data0) */ - usb_dotoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); - } - td->link =3D 1; /* Terminate */ - td->status |=3D (1 << 24); /* IOC */ - - /* CHANGE DIRECTION HERE! SAVE IT SOMEWHERE IN THE ENDPOINT!!! */ - - /* Start it up.. */ - ret =3D uhci_run_bulk(dev, first, td, rval); - - { - int maxcount =3D 100; - struct uhci_td *curtd =3D first; - unsigned int nextlink; - - do { - nextlink =3D curtd->link; - uhci_remove_td(curtd); - uhci_td_deallocate(curtd); - if (nextlink & 1) /* Tail? */ - break; - - curtd =3D bus_to_virt(nextlink & ~0xF); - if (!--maxcount) { - printk("runaway td's!?\n"); - break; - } - } while (1); - } - - return ret; -} - -static struct usb_device *uhci_usb_allocate(struct usb_device *parent) -{ - struct usb_device *usb_dev; - struct uhci_device *dev; - int i; - - usb_dev =3D kmalloc(sizeof(*usb_dev), GFP_KERNEL); - if (!usb_dev) - return NULL; - - memset(usb_dev, 0, sizeof(*usb_dev)); - - dev =3D kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - usb_destroy_configuration(usb_dev); - kfree(usb_dev); - return NULL; - } - - /* Initialize "dev" */ - memset(dev, 0, sizeof(*dev)); - - usb_dev->hcpriv =3D dev; - dev->usb =3D usb_dev; - - usb_dev->parent =3D parent; - - if (parent) { - usb_dev->bus =3D parent->bus; - dev->uhci =3D usb_to_uhci(parent)->uhci; - } - - /* Reset the QH's and TD's */ - for (i =3D 0; i < UHCI_MAXQH; i++) { - dev->qh[i].link =3D 1; - dev->qh[i].element =3D 1; - dev->qh[i].inuse =3D 0; - } - - for (i =3D 0; i < UHCI_MAXTD; i++) { - dev->td[i].link =3D 1; - dev->td[i].inuse =3D 0; - } - - return usb_dev; -} - -static int uhci_usb_deallocate(struct usb_device *usb_dev) -{ - struct uhci_device *dev =3D usb_to_uhci(usb_dev); - int i; - - /* There are UHCI_MAXTD preallocated tds */ - for (i =3D 0; i < UHCI_MAXTD; ++i) { - struct uhci_td *td =3D dev->td + i; - - if (td->inuse & 1) { - uhci_remove_td(td); - - /* And remove it from the irq list, if it's active */ - if (td->status & (1 << 23)) - td->status &=3D ~(1 << 23); + struct uhci *uhci; #if 0 - uhci_remove_irq_list(td); + struct uhci_qh qh[UHCI_MAXQH]; /* These are the "common" qh's for = each device */ + struct uhci_td td[UHCI_MAXTD]; #endif - } - } =20 - /* Remove the td from any queues */ - for (i =3D 0; i < UHCI_MAXQH; ++i) { - struct uhci_qh *qh =3D dev->qh + i; - - if (qh->inuse & 1) - uhci_remove_qh(qh->skel, qh); - } - - kfree(dev); - usb_destroy_configuration(usb_dev); - kfree(usb_dev); - - return 0; -} - -struct usb_operations uhci_device_operations =3D { - uhci_usb_allocate, - uhci_usb_deallocate, - uhci_control_msg, - uhci_bulk_msg, - uhci_request_irq, - uhci_release_irq, - uhci_allocate_isochronous, - uhci_delete_isochronous, - uhci_schedule_isochronous, - uhci_unschedule_isochronous, - uhci_compress_isochronous + unsigned long data[16]; }; =20 -/* - * This is just incredibly fragile. The timings must be just - * right, and they aren't really documented very well. - * - * Note the short delay between disabling reset and enabling - * the port.. - */ -static void uhci_reset_port(unsigned int port) -{ - unsigned short status; - - status =3D inw(port); - outw(status | USBPORTSC_PR, port); /* reset port */ - wait_ms(10); - outw(status & ~USBPORTSC_PR, port); - udelay(5); - - status =3D inw(port); - outw(status | USBPORTSC_PE, port); /* enable port */ - wait_ms(10); - - status =3D inw(port); - if(!(status & USBPORTSC_PE)) { - outw(status | USBPORTSC_PE, port); /* one more try at enabling port = */ - wait_ms(50); - } - -} - - -/* - * This gets called if the connect status on the root - * hub (and the root hub only) changes. - */ -static void uhci_connect_change(struct uhci *uhci, unsigned int port, = unsigned int nr) -{ - struct usb_device *usb_dev; - struct uhci_device *dev; - unsigned short status; - struct uhci_device *root_hub=3Dusb_to_uhci(uhci->bus->root_hub); - printk("uhci_connect_change: called for %d\n", nr); - - /* - * Even if the status says we're connected, - * the fact that the status bits changed may - * that we got disconnected and then reconnected. - * - * So start off by getting rid of any old devices.. - */ - usb_disconnect(&root_hub->usb->children[nr]); - - status =3D inw(port); - - /* If we have nothing connected, then clear change status and disable = the port */ - status =3D (status & ~USBPORTSC_PE) | USBPORTSC_PEC; - if (!(status & USBPORTSC_CCS)) { - outw(status, port); - return; - } - - /* - * Ok, we got a new connection. Allocate a device to it, - * and find out what it wants to do.. - */ - usb_dev =3D uhci_usb_allocate(root_hub->usb); - if (!usb_dev) - return; -=09 - dev =3D usb_dev->hcpriv; - - dev->uhci =3D uhci; - - usb_connect(usb_dev); - - root_hub->usb->children[nr] =3D usb_dev; - - wait_ms(200); /* wait for powerup */ - uhci_reset_port(port); - - /* Get speed information */ - usb_dev->slow =3D (inw(port) & USBPORTSC_LSDA) ? 1 : 0; - - /* - * Ok, all the stuff specific to the root hub has been done. - * The rest is generic for any new USB attach, regardless of - * hub type. - */ - usb_new_device(usb_dev); -} - -/* - * This gets called when the root hub configuration - * has changed. Just go through each port, seeing if - * there is something interesting happening. - */ -static void uhci_check_configuration(struct uhci *uhci) -{ - struct uhci_device * root_hub=3Dusb_to_uhci(uhci->bus->root_hub); - unsigned int io_addr =3D uhci->io_addr + USBPORTSC1; - int maxchild =3D root_hub->usb->maxchild; - int nr =3D 0; - - do { - unsigned short status =3D inw(io_addr); - - if (status & USBPORTSC_CSC) - uhci_connect_change(uhci, io_addr, nr); - - nr++; io_addr +=3D 2; - } while (nr < maxchild); -} - -static void uhci_interrupt_notify(struct uhci *uhci) -{ - struct list_head *head =3D &uhci->interrupt_list; - struct list_head *tmp; - int status; - - spin_lock(&irqlist_lock); - tmp =3D head->next; - while (tmp !=3D head) { - struct uhci_td *td =3D list_entry(tmp, struct uhci_td, irq_list); - struct list_head *next; - - next =3D tmp->next; - - if (!((status =3D td->status) & (1 << 23)) || /* No longer active? = */ - ((td->qh->element & ~15) &&=20 - !((status =3D uhci_link_to_td(td->qh->element)->status) & (1 = <<23)) && - (status & 0x760000) /* is in error state (Stall, db, babble, = timeout, bitstuff) */)) {=09 - /* remove from IRQ list */ - __list_del(tmp->prev, next); - INIT_LIST_HEAD(tmp); - if (td->completed(uhci_map_status(status, 0), = bus_to_virt(td->buffer), -1, td->dev_id)) { - list_add(&td->irq_list, &uhci->interrupt_list); - - if (!(td->status & (1 << 25))) { - struct uhci_qh *interrupt_qh =3D td->qh; - - usb_dotoggle(td->dev, usb_pipeendpoint(td->info), = usb_pipeout(td->info)); - td->info &=3D ~(1 << 19); /* clear data toggle */ - td->info |=3D usb_gettoggle(td->dev, usb_pipeendpoint(td->info), = usb_pipeout(td->info)) << 19; /* toggle between data0 and data1 */ - td->status =3D (td->status & 0x2f000000) | (1 << 23) | (1 << 24); = /* active */ - - /* Remove then readd? Is that necessary */ - uhci_remove_td(td); - uhci_insert_td_in_qh(interrupt_qh, td); - } - } else if (td->inuse & 2) { - struct uhci_qh *interrupt_qh =3D td->qh; - /* marked for removal */ - td->inuse &=3D ~2; - usb_dotoggle(td->dev, usb_pipeendpoint(td->info), = usb_pipeout(td->info)); - uhci_remove_qh(interrupt_qh->skel, interrupt_qh); - uhci_qh_deallocate(interrupt_qh); - uhci_td_deallocate(td); - } - /* If completed wants to not reactivate, then it's */ - /* responsible for free'ing the TD's and QH's */ - /* or another function (such as run_control) */ - }=20 - tmp =3D next; - } - spin_unlock(&irqlist_lock); -} +#define uhci_to_usb(uhci) ((uhci)->usb) +#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv) =20 /* - * Check port status - Connect Status Change - for - * each of the attached ports (defaults to two ports, - * but at least in theory there can be more of them). + * There are various standard queues. We set up several different + * queues for each of the three basic queue types: interrupt, + * control, and bulk. * - * Wake up the configurator if something happened, we - * can't really do much at interrupt time. - */ -static void uhci_root_hub_events(struct uhci *uhci, unsigned int = io_addr) -{ - if (waitqueue_active(&uhci_configure)) { - struct uhci_device * root_hub=3Dusb_to_uhci(uhci->bus->root_hub); - int ports =3D root_hub->usb->maxchild; - io_addr +=3D USBPORTSC1; - do { - if (inw(io_addr) & USBPORTSC_CSC) { - wake_up(&uhci_configure); - return; - } - io_addr +=3D 2; - } while (--ports > 0); - } -} - -static void uhci_interrupt(int irq, void *__uhci, struct pt_regs = *regs) -{ - struct uhci *uhci =3D __uhci; - unsigned int io_addr =3D uhci->io_addr; - unsigned short status; - - /* - * Read the interrupt status, and write it back to clear the = interrupt cause - */ - status =3D inw(io_addr + USBSTS); - outw(status, io_addr + USBSTS); - -// if ((status & ~0x21) !=3D 0) -// printk("interrupt: %X\n", status); - - /* Walk the list of pending TD's to see which ones completed.. */ - uhci_interrupt_notify(uhci); - - /* Check if there are any events on the root hub.. */ - uhci_root_hub_events(uhci, io_addr); -} - -/* - * We init one packet, and mark it just IOC and _not_ - * active. Which will result in no actual USB traffic, - * but _will_ result in an interrupt every second. + * - There are various different interrupt latencies: ranging from + * every other USB frame (2 ms apart) to every 256 USB frames (ie + * 256 ms apart). Make your choice according to how obnoxious you + * want to be on the wire, vs how critical latency is for you. + * - The control list is done every frame. + * - There are 4 bulk lists, so that up to four devices can have a + * bulk list of their own and when run concurrently all four lists + * will be be serviced. * - * Which is exactly what we want. - */ -static void uhci_init_ticktd(struct uhci *uhci) -{ - struct uhci_device *dev =3D usb_to_uhci(uhci->bus->root_hub); - struct uhci_td *td =3D uhci_td_allocate(dev); - - td->link =3D 1; - td->status =3D (1 << 24); /* interrupt on completion */ - td->info =3D (15 << 21) | 0x7f69; /* (ignored) input packet, 16 = bytes, device 127 */ - td->buffer =3D 0; - td->first =3D td; - td->qh =3D NULL; - - uhci->fl->frame[0] =3D virt_to_bus(td); -} - -static void reset_hc(struct uhci *uhci) -{ - unsigned int io_addr =3D uhci->io_addr; - - /* Global reset for 50ms */ - outw(USBCMD_GRESET, io_addr+USBCMD); - wait_ms(50); - outw(0, io_addr+USBCMD); - wait_ms(10); -} - -static void start_hc(struct uhci *uhci) -{ - unsigned int io_addr =3D uhci->io_addr; - int timeout =3D 1000; - - uhci_init_ticktd(uhci); - - /* - * Reset the HC - this will force us to get a - * new notification of any already connected - * ports due to the virtual disconnect that it - * implies. - */ - outw(USBCMD_HCRESET, io_addr + USBCMD); - while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { - if (!--timeout) { - printk("USBCMD_HCRESET timed out!\n"); - break; - } - } - - - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, = io_addr + USBINTR); - outw(0, io_addr + USBFRNUM); - outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); - - /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); -} - -/* - * Allocate a frame list, and four regular queues. + * This is a bit misleading, there are various interrupt latencies, = but they + * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms = since the + * other queues can "override" it. interrupt4 can vary up to 8ms, etc. = Minor + * problem + * + * In the case of the root hub, these QH's are just head's of qh's. = Don't + * be scared, it kinda makes sense. Look at this wonderful picture = care of + * Linus: + * + * generic-iso-QH -> dev1-iso-QH -> generic-irq-QH -> = dev1-irq-QH -> ... + * | | | | + * End dev1-iso-TD1 End = dev1-irq-TD1 + * | + * dev1-iso-TD2 + * | + * .... * - * The hardware doesn't really know any difference - * in the queues, but the order does matter for the - * protocols higher up. The order is: + * This may vary a bit (the UHCI docs don't explicitly say you can put = iso + * transfers in QH's and all of their pictures don't have that either) = but + * other than that, that is what we're doing now * - * - any isochronous events handled before any - * of the queues. We don't do that here, because - * we'll create the actual TD entries on demand. - * - The first queue is the "interrupt queue". - * - The second queue is the "control queue". - * - The third queue is "bulk data". + * And now we don't put Iso transfers in QH's, so we don't waste one = on it * - * We could certainly have multiple queues of the same - * type, and maybe we should. We could have per-device - * queues, for example. We begin small. + * To keep with Linus' nomenclature, this is called the QH skeleton. = These + * labels (below) are only signficant to the root hub's QH's */ -static struct uhci *alloc_uhci(unsigned int io_addr) -{ - int i; - struct uhci *uhci; - struct usb_bus *bus; - struct uhci_device *dev; - struct usb_device *usb; - - uhci =3D kmalloc(sizeof(*uhci), GFP_KERNEL); - if (!uhci) - return NULL; - - memset(uhci, 0, sizeof(*uhci)); - - uhci->irq =3D -1; - uhci->io_addr =3D io_addr; - INIT_LIST_HEAD(&uhci->interrupt_list); - - /* We need exactly one page (per UHCI specs), how convenient */ - uhci->fl =3D (void *)__get_free_page(GFP_KERNEL); - if (!uhci->fl) - goto au_free_uhci; - - bus =3D kmalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - goto au_free_fl; - - memset(bus, 0, sizeof(*bus)); - - uhci->bus =3D bus; - bus->hcpriv =3D uhci; - bus->op =3D &uhci_device_operations; - - /* - * We allocate a 8kB area for the UHCI hub. The area - * is described by the uhci_device structure, and basically - * contains everything needed for normal operation. - * - * The first page is the actual device descriptor for the - * hub. - * - * The second page is used for the frame list. - */ - usb =3D uhci_usb_allocate(NULL); - if (!usb) - goto au_free_bus; - - usb->bus =3D bus; - dev =3D usb_to_uhci(usb); - uhci->bus->root_hub=3Duhci_to_usb(dev); - /* Initialize the root hub */ - /* UHCI specs says devices must have 2 ports, but goes on to say */ - /* they may have more but give no way to determine how many they */ - /* have, so default to 2 */ - usb->maxchild =3D 2; - usb_init_root_hub(usb); - - /* - * Initialize the queues. They all start out empty, - * linked to each other in the proper order. - */ - for (i =3D 1 ; i < 9; i++) { - dev->qh[i].link =3D 2 | virt_to_bus(&dev->skel_control_qh); - dev->qh[i].element =3D 1; - } -=09 - dev->skel_control_qh.link =3D 2 | virt_to_bus(&dev->skel_bulk0_qh); - dev->skel_control_qh.element =3D 1; - - dev->skel_bulk0_qh.link =3D 2 | virt_to_bus(&dev->skel_bulk1_qh); - dev->skel_bulk0_qh.element =3D 1; - - dev->skel_bulk1_qh.link =3D 2 | virt_to_bus(&dev->skel_bulk2_qh); - dev->skel_bulk1_qh.element =3D 1; - - dev->skel_bulk2_qh.link =3D 2 | virt_to_bus(&dev->skel_bulk3_qh); - dev->skel_bulk2_qh.element =3D 1; - - dev->skel_bulk3_qh.link =3D 1; - dev->skel_bulk3_qh.element =3D 1; - - /* - * Fill the frame list: make all entries point to - * the proper interrupt queue. - * - * This is probably silly, but it's a simple way to - * scatter the interrupt queues in a way that gives - * us a reasonable dynamic range for irq latencies. - */ - for (i =3D 0; i < 1024; i++) { - struct uhci_qh * irq =3D &dev->skel_int2_qh; - if (i & 1) { - irq++; - if (i & 2) { - irq++; - if (i & 4) {=20 - irq++; - if (i & 8) {=20 - irq++; - if (i & 16) { - irq++; - if (i & 32) { - irq++; - if (i & 64) { - irq++; - } - } - } - } - } - } - } - uhci->fl->frame[i] =3D 2 | virt_to_bus(irq); - } - - return uhci; +#define UHCI_NUM_SKELQH 10 =20 -/* - * error exits: - */ +#define skel_int2_qh skelqh[0] +#define skel_int4_qh skelqh[1] +#define skel_int8_qh skelqh[2] +#define skel_int16_qh skelqh[3] +#define skel_int32_qh skelqh[4] +#define skel_int64_qh skelqh[5] +#define skel_int128_qh skelqh[6] +#define skel_int256_qh skelqh[7] =20 -au_free_bus: - kfree (bus); -au_free_fl: - free_page ((unsigned long)uhci->fl); -au_free_uhci: - kfree (uhci); - return NULL; -} +#define skel_control_qh skelqh[8] =20 +#define skel_bulk_qh skelqh[9] =20 /* - * De-allocate all resources.. + * This describes the full uhci information. + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. */ -static void release_uhci(struct uhci *uhci) -{ - if (uhci->irq >=3D 0) { - free_irq(uhci->irq, uhci); - uhci->irq =3D -1; - } +struct uhci { + int irq; + unsigned int io_addr; + unsigned int io_size; =20 -#if 0 - if (uhci->bus->root_hub) { - uhci_usb_deallocate(uhci_to_usb(uhci->bus->root_hub)); - uhci->bus->root_hub =3D NULL; - } -#endif - - if (uhci->fl) { - free_page((unsigned long)uhci->fl); - uhci->fl =3D NULL; - } - - kfree(uhci->bus); - kfree(uhci); -} - -static int uhci_control_thread(void * __uhci) -{ - struct uhci *uhci =3D (struct uhci *)__uhci; - struct uhci_device * root_hub =3Dusb_to_uhci(uhci->bus->root_hub); - - lock_kernel(); - request_region(uhci->io_addr, 32, "usb-uhci"); - - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources.. - */ - printk("uhci_control_thread at %p\n", &uhci_control_thread); - exit_mm(current); - exit_files(current); - //exit_fs(current); - - strcpy(current->comm, "uhci-control"); - - /* - * Ok, all systems are go.. - */ - start_hc(uhci); - usb_register_bus(uhci->bus); - for(;;) { - siginfo_t info; - int unsigned long signr; - - interruptible_sleep_on(&uhci_configure); -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume =3D 0; - start_hc(uhci); - continue; - } -#endif - uhci_check_configuration(uhci); + int control_pid; + int control_running; + int control_continue; =20 - if(signal_pending(current)) { - /* sending SIGUSR1 makes us print out some info */ - spin_lock_irq(&current->sigmask_lock); - signr =3D dequeue_signal(&current->blocked, &info); - spin_unlock_irq(&current->sigmask_lock); - - if(signr =3D=3D SIGUSR1) { - printk("UHCI queue dump:\n"); - show_queues(uhci); - } else if (signr =3D=3D SIGUSR2) { - uhci_debug =3D !uhci_debug; - printk("UHCI debug toggle =3D %x\n", uhci_debug); - } else { - break; - } - } - } - - { - int i; - if(root_hub) - for(i =3D 0; i < root_hub->usb->maxchild; i++) - usb_disconnect(root_hub->usb->children + i); - } - - usb_deregister_bus(uhci->bus); - - reset_hc(uhci); - release_region(uhci->io_addr, 32); + struct list_head uhci_list; =20 - release_uhci(uhci); - MOD_DEC_USE_COUNT; + struct usb_bus *bus; =20 - printk("uhci_control_thread exiting\n"); + struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ =20 - return 0; -}=09 + struct uhci_framelist *fl; /* Frame list */ + struct list_head interrupt_list; /* List of interrupt-active TD's for = this uhci */ =20 -/* - * If we've successfully found a UHCI, now is the time to increment = the - * module usage count, start the control thread, and return success.. - */ -static int found_uhci(int irq, unsigned int io_addr) -{ - int retval; - struct uhci *uhci; - - uhci =3D alloc_uhci(io_addr); - if (!uhci) - return -ENOMEM; - - reset_hc(uhci); - - retval =3D -EBUSY; - if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) =3D=3D 0) = { - int pid; - MOD_INC_USE_COUNT; - uhci->irq =3D irq; - pid =3D kernel_thread(uhci_control_thread, uhci, - CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >=3D 0) - return 0; - - MOD_DEC_USE_COUNT; - retval =3D pid; - } - release_uhci(uhci); - return retval; -} - -static int start_uhci(struct pci_dev *dev) -{ - int i; - - /* Search for the IO base address.. */ - for (i =3D 0; i < 6; i++) { - unsigned int io_addr =3D dev->base_address[i]; - - /* IO address? */ - if (!(io_addr & 1)) - continue; - - io_addr &=3D PCI_BASE_ADDRESS_IO_MASK; - - /* Is it already in use? */ - if (check_region(io_addr, 32)) - break; - - return found_uhci(dev->irq, io_addr); - } - return -1; -} - -#ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) -{ - static int down =3D 0; - - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - if (down) { - printk(KERN_DEBUG "uhci: received extra suspend event\n"); - break; - } - down =3D 1; - break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - if (!down) { - printk(KERN_DEBUG "uhci: received bogus resume event\n"); - break; - } - down =3D 0; - if (waitqueue_active(&uhci_configure)) { - apm_resume =3D 1; - wake_up(&uhci_configure); - } - break; - } - return 0; -} -#endif + struct uhci_td *ticktd; +}; =20 +/* needed for the debugging code */ +struct uhci_td *uhci_link_to_td(unsigned int element); =20 -int uhci_init(void) -{ - int retval; - struct pci_dev *dev =3D NULL; - u8 type; - - retval =3D -ENODEV; - for (;;) { - dev =3D pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev); - if (!dev) - break; - /* Is it UHCI */ - pci_read_config_byte(dev, PCI_CLASS_PROG, &type); - if(type !=3D 0) - continue; - /* Ok set it up */ - retval =3D start_uhci(dev); - if (retval < 0) - continue; +/* Debugging code */ +void uhci_show_td(struct uhci_td *td); +void uhci_show_status(struct uhci *uhci); +void uhci_show_queue(struct uhci_qh *qh); +void uhci_show_queues(struct uhci *uhci); =20 -#ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); -#endif - return 0; - } - return retval; -} - -#ifdef MODULE -int init_module(void) -{ - return uhci_init(); -} - -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); #endif -} -#endif //MODULE +

------_=_NextPart_000_01BF0F51.7BA1B5F5--

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/