1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
Index: linux-2.6.15/drivers/usb/host/ohci.h
===================================================================
--- linux-2.6.15.orig/drivers/usb/host/ohci.h
+++ linux-2.6.15/drivers/usb/host/ohci.h
@@ -336,6 +336,23 @@ typedef struct urb_priv {
// sizeof (struct td) ~= 64 == 2^6 ...
#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
+struct ohci_hcd;
+
+/*
+ * Hooks to support controllers that must resort to a PIO-ish
+ * implementation because they only dma with on-chip memory.
+ */
+struct ohci_ops {
+ void* (*dma_pool_alloc) (struct dma_pool *pool, gfp_t mem_flags,
+ dma_addr_t *handle);
+ void (*dma_pool_free) (struct dma_pool *pool, void *vaddr,
+ dma_addr_t addr);
+
+ void (*td_fill) (struct ohci_hcd *ohci, u32 info, dma_addr_t data,
+ int len, struct urb *urb, int index);
+ void (*td_done) (struct ohci_hcd *ohci, struct urb *urb,
+ struct td *td);
+};
/*
* This is the full ohci controller description
@@ -346,6 +363,7 @@ typedef struct urb_priv {
struct ohci_hcd {
spinlock_t lock;
+ const struct ohci_ops *ops;
/*
* I/O memory used to communicate with the HC (dma-consistent)
Index: linux-2.6.15/drivers/usb/host/ohci-mem.c
===================================================================
--- linux-2.6.15.orig/drivers/usb/host/ohci-mem.c
+++ linux-2.6.15/drivers/usb/host/ohci-mem.c
@@ -21,10 +21,13 @@
* No memory seen by this driver is pagable.
*/
+static const struct ohci_ops ohci_ops;
+
/*-------------------------------------------------------------------------*/
static void ohci_hcd_init (struct ohci_hcd *ohci)
{
+ ohci->ops = &ohci_ops;
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
@@ -88,7 +91,7 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem
dma_addr_t dma;
struct td *td;
- td = dma_pool_alloc (hc->td_cache, mem_flags, &dma);
+ td = hc->ops->dma_pool_alloc (hc->td_cache, mem_flags, &dma);
if (td) {
/* in case hc fetches it, make it look dead */
memset (td, 0, sizeof *td);
@@ -110,7 +113,7 @@ td_free (struct ohci_hcd *hc, struct td
*prev = td->td_hash;
else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0)
ohci_dbg (hc, "no hash for td %p\n", td);
- dma_pool_free (hc->td_cache, td, td->td_dma);
+ hc->ops->dma_pool_free (hc->td_cache, td, td->td_dma);
}
/*-------------------------------------------------------------------------*/
@@ -122,7 +125,7 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem
dma_addr_t dma;
struct ed *ed;
- ed = dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ ed = hc->ops->dma_pool_alloc (hc->ed_cache, mem_flags, &dma);
if (ed) {
memset (ed, 0, sizeof (*ed));
INIT_LIST_HEAD (&ed->td_list);
@@ -134,6 +137,6 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem
static void
ed_free (struct ohci_hcd *hc, struct ed *ed)
{
- dma_pool_free (hc->ed_cache, ed, ed->dma);
+ hc->ops->dma_pool_free (hc->ed_cache, ed, ed->dma);
}
Index: linux-2.6.15/drivers/usb/host/ohci-q.c
===================================================================
--- linux-2.6.15.orig/drivers/usb/host/ohci-q.c
+++ linux-2.6.15/drivers/usb/host/ohci-q.c
@@ -629,7 +629,7 @@ static void td_submit_urb (
: TD_T_TOGGLE | TD_CC | TD_DP_IN;
/* TDs _could_ transfer up to 8K each */
while (data_len > 4096) {
- td_fill (ohci, info, data, 4096, urb, cnt);
+ ohci->ops->td_fill (ohci, info, data, 4096, urb, cnt);
data += 4096;
data_len -= 4096;
cnt++;
@@ -637,11 +637,11 @@ static void td_submit_urb (
/* maybe avoid ED halt on final TD short read */
if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
info |= TD_R;
- td_fill (ohci, info, data, data_len, urb, cnt);
+ ohci->ops->td_fill (ohci, info, data, data_len, urb, cnt);
cnt++;
if ((urb->transfer_flags & URB_ZERO_PACKET)
&& cnt < urb_priv->length) {
- td_fill (ohci, info, 0, 0, urb, cnt);
+ ohci->ops->td_fill (ohci, info, 0, 0, urb, cnt);
cnt++;
}
/* maybe kickstart bulk list */
@@ -656,17 +656,18 @@ static void td_submit_urb (
*/
case PIPE_CONTROL:
info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
- td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
+ ohci->ops->td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
if (data_len > 0) {
info = TD_CC | TD_R | TD_T_DATA1;
info |= is_out ? TD_DP_OUT : TD_DP_IN;
/* NOTE: mishandles transfers >8K, some >4K */
- td_fill (ohci, info, data, data_len, urb, cnt++);
+ ohci->ops->td_fill (ohci, info, data, data_len,
+ urb, cnt++);
}
info = (is_out || data_len == 0)
? TD_CC | TD_DP_IN | TD_T_DATA1
: TD_CC | TD_DP_OUT | TD_T_DATA1;
- td_fill (ohci, info, data, 0, urb, cnt++);
+ ohci->ops->td_fill (ohci, info, data, 0, urb, cnt++);
/* maybe kickstart control list */
wmb ();
ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus);
@@ -685,7 +686,7 @@ static void td_submit_urb (
// a 2^16 iso range, vs other HCs max of 2^10)
frame += cnt * urb->interval;
frame &= 0xffff;
- td_fill (ohci, TD_CC | TD_ISO | frame,
+ ohci->ops->td_fill (ohci, TD_CC | TD_ISO | frame,
data + urb->iso_frame_desc [cnt].offset,
urb->iso_frame_desc [cnt].length, urb, cnt);
}
@@ -788,6 +789,14 @@ static void td_done (struct ohci_hcd *oh
}
}
+/* default operations for most HCs */
+static const struct ohci_ops ohci_ops = {
+ .dma_pool_alloc = dma_pool_alloc,
+ .dma_pool_free = dma_pool_free,
+ .td_fill = td_fill,
+ .td_done = td_done,
+};
+
/*-------------------------------------------------------------------------*/
static inline struct td *
@@ -984,7 +993,7 @@ rescan_this:
*prev = td->hwNextTD | savebits;
/* HC may have partly processed this TD */
- td_done (ohci, urb, td);
+ ohci->ops->td_done (ohci, urb, td);
urb_priv->td_cnt++;
/* if URB is done, clean up */
@@ -1079,7 +1088,7 @@ dl_done_list (struct ohci_hcd *ohci, str
struct ed *ed = td->ed;
/* update URB's length and status from TD */
- td_done (ohci, urb, td);
+ ohci->ops->td_done (ohci, urb, td);
urb_priv->td_cnt++;
/* If all this urb's TDs are done, call complete() */
|