patch-2.3.18 linux/drivers/usb/ohci-hcd.c
Next file: linux/drivers/usb/ohci-hcd.h
Previous file: linux/drivers/usb/mouse.c
Back to the patch index
Back to the overall index
- Lines: 706
- Date:
Fri Sep 10 12:59:49 1999
- Orig file:
v2.3.17/linux/drivers/usb/ohci-hcd.c
- Orig date:
Wed Aug 18 16:22:23 1999
diff -u --recursive --new-file v2.3.17/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c
@@ -5,7 +5,7 @@
*
* The OHCI HCD layer is a simple but nearly complete implementation of what
* the USB people would call a HCD for the OHCI.
- * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
+ * (ISO alpha , Bulk, INT u. CTRL transfers enabled)
* The layer on top of it, is for interfacing to the alternate-usb
* device-drivers.
*
@@ -17,12 +17,15 @@
* [ $Log: ohci.c,v $ ]
* [ Revision 1.1 1999/04/05 08:32:30 greg ]
*
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18
* v3.0 1999/06/25
* v2.1 1999/05/09 code clean up
* v2.0 1999/05/04
- * virtual root hub is now an option,
+ * virtual root hub is now enabled,
* memory allocation based on kmalloc and kfree now, Bus error handling,
- * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
+ * INT, CTRL and BULK transfers enabled, ISO needs testing (alpha)
*
* from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff)
* from Greg Smith (ohci.c) (reset controller handling, hub)
@@ -44,8 +47,8 @@
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/timer.h>
+#include <linux/spinlock.h>
-#include <asm/spinlock.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
@@ -58,8 +61,11 @@
static int handle_apm_event(apm_event_t event);
static int apm_resume = 0;
#endif
+
static int ohci_link_ed(struct ohci * ohci, struct usb_ohci_ed *ed);
-
+static int sohci_kill_isoc (struct usb_isoc_desc *id);
+static int sohci_get_current_frame_number (struct usb_device *usb_dev);
+static int sohci_run_isoc(struct usb_isoc_desc *id, struct usb_isoc_desc *pr_id);
static DECLARE_WAIT_QUEUE_HEAD(op_wakeup);
void usb_pipe_to_hcd_ed(struct usb_device *usb_dev, unsigned int pipe, struct usb_hcd_ed *hcd_ed)
@@ -70,7 +76,8 @@
hcd_ed->type = usb_pipetype(pipe);
hcd_ed->slow = usb_pipeslow(pipe);
hcd_ed->maxpack = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe));
- OHCI_DEBUG(printk("******* hcd_ed: endpoint: %4x, function: %4x, out: %4x, type: %4x, slow: %4x, maxpack: %4x\n", hcd_ed->endpoint, hcd_ed->function, hcd_ed->out, hcd_ed->type, hcd_ed->slow, hcd_ed->maxpack); )
+ OHCI_DEBUG(printk("******* hcd_ed: pipe: %8x, endpoint: %4x, function: %4x, out: %4x, type: %4x, slow: %4x, maxpack: %4x\n", pipe, hcd_ed->endpoint, hcd_ed->function, hcd_ed->out, hcd_ed->type, hcd_ed->slow, hcd_ed->maxpack); )
+OHCI_DEBUG(printk("******* dev: devnum: %4x, slow: %4x, maxpacketsize: %4x\n",usb_dev->devnum, usb_dev->slow, usb_dev->maxpacketsize); )
}
@@ -81,7 +88,10 @@
static int sohci_blocking_handler(void * ohci_in, struct usb_ohci_ed *ed, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1)
{
if(lw0 != NULL) {
- ((struct ohci_state * )lw0)->status = status;
+ if(USB_ST_CRC < 0 && (status == USB_ST_DATAUNDERRUN || status == USB_ST_NOERROR))
+ ((struct ohci_state * )lw0)->status = data_len;
+ else
+ ((struct ohci_state * )lw0)->status = status;
((struct ohci_state * )lw0)->len = data_len;
}
if(lw1 != NULL) {
@@ -116,8 +126,60 @@
return 0;
}
-
-static void * sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+
+static int sohci_iso_handler(void * ohci_in, struct usb_ohci_ed *ed, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) {
+
+ // struct ohci * ohci = ohci_in;
+ unsigned int ix = (unsigned int) lw0;
+ struct usb_isoc_desc * id = (struct usb_isoc_desc *) lw1;
+ struct usb_ohci_td **tdp = id->td;
+ int ret = 0;
+ int fx;
+
+ OHCI_DEBUG({ int i; printk("USB HC ISO |||: %x: data(%d):", ed->hwINFO, data_len);)
+ OHCI_DEBUG( for(i=0; i < 16 ; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk(" ... ret_status: %x\n", status); })
+
+ tdp[ix] = NULL;
+ id->frames[ix].frame_length = data_len;
+ id->frames[ix].frame_status = status;
+ id->total_length += data_len;
+ if(status) id->error_count++;
+
+ id->cur_completed_frame++;
+ id->total_completed_frames++;
+
+ if(id->cur_completed_frame == id->callback_frames) {
+ id->prev_completed_frame = id->cur_completed_frame;
+ id->cur_completed_frame = 0;
+ OHCI_DEBUG(printk("USB HC ISO <<<: %x: \n", ed->hwINFO);)
+ ret = id->callback_fn(id->error_count, id->data, id->total_length, id);
+ switch (ret) {
+ case CB_CONT_RUN:
+ for (fx = 0; fx < id->frame_count; fx++)
+ id->frames[fx].frame_length = id->frame_size;
+ sohci_run_isoc(id, id->prev_isocdesc);
+ break;
+
+ case CB_CONTINUE:
+ break;
+
+ case CB_REUSE:
+ break;
+
+ case CB_RESTART:
+ break;
+
+ case CB_ABORT:
+ sohci_kill_isoc(id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id, void **handle)
{
struct ohci * ohci = usb_dev->bus->hcpriv;
struct ohci_device * dev = usb_to_ohci(usb_dev);
@@ -126,7 +188,7 @@
#ifdef VROOTHUB
if(usb_pipedevice(pipe) == ohci->rh.devnum)
- return root_hub_request_irq(usb_dev, pipe, handler, period, dev_id);
+ return root_hub_request_irq(usb_dev, pipe, handler, period, dev_id, handle);
#endif
usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed);
@@ -136,8 +198,9 @@
OHCI_DEBUG( printk("USB HC IRQ>>>: %x: every %d ms\n", ed->hwINFO, period);)
ohci_trans_req(ohci, ed, 0, NULL, dev->data, hcd_ed.maxpack, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id, INT_IN, sohci_int_handler);
- if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed);
- return ed;
+ if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed);
+ *handle = ed;
+ return 0;
}
@@ -146,8 +209,8 @@
// struct usb_device *usb_dev = ((struct ohci_device *) ((unsigned int)ed & 0xfffff000))->usb;
struct ohci * ohci = usb_dev->bus->hcpriv;
- OHCI_DEBUG( printk("USB HC RM_IRQ>>>:%4x\n", (unsigned int) ed);)
-
+ OHCI_DEBUG( printk("USB HC ***** RM_IRQ>>>:%4x\n", (unsigned int) ed);)
+
if(ed == NULL) return 0;
#ifdef VROOTHUB
@@ -157,7 +220,7 @@
ED_setSTATE((struct usb_ohci_ed *)ed, ED_STOP);
usb_ohci_rm_ep(usb_dev, (struct usb_ohci_ed *) ed, NULL, NULL, NULL, 0);
-
+
return 0;
}
@@ -179,7 +242,7 @@
ed = usb_ohci_add_ep(usb_dev, &hcd_ed, 0, 1);
- OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: %x: ctrl(%d):", ed->hwINFO, 8);)
+ OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: ed:%x-%x: ctrl(%d):", (unsigned int) ed, ed->hwINFO, 8);)
OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);)
OHCI_DEBUG( printk(" data(%d):", len);)
OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
@@ -191,7 +254,7 @@
OHCI_DEBUG(printk("USB HC trans req ed %x: %x :", ed->hwINFO, (unsigned int ) ed); )
OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; )
if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed);
- schedule_timeout(HZ/10);
+ schedule_timeout(HZ*5);
if(state.status == TD_NOTACCESSED) {
current->state = TASK_UNINTERRUPTIBLE;
@@ -223,7 +286,7 @@
ohci_trans_req(ohci, ed, 0, NULL, data, len, (__OHCI_BAG) &state, (__OHCI_BAG) &wait,(usb_pipeout(pipe))?BULK_OUT:BULK_IN, sohci_blocking_handler);
if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed);
- schedule_timeout(HZ/10);
+ schedule_timeout(HZ*5);
if(state.status == TD_NOTACCESSED) {
current->state = TASK_UNINTERRUPTIBLE;
@@ -267,13 +330,36 @@
return 1;
}
-static int sohci_usb_deallocate(struct usb_device *usb_dev) {
+static int sohci_alloc_dev(struct usb_device *usb_dev)
+{
+ struct ohci_device *dev;
+
+ /* Allocate the OHCI_HCD device private data */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -1;
+
+ /* Initialize "dev" */
+ memset(dev, 0, sizeof(*dev));
+
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+ atomic_set(&dev->refcnt, 1);
+
+ if (usb_dev->parent)
+ dev->ohci = usb_to_ohci(usb_dev->parent)->ohci;
+
+ return 0;
+}
+
+static int sohci_free_dev(struct usb_device *usb_dev)
+{
int cnt;
DECLARE_WAITQUEUE(wait, current);
- struct ohci_device *dev = usb_to_ohci(usb_dev);
-
- OHCI_DEBUG(printk("USB HC dealloc %x\n", usb_dev->devnum);)
-
+ struct ohci_device *dev = usb_to_ohci(usb_dev);
+
+ OHCI_DEBUG(printk("USB HC ***** free %x\n", usb_dev->devnum);)
+
if(usb_dev->devnum >= 0) {
current->state = TASK_UNINTERRUPTIBLE;
cnt = usb_ohci_rm_function(usb_dev, sohci_blocking_handler, NULL, &wait);
@@ -281,109 +367,161 @@
schedule();
remove_wait_queue(&op_wakeup, &wait);
}
+ current->state = TASK_INTERRUPTIBLE;
}
-
- USB_FREE(dev);
- usb_destroy_configuration(usb_dev);
- USB_FREE(usb_dev);
+
+if (atomic_dec_and_test(&dev->refcnt))
+ kfree(dev);
return 0;
}
-static struct usb_device *sohci_usb_allocate(struct usb_device *parent) {
- struct usb_device *usb_dev;
- struct ohci_device *dev;
-
- USB_ALLOC(usb_dev, sizeof(*usb_dev));
- if (!usb_dev)
- return NULL;
-
- memset(usb_dev, 0, sizeof(*usb_dev));
- USB_ALLOC(dev, sizeof(*dev));
- if (!dev) {
- usb_destroy_configuration(usb_dev);
- USB_FREE(usb_dev);
- return NULL;
- }
+/*
+ * ISO Interface designed by Randy Dunlap
+ */
- /* Initialize "dev" */
- memset(dev, 0, sizeof(*dev));
+static int sohci_get_current_frame_number(struct usb_device *usb_dev) {
+
+ struct ohci * ohci = usb_dev->bus->hcpriv;
- usb_dev->hcpriv = dev;
- dev->usb = usb_dev;
+ return readl(&ohci->regs->fmnumber) & 0xffff;
+}
- usb_dev->parent = parent;
- if (parent) {
- usb_dev->bus = parent->bus;
- dev->ohci = usb_to_ohci(parent)->ohci;
- }
- return usb_dev;
-}
+static int sohci_init_isoc(struct usb_device *usb_dev, unsigned int pipe, int frame_count, void *context, struct usb_isoc_desc **idp) {
-static void *sohci_alloc_isochronous (struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int maxsze, usb_device_irq completed, void *dev_id)
-{
- struct ohci * ohci = usb_dev->bus->hcpriv;
- struct ohci_device * dev = usb_to_ohci(usb_dev);
- struct usb_hcd_ed hcd_ed;
- struct usb_ohci_ed * ed;
- struct usb_ohci_td * td;
-
- usb_pipe_to_hcd_ed(usb_dev, pipe, &hcd_ed);
- hcd_ed.type = ISO;
- ed = usb_ohci_add_ep(usb_dev, &hcd_ed, 1, 1);
+ struct usb_isoc_desc *id;
- OHCI_DEBUG( printk("USB HC ISO>>>: %x: \n", ed->hwINFO);)
+ *idp = NULL;
+
+ id = kmalloc (sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count), GFP_KERNEL);
+ if(!id) return -ENOMEM;
+ memset (id, 0, sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count));
+OHCI_DEBUG(printk("ISO alloc id: %p, size: %d\n", id, sizeof (struct usb_isoc_desc) + (sizeof (struct isoc_frame_desc) * frame_count));)
+ id->td = kmalloc (sizeof (void *) * frame_count, GFP_KERNEL);
+ if(!id->td) {
+ kfree (id);
+ return -ENOMEM;
+ }
+ memset (id->td, 0, sizeof (void *) * frame_count);
+OHCI_DEBUG(printk("ISO alloc id->td: %p, size: %d\n", id->td, sizeof (void *) * frame_count);)
- td = ohci_trans_req(ohci, ed, 0, NULL, dev->data, len, (__OHCI_BAG) completed, (__OHCI_BAG) dev_id, (usb_pipeout(pipe))?ISO_OUT:ISO_IN, sohci_int_handler);
- return td;
-}
+ id->frame_count = frame_count;
+ id->frame_size = usb_maxpacket (usb_dev, pipe, usb_pipeout(pipe));
+ id->start_frame = -1;
+ id->end_frame = -1;
+ id->usb_dev = usb_dev;
+ id->pipe = pipe;
+ id->context = context;
-static void sohci_delete_isochronous (struct usb_device *usb_dev, void *td)
-{
- // struct ohci * ohci = usb_dev->bus->hcpriv;
- DECLARE_WAITQUEUE(wait, current);
+ *idp = id;
+ return 0;
+}
+
+void print_int_eds(struct ohci * ohci);
+
+static int sohci_run_isoc(struct usb_isoc_desc *id, struct usb_isoc_desc *pr_id) {
+
+ struct ohci * ohci = id->usb_dev->bus->hcpriv;
+ struct usb_ohci_td ** tdp = (struct usb_ohci_td **) id->td;
+ struct usb_hcd_ed hcd_ed;
+ struct usb_ohci_ed * ed;
+ int ix, frlen;
+ unsigned char *bufptr;
+
+ if(pr_id)
+ id->start_frame = pr_id->end_frame + 1;
+ else {
+ switch(id->start_type) {
+ case START_ABSOLUTE:
+ break;
+
+ case START_RELATIVE:
+ if(id->start_frame < START_FRAME_FUDGE) id->start_frame = START_FRAME_FUDGE;
+ id->start_frame += sohci_get_current_frame_number(id->usb_dev);
+ break;
+
+ case START_ASAP:
+ id->start_frame = START_FRAME_FUDGE + sohci_get_current_frame_number(id->usb_dev);
+ break;
+ }
+ }
+
+ id->start_frame &= 0xffff;
+ id->end_frame = (id->start_frame + id->frame_count - 1) & 0xffff;
+
+ id->prev_completed_frame = 0;
+ id->cur_completed_frame = 0;
+ if (id->frame_spacing <= 0) id->frame_spacing = 1;
+
+ bufptr = id->data;
- OHCI_DEBUG( printk("USB HC RM_ISO>>>:%4x\n", (unsigned int) td);)
+ usb_pipe_to_hcd_ed(id->usb_dev, id->pipe, &hcd_ed);
+ hcd_ed.type = ISO;
+ ed = usb_ohci_add_ep(id->usb_dev, &hcd_ed, 1, 1);
+ OHCI_DEBUG( printk("USB HC ISO>>>: ed: %x-%x: (%d tds)\n", (unsigned int) ed, ed->hwINFO, id->frame_count);)
- current->state = TASK_UNINTERRUPTIBLE;
- usb_ohci_rm_ep(usb_dev, ((struct usb_ohci_td *) td)->ed, sohci_blocking_handler, NULL, &wait, SEND);
- schedule();
- remove_wait_queue(&op_wakeup, &wait);
- return;
+ for (ix = 0; ix < id->frame_count; ix++) {
+ frlen = (id->frames[ix].frame_length > id->frame_size)? id->frame_size : id->frames[ix].frame_length;
+ printk("ISO run id->td: %p \n", &tdp[ix]);
+ tdp[ix] = ohci_trans_req(ohci, ed, id->start_frame + ix , NULL, bufptr, frlen, (__OHCI_BAG) ix, (__OHCI_BAG) id,
+ (usb_pipeout(id->pipe))?ISO_OUT:ISO_IN,
+ sohci_iso_handler);
+ bufptr += frlen;
+ }
+
+ if (ED_STATE(ed) != ED_OPER) ohci_link_ed(ohci, ed);
+ print_int_eds(ohci);
+ return 0;
}
-static int sohci_sched_isochronous (struct usb_device *usb_dev, void *_isodesc, void *_pisodesc)
-{
- return USB_ST_NOTSUPPORTED;
-}
+static int sohci_kill_isoc(struct usb_isoc_desc *id) {
-static int sohci_unsched_isochronous (struct usb_device *usb_dev, void *_isodesc)
-{
- return USB_ST_NOTSUPPORTED;
+ struct usb_ohci_ed *ed = NULL;
+ struct usb_ohci_td **td = id->td;
+ int i;
+printk("KILL_ISOC***:\n");
+ for (i = 0; i < id->frame_count; i++) {
+ if(td[i]) {
+ td[i]->type |= DEL;
+ ed = td[i]->ed; printk(" %d", i);
+ }
+ }
+ if(ed) usb_ohci_rm_ep(id->usb_dev, ed, NULL, NULL, NULL, TD_RM);
+printk(": end KILL_ISOC***: %p\n", ed);
+ id->start_frame = -1;
+ return 0;
}
-static int sohci_compress_isochronous (struct usb_device *usb_dev, void *_isodesc)
-{
- return USB_ST_NOTSUPPORTED;
+
+static void sohci_free_isoc(struct usb_isoc_desc *id) {
+printk("FREE_ISOC***\n");
+wait_ms(2000);
+ if(id->start_frame >= 0) sohci_kill_isoc(id);
+printk("FREE_ISOC2***\n");
+wait_ms(2000);
+ kfree(id->td);
+ kfree(id);
+printk("FREE_ISOC3***\n");
+wait_ms(2000);
}
struct usb_operations sohci_device_operations = {
- sohci_usb_allocate,
- sohci_usb_deallocate,
+ sohci_alloc_dev,
+ sohci_free_dev,
sohci_control_msg,
sohci_bulk_msg,
sohci_request_irq,
sohci_release_irq,
sohci_request_bulk,
sohci_terminate_bulk,
- sohci_alloc_isochronous,
- sohci_delete_isochronous,
- sohci_sched_isochronous,
- sohci_unsched_isochronous,
- sohci_compress_isochronous
+ sohci_get_current_frame_number,
+ sohci_init_isoc,
+ sohci_free_isoc,
+ sohci_run_isoc,
+ sohci_kill_isoc
};
@@ -394,7 +532,7 @@
/* just for debugging; prints all 32 branches of the int ed tree inclusive iso eds*/
void print_int_eds(struct ohci * ohci) {int i; __u32 * ed_p;
for(i= 0; i < 32; i++) {
- OHCI_DEBUG(printk("unlnk_branch int %2d(%2x): ", i,i); )
+ OHCI_DEBUG(printk("branch int %2d(%2x): ", i,i); )
ed_p = &(ohci->hc_area->hcca.int_table[i]);
while (*ed_p != 0) {
OHCI_DEBUG(printk("ed: %4x; ", (((struct usb_ohci_ed *)bus_to_virt(*ed_p))->hwINFO));)
@@ -431,7 +569,7 @@
}
/* the int tree is a binary tree
- * in order to process it sequentielly the indexes of the branches have to be mapped
+ * in order to process it sequentially the indexes of the branches have to be mapped
* the mapping reverses the bits of a word of num_bits length
* */
static int rev(int num_bits, int word) {
@@ -445,7 +583,7 @@
/* get the ed from the endpoint / usb_device address */
struct usb_ohci_ed * ohci_find_ep(struct usb_device *usb_dev, struct usb_hcd_ed *hcd_ed) {
- return &(usb_to_ohci(usb_dev)->ed[hcd_ed->endpoint << 1 | ((hcd_ed->type == CTRL)? 0:hcd_ed->out)]);
+ return &(usb_to_ohci(usb_dev)->ed[(hcd_ed->endpoint << 1) | ((hcd_ed->type == CTRL)? 0:hcd_ed->out)]);
}
/* link an ed into one of the HC chains */
@@ -634,16 +772,19 @@
// struct ohci * ohci = usb_dev->bus->hcpriv;
struct usb_ohci_td * td;
- struct usb_ohci_ed * ed;
+ struct usb_ohci_ed * ed, *ed1;
- int ed_state;
+ int ed_state, ed_state1;
spin_lock(&usb_ed_lock);
- ed = ohci_find_ep(usb_dev, hcd_ed);
+ ed = ohci_find_ep(usb_dev, hcd_ed);
+
+ ed1 = ((void *) ed) + 0x40; ed_state1 = ED_STATE(ed1);
+OHCI_DEBUG(printk("++++ USB HC add 60 ed1 %x: %x :state: %x\n", ed1->hwINFO, (unsigned int ) ed1, ed_state1); )
ed_state = ED_STATE(ed); /* store state of ed */
-
+ OHCI_DEBUG(printk("USB HC add ed %x: %x :state: %x\n", ed->hwINFO, (unsigned int ) ed, ed_state); )
if (ed_state == ED_NEW) {
OHCI_ALLOC(td, sizeof(*td)); /* dummy td; end of td list for ed */
ed->hwTailP = virt_to_bus(td);
@@ -678,6 +819,8 @@
* put the ep on the rm_list and request a stop of the bulk or ctrl list
* real removal is done at the next start of frame (SOF) hardware interrupt
* the dummy td carries the essential information (handler, proc queue, ...)
+ * if(send & TD_RM) then just the TD witch have (TD->type & DEL) set will be removed
+ * otherwise all TDs including the dummy TD of the ED will be removed
*/
int usb_ohci_rm_ep(struct usb_device * usb_dev, struct usb_ohci_ed *ed, f_handler handler, __OHCI_BAG lw0, __OHCI_BAG lw1, int send)
{
@@ -692,17 +835,21 @@
ed->hwINFO |= OHCI_ED_SKIP;
writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
- if(ED_STATE(ed) == ED_OPER) ohci_unlink_ed(ohci, ed);
- td = (struct usb_ohci_td *) bus_to_virt(ed->hwTailP);
- td->lw0 = lw0;
- td->lw1 = lw1;
- td->ed = ed;
- td->hwINFO = TD_CC;
- td->handler = handler;
- td->type = send;
-
- ED_setSTATE(ed, ED_DEL);
+ if(send & TD_RM) { /* delete selected TDs */
+ ED_setSTATE(ed, ED_TD_DEL);
+ }
+ else { /* delete all TDS */
+ if(ED_STATE(ed) == ED_OPER) ohci_unlink_ed(ohci, ed);
+ td = (struct usb_ohci_td *) bus_to_virt(ed->hwTailP);
+ td->lw0 = lw0;
+ td->lw1 = lw1;
+ td->ed = ed;
+ td->hwINFO = TD_CC;
+ td->handler = handler;
+ td->type = send;
+ ED_setSTATE(ed, ED_DEL);
+ }
ed->ed_prev = ohci->ed_rm_list;
ohci->ed_rm_list = ed;
@@ -770,7 +917,7 @@
#define FILL_ISO_TD(INFO, DATA, LEN, LW0, LW1, TYPE, HANDLER) \
OHCI_ALLOC(td_pt, sizeof(*td_pt)); \
- td_ret = (struct usb_ohci_td *) bus_to_virt(usb_ed->hwTailP); \
+ td_ret = (struct usb_ohci_td *) bus_to_virt(usb_ed->hwTailP & 0xfffffff0); \
td_pt1 = td_ret; \
td_pt1->ed = ed; \
td_pt1->buffer_start = (DATA); \
@@ -779,12 +926,13 @@
td_pt1->lw0 = (LW0); \
td_pt1->lw1 = (LW1); \
td_pt1->hwINFO = (INFO); \
- td_pt1->hwCBP = (virt_to_bus(DATA) & 0xfffff000); \
- td_pt1->hwBE = virt_to_bus((DATA) + (LEN) - 1); \
+ td_pt1->hwCBP = (((DATA)==NULL)||((LEN)==0))?0:(virt_to_bus(DATA) & 0xfffff000); \
+ td_pt1->hwBE = (((DATA)==NULL)||((LEN)==0))?0:virt_to_bus((DATA) + (LEN) - 1); \
td_pt1->hwNextTD = virt_to_bus(td_pt); \
- td_pt1->hwPSW[0] = virt_to_bus(DATA) & 0xfff; \
- usb_ed->hwTailP = virt_to_bus(td_pt); \
- td_pt->hwNextTD = 0
+ td_pt1->hwPSW[0] = (virt_to_bus(DATA) & 0xfff) | 0xe000; \
+ td_pt->hwNextTD = 0; \
+ usb_ed->hwTailP = td_pt1->hwNextTD
+
spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED;
@@ -811,7 +959,7 @@
case BULK_IN:
while(data_len > 4096)
{
- FILL_TD( TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, 4096, NULL, NULL, BULK_IN | ADD_LEN|(cnt++?0:ST_ADDR), NULL);
+ FILL_TD( TD_CC | TD_DP_IN | TD_T_TOGGLE, data, 4096, NULL, NULL, BULK_IN | ADD_LEN|(cnt++?0:ST_ADDR), NULL);
data += 4096; data_len -= 4096;
}
FILL_TD( TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1, BULK_IN |ADD_LEN|SEND|(cnt++?0:ST_ADDR), handler);
@@ -925,7 +1073,8 @@
}
/* there are some pending requests to remove some of the eds
- * we process every td including the dummy td of these eds
+ * we either process every td including the dummy td of these eds
+ * or just those marked with TD->type&DEL
* and link them to a list
* */
static struct usb_ohci_td * usb_ohci_del_list(struct ohci * ohci) {
@@ -964,11 +1113,12 @@
OHCI_DEBUG(printk("USB HC ed_rm_list: dummy (ED_DEL) td: %4x\n", (unsigned int) td);)
ED_setSTATE(td->ed, ED_DEL| ED_NEW);
td->next_dl_td = NULL;
- }
+ }
+ if(ED_TYPE(ed) == CTRL) writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
+ if(ED_TYPE(ed) == BULK) writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
+
}
- writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
- writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
writel_set((0x03<<4), &ohci->regs->control); /* start CTRL u. BULK list */
ohci->ed_rm_list = NULL;
@@ -1014,11 +1164,11 @@
}
}
/* error code of transfer */
- cc = (ED_STATE(td_list->ed) == ED_DEL || (td_list->type & DEL))? USB_ST_REMOVED : TD_CC_GET(td_list->hwINFO);
+ cc = (ED_STATE(td_list->ed) == ED_DEL || (td_list->type & DEL))? USB_ST_REMOVED : (USB_ST_CRC*TD_CC_GET(td_list->hwINFO));
if(td_list->type & DEL_ED) ED_setSTATE(td_list->ed, ED_NEW); /* remove ed */
- if((td_list->type & SEND) && (ED_STATE(td_list->ed) != ED_STOP)) { /* send the reply */
+ if((td_list->type & SEND) && (ED_STATE(td_list->ed) != ED_STOP) && (td_list->handler)) { /* send the reply */
td_list->handler((void *) ohci,
td_list->ed,
td_list->ed->buffer_start,
@@ -1029,6 +1179,7 @@
OHCI_DEBUG(if(cc != TD_CC_NOERROR) printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hwINFO), td_list->ed->hwINFO);)
}
+ if(ED_STATE(td_list->ed) == ED_TD_DEL) td_list->ed->hwINFO &= ~OHCI_ED_SKIP;
if(((td_list->ed->hwHeadP & 0xfffffff0) == td_list->ed->hwTailP) && (ED_STATE(td_list->ed) > ED_UNLINK))
ohci_unlink_ed(ohci, td_list->ed); /* unlink eds if they are not busy */
@@ -1050,12 +1201,18 @@
void reset_hc(struct ohci *ohci) {
int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */
writel(0x08, &ohci->regs->cmdstatus); /* request ownership */
printk("USB HC TakeOver from SMM\n");
- if(readl(&ohci->regs->control) & 0x100)
- printk("USB HC TakeOver failed!\n");
+ while(readl(&ohci->regs->control) & 0x100) {
+ wait_ms(10);
+ if (--smm_timeout == 0) {
+ printk("USB HC TakeOver failed!\n");
+ break;
+ }
+ }
}
writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */
@@ -1092,7 +1249,7 @@
writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */
- writel((0xB7), &ohci->regs->control); /* USB Operational */
+ writel((0xBF), &ohci->regs->control); /* USB Operational */
fminterval = 0x2edf;
writel(((fminterval)*9)/10, &ohci->regs->periodicstart);
@@ -1121,16 +1278,16 @@
struct ohci_device *dev;
- usb_dev = sohci_usb_allocate(NULL);
+ usb_dev = usb_alloc_dev(NULL, ohci->bus);
if (!usb_dev) return -1;
dev = usb_to_ohci(usb_dev);
- usb_dev->bus = ohci->bus;
+ // usb_dev->bus = ohci->bus;
ohci->bus->root_hub = usb_dev;
dev->ohci = ohci;
usb_connect(usb_dev);
if(usb_new_device(usb_dev) != 0) {
- sohci_usb_deallocate(usb_dev);
+ usb_free_dev(usb_dev);
return -1;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)