aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@hobbes.lan>2008-07-28 18:15:46 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-07-28 18:15:46 -0400
commit756f7bc6683916177e8176f8d3fa5f4c11c88afc (patch)
treecbe3ffc375950b016198fa61dea63bb449c59c93
parent979b1791e5b8f8b556faeec4c48339e7ed63af9f (diff)
parentcb28a1bbdb4790378e7366d6c9ee1d2340b84f92 (diff)
Merge branch 'core/generic-dma-coherent' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip into for-linus
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/mm/consistent.c8
-rw-r--r--arch/cris/arch-v32/drivers/Kconfig1
-rw-r--r--arch/cris/arch-v32/drivers/pci/dma.c106
-rw-r--r--arch/sh/Kconfig1
-rw-r--r--arch/sh/mm/consistent.c98
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/kernel/pci-dma.c122
-rw-r--r--include/asm-arm/dma-mapping.h2
-rw-r--r--include/asm-cris/dma-mapping.h2
-rw-r--r--include/asm-generic/dma-coherent.h32
-rw-r--r--include/asm-sh/dma-mapping.h1
-rw-r--r--include/asm-x86/dma-mapping.h22
-rw-r--r--init/Kconfig4
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/dma-coherent.c154
16 files changed, 217 insertions, 339 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index c8f528284a94..652cd32a09c5 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -17,6 +17,7 @@ config ARM
17 select HAVE_KRETPROBES if (HAVE_KPROBES) 17 select HAVE_KRETPROBES if (HAVE_KPROBES)
18 select HAVE_FTRACE if (!XIP_KERNEL) 18 select HAVE_FTRACE if (!XIP_KERNEL)
19 select HAVE_DYNAMIC_FTRACE if (HAVE_FTRACE) 19 select HAVE_DYNAMIC_FTRACE if (HAVE_FTRACE)
20 select HAVE_GENERIC_DMA_COHERENT
20 help 21 help
21 The ARM series is a line of low-power-consumption RISC chip designs 22 The ARM series is a line of low-power-consumption RISC chip designs
22 licensed by ARM Ltd and targeted at embedded applications and 23 licensed by ARM Ltd and targeted at embedded applications and
diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c
index 333a82a3717e..db7b3e38ef1d 100644
--- a/arch/arm/mm/consistent.c
+++ b/arch/arm/mm/consistent.c
@@ -274,6 +274,11 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
274void * 274void *
275dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) 275dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
276{ 276{
277 void *memory;
278
279 if (dma_alloc_from_coherent(dev, size, handle, &memory))
280 return memory;
281
277 if (arch_is_coherent()) { 282 if (arch_is_coherent()) {
278 void *virt; 283 void *virt;
279 284
@@ -362,6 +367,9 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
362 367
363 WARN_ON(irqs_disabled()); 368 WARN_ON(irqs_disabled());
364 369
370 if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
371 return;
372
365 if (arch_is_coherent()) { 373 if (arch_is_coherent()) {
366 kfree(cpu_addr); 374 kfree(cpu_addr);
367 return; 375 return;
diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig
index 2a92cb1886ca..7a64fcef9d07 100644
--- a/arch/cris/arch-v32/drivers/Kconfig
+++ b/arch/cris/arch-v32/drivers/Kconfig
@@ -641,6 +641,7 @@ config PCI
641 bool 641 bool
642 depends on ETRAX_CARDBUS 642 depends on ETRAX_CARDBUS
643 default y 643 default y
644 select HAVE_GENERIC_DMA_COHERENT
644 645
645config ETRAX_IOP_FW_LOAD 646config ETRAX_IOP_FW_LOAD
646 tristate "IO-processor hotplug firmware loading support" 647 tristate "IO-processor hotplug firmware loading support"
diff --git a/arch/cris/arch-v32/drivers/pci/dma.c b/arch/cris/arch-v32/drivers/pci/dma.c
index e0364654fc44..fbe65954ee6c 100644
--- a/arch/cris/arch-v32/drivers/pci/dma.c
+++ b/arch/cris/arch-v32/drivers/pci/dma.c
@@ -15,35 +15,16 @@
15#include <linux/pci.h> 15#include <linux/pci.h>
16#include <asm/io.h> 16#include <asm/io.h>
17 17
18struct dma_coherent_mem {
19 void *virt_base;
20 u32 device_base;
21 int size;
22 int flags;
23 unsigned long *bitmap;
24};
25
26void *dma_alloc_coherent(struct device *dev, size_t size, 18void *dma_alloc_coherent(struct device *dev, size_t size,
27 dma_addr_t *dma_handle, gfp_t gfp) 19 dma_addr_t *dma_handle, gfp_t gfp)
28{ 20{
29 void *ret; 21 void *ret;
30 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
31 int order = get_order(size); 22 int order = get_order(size);
32 /* ignore region specifiers */ 23 /* ignore region specifiers */
33 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); 24 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
34 25
35 if (mem) { 26 if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
36 int page = bitmap_find_free_region(mem->bitmap, mem->size, 27 return ret;
37 order);
38 if (page >= 0) {
39 *dma_handle = mem->device_base + (page << PAGE_SHIFT);
40 ret = mem->virt_base + (page << PAGE_SHIFT);
41 memset(ret, 0, size);
42 return ret;
43 }
44 if (mem->flags & DMA_MEMORY_EXCLUSIVE)
45 return NULL;
46 }
47 28
48 if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) 29 if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
49 gfp |= GFP_DMA; 30 gfp |= GFP_DMA;
@@ -60,90 +41,9 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
60void dma_free_coherent(struct device *dev, size_t size, 41void dma_free_coherent(struct device *dev, size_t size,
61 void *vaddr, dma_addr_t dma_handle) 42 void *vaddr, dma_addr_t dma_handle)
62{ 43{
63 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
64 int order = get_order(size); 44 int order = get_order(size);
65 45
66 if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { 46 if (!dma_release_from_coherent(dev, order, vaddr))
67 int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
68
69 bitmap_release_region(mem->bitmap, page, order);
70 } else
71 free_pages((unsigned long)vaddr, order); 47 free_pages((unsigned long)vaddr, order);
72} 48}
73 49
74int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
75 dma_addr_t device_addr, size_t size, int flags)
76{
77 void __iomem *mem_base;
78 int pages = size >> PAGE_SHIFT;
79 int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
80
81 if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
82 goto out;
83 if (!size)
84 goto out;
85 if (dev->dma_mem)
86 goto out;
87
88 /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
89
90 mem_base = ioremap(bus_addr, size);
91 if (!mem_base)
92 goto out;
93
94 dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
95 if (!dev->dma_mem)
96 goto iounmap_out;
97 dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
98 if (!dev->dma_mem->bitmap)
99 goto free1_out;
100
101 dev->dma_mem->virt_base = mem_base;
102 dev->dma_mem->device_base = device_addr;
103 dev->dma_mem->size = pages;
104 dev->dma_mem->flags = flags;
105
106 if (flags & DMA_MEMORY_MAP)
107 return DMA_MEMORY_MAP;
108
109 return DMA_MEMORY_IO;
110
111 free1_out:
112 kfree(dev->dma_mem);
113 iounmap_out:
114 iounmap(mem_base);
115 out:
116 return 0;
117}
118EXPORT_SYMBOL(dma_declare_coherent_memory);
119
120void dma_release_declared_memory(struct device *dev)
121{
122 struct dma_coherent_mem *mem = dev->dma_mem;
123
124 if(!mem)
125 return;
126 dev->dma_mem = NULL;
127 iounmap(mem->virt_base);
128 kfree(mem->bitmap);
129 kfree(mem);
130}
131EXPORT_SYMBOL(dma_release_declared_memory);
132
133void *dma_mark_declared_memory_occupied(struct device *dev,
134 dma_addr_t device_addr, size_t size)
135{
136 struct dma_coherent_mem *mem = dev->dma_mem;
137 int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
138 int pos, err;
139
140 if (!mem)
141 return ERR_PTR(-EINVAL);
142
143 pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
144 err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
145 if (err != 0)
146 return ERR_PTR(err);
147 return mem->virt_base + (pos << PAGE_SHIFT);
148}
149EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 7bfb0d219d67..0b88dc462d73 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -11,6 +11,7 @@ config SUPERH
11 select HAVE_CLK 11 select HAVE_CLK
12 select HAVE_IDE 12 select HAVE_IDE
13 select HAVE_OPROFILE 13 select HAVE_OPROFILE
14 select HAVE_GENERIC_DMA_COHERENT
14 help 15 help
15 The SuperH is a RISC processor targeted for use in embedded systems 16 The SuperH is a RISC processor targeted for use in embedded systems
16 and consumer electronics; it was also used in the Sega Dreamcast 17 and consumer electronics; it was also used in the Sega Dreamcast
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
index 8277982d0938..b2ce014401b5 100644
--- a/arch/sh/mm/consistent.c
+++ b/arch/sh/mm/consistent.c
@@ -28,21 +28,10 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
28 dma_addr_t *dma_handle, gfp_t gfp) 28 dma_addr_t *dma_handle, gfp_t gfp)
29{ 29{
30 void *ret, *ret_nocache; 30 void *ret, *ret_nocache;
31 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
32 int order = get_order(size); 31 int order = get_order(size);
33 32
34 if (mem) { 33 if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
35 int page = bitmap_find_free_region(mem->bitmap, mem->size, 34 return ret;
36 order);
37 if (page >= 0) {
38 *dma_handle = mem->device_base + (page << PAGE_SHIFT);
39 ret = mem->virt_base + (page << PAGE_SHIFT);
40 memset(ret, 0, size);
41 return ret;
42 }
43 if (mem->flags & DMA_MEMORY_EXCLUSIVE)
44 return NULL;
45 }
46 35
47 ret = (void *)__get_free_pages(gfp, order); 36 ret = (void *)__get_free_pages(gfp, order);
48 if (!ret) 37 if (!ret)
@@ -72,11 +61,7 @@ void dma_free_coherent(struct device *dev, size_t size,
72 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; 61 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
73 int order = get_order(size); 62 int order = get_order(size);
74 63
75 if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { 64 if (!dma_release_from_coherent(dev, order, vaddr)) {
76 int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
77
78 bitmap_release_region(mem->bitmap, page, order);
79 } else {
80 WARN_ON(irqs_disabled()); /* for portability */ 65 WARN_ON(irqs_disabled()); /* for portability */
81 BUG_ON(mem && mem->flags & DMA_MEMORY_EXCLUSIVE); 66 BUG_ON(mem && mem->flags & DMA_MEMORY_EXCLUSIVE);
82 free_pages((unsigned long)phys_to_virt(dma_handle), order); 67 free_pages((unsigned long)phys_to_virt(dma_handle), order);
@@ -85,83 +70,6 @@ void dma_free_coherent(struct device *dev, size_t size,
85} 70}
86EXPORT_SYMBOL(dma_free_coherent); 71EXPORT_SYMBOL(dma_free_coherent);
87 72
88int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
89 dma_addr_t device_addr, size_t size, int flags)
90{
91 void __iomem *mem_base = NULL;
92 int pages = size >> PAGE_SHIFT;
93 int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
94
95 if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
96 goto out;
97 if (!size)
98 goto out;
99 if (dev->dma_mem)
100 goto out;
101
102 /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
103
104 mem_base = ioremap_nocache(bus_addr, size);
105 if (!mem_base)
106 goto out;
107
108 dev->dma_mem = kmalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
109 if (!dev->dma_mem)
110 goto out;
111 dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
112 if (!dev->dma_mem->bitmap)
113 goto free1_out;
114
115 dev->dma_mem->virt_base = mem_base;
116 dev->dma_mem->device_base = device_addr;
117 dev->dma_mem->size = pages;
118 dev->dma_mem->flags = flags;
119
120 if (flags & DMA_MEMORY_MAP)
121 return DMA_MEMORY_MAP;
122
123 return DMA_MEMORY_IO;
124
125 free1_out:
126 kfree(dev->dma_mem);
127 out:
128 if (mem_base)
129 iounmap(mem_base);
130 return 0;
131}
132EXPORT_SYMBOL(dma_declare_coherent_memory);
133
134void dma_release_declared_memory(struct device *dev)
135{
136 struct dma_coherent_mem *mem = dev->dma_mem;
137
138 if (!mem)
139 return;
140 dev->dma_mem = NULL;
141 iounmap(mem->virt_base);
142 kfree(mem->bitmap);
143 kfree(mem);
144}
145EXPORT_SYMBOL(dma_release_declared_memory);
146
147void *dma_mark_declared_memory_occupied(struct device *dev,
148 dma_addr_t device_addr, size_t size)
149{
150 struct dma_coherent_mem *mem = dev->dma_mem;
151 int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT;
152 int pos, err;
153
154 if (!mem)
155 return ERR_PTR(-EINVAL);
156
157 pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
158 err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
159 if (err != 0)
160 return ERR_PTR(err);
161 return mem->virt_base + (pos << PAGE_SHIFT);
162}
163EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
164
165void dma_cache_sync(struct device *dev, void *vaddr, size_t size, 73void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
166 enum dma_data_direction direction) 74 enum dma_data_direction direction)
167{ 75{
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index b6fa2877b173..3d0f2b6a5a16 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -30,6 +30,7 @@ config X86
30 select HAVE_FTRACE 30 select HAVE_FTRACE
31 select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64) 31 select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
32 select HAVE_ARCH_KGDB if !X86_VOYAGER 32 select HAVE_ARCH_KGDB if !X86_VOYAGER
33 select HAVE_GENERIC_DMA_COHERENT if X86_32
33 select HAVE_EFFICIENT_UNALIGNED_ACCESS 34 select HAVE_EFFICIENT_UNALIGNED_ACCESS
34 35
35config ARCH_DEFCONFIG 36config ARCH_DEFCONFIG
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index 37544123896d..8dbffb846de9 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -192,124 +192,6 @@ static __init int iommu_setup(char *p)
192} 192}
193early_param("iommu", iommu_setup); 193early_param("iommu", iommu_setup);
194 194
195#ifdef CONFIG_X86_32
196int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
197 dma_addr_t device_addr, size_t size, int flags)
198{
199 void __iomem *mem_base = NULL;
200 int pages = size >> PAGE_SHIFT;
201 int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
202
203 if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
204 goto out;
205 if (!size)
206 goto out;
207 if (dev->dma_mem)
208 goto out;
209
210 /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
211
212 mem_base = ioremap(bus_addr, size);
213 if (!mem_base)
214 goto out;
215
216 dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
217 if (!dev->dma_mem)
218 goto out;
219 dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
220 if (!dev->dma_mem->bitmap)
221 goto free1_out;
222
223 dev->dma_mem->virt_base = mem_base;
224 dev->dma_mem->device_base = device_addr;
225 dev->dma_mem->size = pages;
226 dev->dma_mem->flags = flags;
227
228 if (flags & DMA_MEMORY_MAP)
229 return DMA_MEMORY_MAP;
230
231 return DMA_MEMORY_IO;
232
233 free1_out:
234 kfree(dev->dma_mem);
235 out:
236 if (mem_base)
237 iounmap(mem_base);
238 return 0;
239}
240EXPORT_SYMBOL(dma_declare_coherent_memory);
241
242void dma_release_declared_memory(struct device *dev)
243{
244 struct dma_coherent_mem *mem = dev->dma_mem;
245
246 if (!mem)
247 return;
248 dev->dma_mem = NULL;
249 iounmap(mem->virt_base);
250 kfree(mem->bitmap);
251 kfree(mem);
252}
253EXPORT_SYMBOL(dma_release_declared_memory);
254
255void *dma_mark_declared_memory_occupied(struct device *dev,
256 dma_addr_t device_addr, size_t size)
257{
258 struct dma_coherent_mem *mem = dev->dma_mem;
259 int pos, err;
260 int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1);
261
262 pages >>= PAGE_SHIFT;
263
264 if (!mem)
265 return ERR_PTR(-EINVAL);
266
267 pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
268 err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
269 if (err != 0)
270 return ERR_PTR(err);
271 return mem->virt_base + (pos << PAGE_SHIFT);
272}
273EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
274
275static int dma_alloc_from_coherent_mem(struct device *dev, ssize_t size,
276 dma_addr_t *dma_handle, void **ret)
277{
278 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
279 int order = get_order(size);
280
281 if (mem) {
282 int page = bitmap_find_free_region(mem->bitmap, mem->size,
283 order);
284 if (page >= 0) {
285 *dma_handle = mem->device_base + (page << PAGE_SHIFT);
286 *ret = mem->virt_base + (page << PAGE_SHIFT);
287 memset(*ret, 0, size);
288 }
289 if (mem->flags & DMA_MEMORY_EXCLUSIVE)
290 *ret = NULL;
291 }
292 return (mem != NULL);
293}
294
295static int dma_release_coherent(struct device *dev, int order, void *vaddr)
296{
297 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
298
299 if (mem && vaddr >= mem->virt_base && vaddr <
300 (mem->virt_base + (mem->size << PAGE_SHIFT))) {
301 int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
302
303 bitmap_release_region(mem->bitmap, page, order);
304 return 1;
305 }
306 return 0;
307}
308#else
309#define dma_alloc_from_coherent_mem(dev, size, handle, ret) (0)
310#define dma_release_coherent(dev, order, vaddr) (0)
311#endif /* CONFIG_X86_32 */
312
313int dma_supported(struct device *dev, u64 mask) 195int dma_supported(struct device *dev, u64 mask)
314{ 196{
315 struct dma_mapping_ops *ops = get_dma_ops(dev); 197 struct dma_mapping_ops *ops = get_dma_ops(dev);
@@ -379,7 +261,7 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
379 /* ignore region specifiers */ 261 /* ignore region specifiers */
380 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32); 262 gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
381 263
382 if (dma_alloc_from_coherent_mem(dev, size, dma_handle, &memory)) 264 if (dma_alloc_from_coherent(dev, size, dma_handle, &memory))
383 return memory; 265 return memory;
384 266
385 if (!dev) { 267 if (!dev) {
@@ -484,7 +366,7 @@ void dma_free_coherent(struct device *dev, size_t size,
484 366
485 int order = get_order(size); 367 int order = get_order(size);
486 WARN_ON(irqs_disabled()); /* for portability */ 368 WARN_ON(irqs_disabled()); /* for portability */
487 if (dma_release_coherent(dev, order, vaddr)) 369 if (dma_release_from_coherent(dev, order, vaddr))
488 return; 370 return;
489 if (ops->unmap_single) 371 if (ops->unmap_single)
490 ops->unmap_single(dev, bus, size, 0); 372 ops->unmap_single(dev, bus, size, 0);
diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h
index f41335ba6337..45329fca1b64 100644
--- a/include/asm-arm/dma-mapping.h
+++ b/include/asm-arm/dma-mapping.h
@@ -7,6 +7,8 @@
7 7
8#include <linux/scatterlist.h> 8#include <linux/scatterlist.h>
9 9
10#include <asm-generic/dma-coherent.h>
11
10/* 12/*
11 * DMA-consistent mapping functions. These allocate/free a region of 13 * DMA-consistent mapping functions. These allocate/free a region of
12 * uncached, unwrite-buffered mapped memory space for use with DMA 14 * uncached, unwrite-buffered mapped memory space for use with DMA
diff --git a/include/asm-cris/dma-mapping.h b/include/asm-cris/dma-mapping.h
index cb2fb25ff8d9..da8ef8e8f842 100644
--- a/include/asm-cris/dma-mapping.h
+++ b/include/asm-cris/dma-mapping.h
@@ -14,6 +14,8 @@
14#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) 14#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
15 15
16#ifdef CONFIG_PCI 16#ifdef CONFIG_PCI
17#include <asm-generic/dma-coherent.h>
18
17void *dma_alloc_coherent(struct device *dev, size_t size, 19void *dma_alloc_coherent(struct device *dev, size_t size,
18 dma_addr_t *dma_handle, gfp_t flag); 20 dma_addr_t *dma_handle, gfp_t flag);
19 21
diff --git a/include/asm-generic/dma-coherent.h b/include/asm-generic/dma-coherent.h
new file mode 100644
index 000000000000..85a3ffaa0242
--- /dev/null
+++ b/include/asm-generic/dma-coherent.h
@@ -0,0 +1,32 @@
1#ifndef DMA_COHERENT_H
2#define DMA_COHERENT_H
3
4#ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT
5/*
6 * These two functions are only for dma allocator.
7 * Don't use them in device drivers.
8 */
9int dma_alloc_from_coherent(struct device *dev, ssize_t size,
10 dma_addr_t *dma_handle, void **ret);
11int dma_release_from_coherent(struct device *dev, int order, void *vaddr);
12
13/*
14 * Standard interface
15 */
16#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
17extern int
18dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
19 dma_addr_t device_addr, size_t size, int flags);
20
21extern void
22dma_release_declared_memory(struct device *dev);
23
24extern void *
25dma_mark_declared_memory_occupied(struct device *dev,
26 dma_addr_t device_addr, size_t size);
27#else
28#define dma_alloc_from_coherent(dev, size, handle, ret) (0)
29#define dma_release_from_coherent(dev, order, vaddr) (0)
30#endif
31
32#endif
diff --git a/include/asm-sh/dma-mapping.h b/include/asm-sh/dma-mapping.h
index 6c0b8a2de143..627315ecdb52 100644
--- a/include/asm-sh/dma-mapping.h
+++ b/include/asm-sh/dma-mapping.h
@@ -5,6 +5,7 @@
5#include <linux/scatterlist.h> 5#include <linux/scatterlist.h>
6#include <asm/cacheflush.h> 6#include <asm/cacheflush.h>
7#include <asm/io.h> 7#include <asm/io.h>
8#include <asm-generic/dma-coherent.h>
8 9
9extern struct bus_type pci_bus_type; 10extern struct bus_type pci_bus_type;
10 11
diff --git a/include/asm-x86/dma-mapping.h b/include/asm-x86/dma-mapping.h
index 0eaa9bf6011f..ad9cd6d49bfc 100644
--- a/include/asm-x86/dma-mapping.h
+++ b/include/asm-x86/dma-mapping.h
@@ -249,25 +249,5 @@ static inline int dma_get_cache_alignment(void)
249 249
250#define dma_is_consistent(d, h) (1) 250#define dma_is_consistent(d, h) (1)
251 251
252#ifdef CONFIG_X86_32 252#include <asm-generic/dma-coherent.h>
253# define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
254struct dma_coherent_mem {
255 void *virt_base;
256 u32 device_base;
257 int size;
258 int flags;
259 unsigned long *bitmap;
260};
261
262extern int
263dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
264 dma_addr_t device_addr, size_t size, int flags);
265
266extern void
267dma_release_declared_memory(struct device *dev);
268
269extern void *
270dma_mark_declared_memory_occupied(struct device *dev,
271 dma_addr_t device_addr, size_t size);
272#endif /* CONFIG_X86_32 */
273#endif 253#endif
diff --git a/init/Kconfig b/init/Kconfig
index 43d6989c275f..250e02c8f8f9 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -802,6 +802,10 @@ config PROC_PAGE_MONITOR
802 802
803endmenu # General setup 803endmenu # General setup
804 804
805config HAVE_GENERIC_DMA_COHERENT
806 bool
807 default n
808
805config SLABINFO 809config SLABINFO
806 bool 810 bool
807 depends on PROC_FS 811 depends on PROC_FS
diff --git a/kernel/Makefile b/kernel/Makefile
index 54f69837d35a..4e1d7df7c3e2 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
84obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o 84obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
85obj-$(CONFIG_MARKERS) += marker.o 85obj-$(CONFIG_MARKERS) += marker.o
86obj-$(CONFIG_LATENCYTOP) += latencytop.o 86obj-$(CONFIG_LATENCYTOP) += latencytop.o
87obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
87obj-$(CONFIG_FTRACE) += trace/ 88obj-$(CONFIG_FTRACE) += trace/
88obj-$(CONFIG_TRACING) += trace/ 89obj-$(CONFIG_TRACING) += trace/
89obj-$(CONFIG_SMP) += sched_cpupri.o 90obj-$(CONFIG_SMP) += sched_cpupri.o
diff --git a/kernel/dma-coherent.c b/kernel/dma-coherent.c
new file mode 100644
index 000000000000..7517115a8cce
--- /dev/null
+++ b/kernel/dma-coherent.c
@@ -0,0 +1,154 @@
1/*
2 * Coherent per-device memory handling.
3 * Borrowed from i386
4 */
5#include <linux/kernel.h>
6#include <linux/dma-mapping.h>
7
8struct dma_coherent_mem {
9 void *virt_base;
10 u32 device_base;
11 int size;
12 int flags;
13 unsigned long *bitmap;
14};
15
16int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
17 dma_addr_t device_addr, size_t size, int flags)
18{
19 void __iomem *mem_base = NULL;
20 int pages = size >> PAGE_SHIFT;
21 int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
22
23 if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
24 goto out;
25 if (!size)
26 goto out;
27 if (dev->dma_mem)
28 goto out;
29
30 /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
31
32 mem_base = ioremap(bus_addr, size);
33 if (!mem_base)
34 goto out;
35
36 dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
37 if (!dev->dma_mem)
38 goto out;
39 dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
40 if (!dev->dma_mem->bitmap)
41 goto free1_out;
42
43 dev->dma_mem->virt_base = mem_base;
44 dev->dma_mem->device_base = device_addr;
45 dev->dma_mem->size = pages;
46 dev->dma_mem->flags = flags;
47
48 if (flags & DMA_MEMORY_MAP)
49 return DMA_MEMORY_MAP;
50
51 return DMA_MEMORY_IO;
52
53 free1_out:
54 kfree(dev->dma_mem);
55 out:
56 if (mem_base)
57 iounmap(mem_base);
58 return 0;
59}
60EXPORT_SYMBOL(dma_declare_coherent_memory);
61
62void dma_release_declared_memory(struct device *dev)
63{
64 struct dma_coherent_mem *mem = dev->dma_mem;
65
66 if (!mem)
67 return;
68 dev->dma_mem = NULL;
69 iounmap(mem->virt_base);
70 kfree(mem->bitmap);
71 kfree(mem);
72}
73EXPORT_SYMBOL(dma_release_declared_memory);
74
75void *dma_mark_declared_memory_occupied(struct device *dev,
76 dma_addr_t device_addr, size_t size)
77{
78 struct dma_coherent_mem *mem = dev->dma_mem;
79 int pos, err;
80 int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1);
81
82 pages >>= PAGE_SHIFT;
83
84 if (!mem)
85 return ERR_PTR(-EINVAL);
86
87 pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
88 err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages));
89 if (err != 0)
90 return ERR_PTR(err);
91 return mem->virt_base + (pos << PAGE_SHIFT);
92}
93EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
94
95/**
96 * Try to allocate memory from the per-device coherent area.
97 *
98 * @dev: device from which we allocate memory
99 * @size: size of requested memory area
100 * @dma_handle: This will be filled with the correct dma handle
101 * @ret: This pointer will be filled with the virtual address
102 * to allocated area.
103 *
104 * This function should be only called from per-arch %dma_alloc_coherent()
105 * to support allocation from per-device coherent memory pools.
106 *
107 * Returns 0 if dma_alloc_coherent should continue with allocating from
108 * generic memory areas, or !0 if dma_alloc_coherent should return %ret.
109 */
110int dma_alloc_from_coherent(struct device *dev, ssize_t size,
111 dma_addr_t *dma_handle, void **ret)
112{
113 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
114 int order = get_order(size);
115
116 if (mem) {
117 int page = bitmap_find_free_region(mem->bitmap, mem->size,
118 order);
119 if (page >= 0) {
120 *dma_handle = mem->device_base + (page << PAGE_SHIFT);
121 *ret = mem->virt_base + (page << PAGE_SHIFT);
122 memset(*ret, 0, size);
123 } else if (mem->flags & DMA_MEMORY_EXCLUSIVE)
124 *ret = NULL;
125 }
126 return (mem != NULL);
127}
128
129/**
130 * Try to free the memory allocated from per-device coherent memory pool.
131 * @dev: device from which the memory was allocated
132 * @order: the order of pages allocated
133 * @vaddr: virtual address of allocated pages
134 *
135 * This checks whether the memory was allocated from the per-device
136 * coherent memory pool and if so, releases that memory.
137 *
138 * Returns 1 if we correctly released the memory, or 0 if
139 * %dma_release_coherent() should proceed with releasing memory from
140 * generic pools.
141 */
142int dma_release_from_coherent(struct device *dev, int order, void *vaddr)
143{
144 struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
145
146 if (mem && vaddr >= mem->virt_base && vaddr <
147 (mem->virt_base + (mem->size << PAGE_SHIFT))) {
148 int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
149
150 bitmap_release_region(mem->bitmap, page, order);
151 return 1;
152 }
153 return 0;
154}
[priv->firmware_type], 0); break; case FIRMWARE_TYPE_SYMBOL: err = symbol_dl_firmware(priv, &orinoco_fw[priv->firmware_type]); break; case FIRMWARE_TYPE_INTERSIL: break; } /* TODO: if we fail we probably need to reinitialise * the driver */ return err; } /********************************************************************/ /* Device methods */ /********************************************************************/ static int orinoco_open(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; int err; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = __orinoco_up(dev); if (! err) priv->open = 1; orinoco_unlock(priv, &flags); return err; } static int orinoco_stop(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); int err = 0; /* We mustn't use orinoco_lock() here, because we need to be able to close the interface even if hw_unavailable is set (e.g. as we're released after a PC Card removal) */ spin_lock_irq(&priv->lock); priv->open = 0; err = __orinoco_down(dev); spin_unlock_irq(&priv->lock); return err; } static struct net_device_stats *orinoco_get_stats(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); return &priv->stats; } static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; struct iw_statistics *wstats = &priv->wstats; int err; unsigned long flags; if (! netif_device_present(dev)) { printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", dev->name); return NULL; /* FIXME: Can we do better than this? */ } /* If busy, return the old stats. Returning NULL may cause * the interface to disappear from /proc/net/wireless */ if (orinoco_lock(priv, &flags) != 0) return wstats; /* We can't really wait for the tallies inquiry command to * complete, so we just use the previous results and trigger * a new tallies inquiry command for next time - Jean II */ /* FIXME: Really we should wait for the inquiry to come back - * as it is the stats we give don't make a whole lot of sense. * Unfortunately, it's not clear how to do that within the * wireless extensions framework: I think we're in user * context, but a lock seems to be held by the time we get in * here so we're not safe to sleep here. */ hermes_inquire(hw, HERMES_INQ_TALLIES); if (priv->iw_mode == IW_MODE_ADHOC) { memset(&wstats->qual, 0, sizeof(wstats->qual)); /* If a spy address is defined, we report stats of the * first spy address - Jean II */ if (SPY_NUMBER(priv)) { wstats->qual.qual = priv->spy_data.spy_stat[0].qual; wstats->qual.level = priv->spy_data.spy_stat[0].level; wstats->qual.noise = priv->spy_data.spy_stat[0].noise; wstats->qual.updated = priv->spy_data.spy_stat[0].updated; } } else { struct { __le16 qual, signal, noise, unused; } __attribute__ ((packed)) cq; err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_COMMSQUALITY, &cq); if (!err) { wstats->qual.qual = (int)le16_to_cpu(cq.qual); wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; wstats->qual.updated = 7; } } orinoco_unlock(priv, &flags); return wstats; } static void orinoco_set_multicast_list(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " "called when hw_unavailable\n", dev->name); return; } __orinoco_set_multicast_list(dev); orinoco_unlock(priv, &flags); } static int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = netdev_priv(dev); if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) return -EINVAL; if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) > (priv->nicbuf_size - ETH_HLEN) ) return -EINVAL; dev->mtu = new_mtu; return 0; } /********************************************************************/ /* Tx path */ /********************************************************************/ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; hermes_t *hw = &priv->hw; int err = 0; u16 txfid = priv->txfid; struct ethhdr *eh; int data_off; int tx_control; unsigned long flags; if (! netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); return NETDEV_TX_BUSY; } if (netif_queue_stopped(dev)) { printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", dev->name); return NETDEV_TX_BUSY; } if (orinoco_lock(priv, &flags) != 0) { printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", dev->name); return NETDEV_TX_BUSY; } if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) { /* Oops, the firmware hasn't established a connection, silently drop the packet (this seems to be the safest approach). */ goto drop; } /* Check packet length */ if (skb->len < ETH_HLEN) goto drop; eh = (struct ethhdr *)skb->data; tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; if (priv->has_alt_txcntl) { /* WPA enabled firmwares have tx_cntl at the end of * the 802.11 header. So write zeroed descriptor and * 802.11 header at the same time */ char desc[HERMES_802_3_OFFSET]; __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET]; memset(&desc, 0, sizeof(desc)); *txcntl = cpu_to_le16(tx_control); err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " "descriptor to BAP\n", dev->name, err); goto busy; } } else { struct hermes_tx_descriptor desc; memset(&desc, 0, sizeof(desc)); desc.tx_control = cpu_to_le16(tx_control); err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing Tx " "descriptor to BAP\n", dev->name, err); goto busy; } /* Clear the 802.11 header and data length fields - some * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused * if this isn't done. */ hermes_clear_words(hw, HERMES_DATA0, HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); } /* Encapsulate Ethernet-II frames */ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ struct header_struct { struct ethhdr eth; /* 802.3 header */ u8 encap[6]; /* 802.2 header */ } __attribute__ ((packed)) hdr; /* Strip destination and source from the data */ skb_pull(skb, 2 * ETH_ALEN); data_off = HERMES_802_2_OFFSET + sizeof(encaps_hdr); /* And move them to a separate header */ memcpy(&hdr.eth, eh, 2 * ETH_ALEN); hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len); memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr), txfid, HERMES_802_3_OFFSET); if (err) { if (net_ratelimit()) printk(KERN_ERR "%s: Error %d writing packet " "header to BAP\n", dev->name, err); goto busy; } } else { /* IEEE 802.3 frame */ data_off = HERMES_802_3_OFFSET; } err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len, txfid, data_off); if (err) { printk(KERN_ERR "%s: Error %d writing packet to BAP\n", dev->name, err); goto busy; } /* Finally, we actually initiate the send */ netif_stop_queue(dev); err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL); if (err) { netif_start_queue(dev); if (net_ratelimit()) printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err); goto busy; } dev->trans_start = jiffies; stats->tx_bytes += data_off + skb->len; goto ok; drop: stats->tx_errors++; stats->tx_dropped++; ok: orinoco_unlock(priv, &flags); dev_kfree_skb(skb); return NETDEV_TX_OK; busy: if (err == -EIO) schedule_work(&priv->reset_work); orinoco_unlock(priv, &flags); return NETDEV_TX_BUSY; } static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); u16 fid = hermes_read_regn(hw, ALLOCFID); if (fid != priv->txfid) { if (fid != DUMMY_FID) printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", dev->name, fid); return; } hermes_write_regn(hw, ALLOCFID, DUMMY_FID); } static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; stats->tx_packets++; netif_wake_queue(dev); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); u16 status; struct hermes_txexc_data hdr; int err = 0; if (fid == DUMMY_FID) return; /* Nothing's really happened */ /* Read part of the frame header - we need status and addr1 */ err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(struct hermes_txexc_data), fid, 0); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); stats->tx_errors++; if (err) { printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " "(FID=%04X error %d)\n", dev->name, fid, err); return; } DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, err, fid); /* We produce a TXDROP event only for retry or lifetime * exceeded, because that's the only status that really mean * that this particular node went away. * Other errors means that *we* screwed up. - Jean II */ status = le16_to_cpu(hdr.desc.status); if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { union iwreq_data wrqu; /* Copy 802.11 dest address. * We use the 802.11 header because the frame may * not be 802.3 or may be mangled... * In Ad-Hoc mode, it will be the node address. * In managed mode, it will be most likely the AP addr * User space will figure out how to convert it to * whatever it needs (IP address or else). * - Jean II */ memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); } netif_wake_queue(dev); } static void orinoco_tx_timeout(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; printk(KERN_WARNING "%s: Tx timeout! " "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", dev->name, hermes_read_regn(hw, ALLOCFID), hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); stats->tx_errors++; schedule_work(&priv->reset_work); } /********************************************************************/ /* Rx path (data frames) */ /********************************************************************/ /* Does the frame have a SNAP header indicating it should be * de-encapsulated to Ethernet-II? */ static inline int is_ethersnap(void *_hdr) { u8 *hdr = _hdr; /* We de-encapsulate all packets which, a) have SNAP headers * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header * and where b) the OUI of the SNAP header is 00:00:00 or * 00:00:f8 - we need both because different APs appear to use * different OUIs for some reason */ return (memcmp(hdr, &encaps_hdr, 5) == 0) && ( (hdr[5] == 0x00) || (hdr[5] == 0xf8) ); } static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, int level, int noise) { struct iw_quality wstats; wstats.level = level - 0x95; wstats.noise = noise - 0x95; wstats.qual = (level > noise) ? (level - noise) : 0; wstats.updated = 7; /* Update spy records */ wireless_spy_update(dev, mac, &wstats); } static void orinoco_stat_gather(struct net_device *dev, struct sk_buff *skb, struct hermes_rx_descriptor *desc) { struct orinoco_private *priv = netdev_priv(dev); /* Using spy support with lots of Rx packets, like in an * infrastructure (AP), will really slow down everything, because * the MAC address must be compared to each entry of the spy list. * If the user really asks for it (set some address in the * spy list), we do it, but he will pay the price. * Note that to get here, you need both WIRELESS_SPY * compiled in AND some addresses in the list !!! */ /* Note : gcc will optimise the whole section away if * WIRELESS_SPY is not defined... - Jean II */ if (SPY_NUMBER(priv)) { orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, desc->signal, desc->silence); } } /* * orinoco_rx_monitor - handle received monitor frames. * * Arguments: * dev network device * rxfid received FID * desc rx descriptor of the frame * * Call context: interrupt */ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, struct hermes_rx_descriptor *desc) { u32 hdrlen = 30; /* return full header by default */ u32 datalen = 0; u16 fc; int err; int len; struct sk_buff *skb; struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; hermes_t *hw = &priv->hw; len = le16_to_cpu(desc->data_len); /* Determine the size of the header and the data */ fc = le16_to_cpu(desc->frame_ctl); switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_DATA: if ((fc & IEEE80211_FCTL_TODS) && (fc & IEEE80211_FCTL_FROMDS)) hdrlen = 30; else hdrlen = 24; datalen = len; break; case IEEE80211_FTYPE_MGMT: hdrlen = 24; datalen = len; break; case IEEE80211_FTYPE_CTL: switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PSPOLL: case IEEE80211_STYPE_RTS: case IEEE80211_STYPE_CFEND: case IEEE80211_STYPE_CFENDACK: hdrlen = 16; break; case IEEE80211_STYPE_CTS: case IEEE80211_STYPE_ACK: hdrlen = 10; break; } break; default: /* Unknown frame type */ break; } /* sanity check the length */ if (datalen > IEEE80211_DATA_LEN + 12) { printk(KERN_DEBUG "%s: oversized monitor frame, " "data length = %d\n", dev->name, datalen); stats->rx_length_errors++; goto update_stats; } skb = dev_alloc_skb(hdrlen + datalen); if (!skb) { printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n", dev->name); goto update_stats; } /* Copy the 802.11 header to the skb */ memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); skb_reset_mac_header(skb); /* If any, copy the data from the card to the skb */ if (datalen > 0) { err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), ALIGN(datalen, 2), rxfid, HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading monitor frame\n", dev->name, err); goto drop; } } skb->dev = dev; skb->ip_summed = CHECKSUM_NONE; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = __constant_htons(ETH_P_802_2); dev->last_rx = jiffies; stats->rx_packets++; stats->rx_bytes += skb->len; netif_rx(skb); return; drop: dev_kfree_skb_irq(skb); update_stats: stats->rx_errors++; stats->rx_dropped++; } static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; u16 rxfid, status, fc; int length; struct hermes_rx_descriptor desc; struct ethhdr *hdr; int err; rxfid = hermes_read_regn(hw, RXFID); err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), rxfid, 0); if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " "Frame dropped.\n", dev->name, err); goto update_stats; } status = le16_to_cpu(desc.status); if (status & HERMES_RXSTAT_BADCRC) { DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); stats->rx_crc_errors++; goto update_stats; } /* Handle frames in monitor mode */ if (priv->iw_mode == IW_MODE_MONITOR) { orinoco_rx_monitor(dev, rxfid, &desc); return; } if (status & HERMES_RXSTAT_UNDECRYPTABLE) { DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", dev->name); wstats->discard.code++; goto update_stats; } length = le16_to_cpu(desc.data_len); fc = le16_to_cpu(desc.frame_ctl); /* Sanity checks */ if (length < 3) { /* No for even an 802.2 LLC header */ /* At least on Symbol firmware with PCF we get quite a lot of these legitimately - Poll frames with no data. */ return; } if (length > IEEE80211_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", dev->name, length); stats->rx_length_errors++; goto update_stats; } /* We need space for the packet data itself, plus an ethernet header, plus 2 bytes so we can align the IP header on a 32bit boundary, plus 1 byte so we can read in odd length packets from the card, which has an IO granularity of 16 bits */ skb = dev_alloc_skb(length+ETH_HLEN+2+1); if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", dev->name); goto update_stats; } /* We'll prepend the header, so reserve space for it. The worst case is no decapsulation, when 802.3 header is prepended and nothing is removed. 2 is for aligning the IP header. */ skb_reserve(skb, ETH_HLEN + 2); err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length), ALIGN(length, 2), rxfid, HERMES_802_2_OFFSET); if (err) { printk(KERN_ERR "%s: error %d reading frame. " "Frame dropped.\n", dev->name, err); goto drop; } /* Handle decapsulation * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs * are not properly recognised by most firmwares. * So, check ourselves */ if (length >= ENCAPS_OVERHEAD && (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || is_ethersnap(skb->data))) { /* These indicate a SNAP within 802.2 LLC within 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD); } else { /* 802.3 frame - prepend 802.3 header as is */ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); hdr->h_proto = htons(length); } memcpy(hdr->h_dest, desc.addr1, ETH_ALEN); if (fc & IEEE80211_FCTL_FROMDS) memcpy(hdr->h_source, desc.addr3, ETH_ALEN); else memcpy(hdr->h_source, desc.addr2, ETH_ALEN); dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; if (fc & IEEE80211_FCTL_TODS) skb->pkt_type = PACKET_OTHERHOST; /* Process the wireless stats if needed */ orinoco_stat_gather(dev, skb, &desc); /* Pass the packet to the networking stack */ netif_rx(skb); stats->rx_packets++; stats->rx_bytes += length; return; drop: dev_kfree_skb_irq(skb); update_stats: stats->rx_errors++; stats->rx_dropped++; } /********************************************************************/ /* Rx path (info frames) */ /********************************************************************/ static void print_linkstatus(struct net_device *dev, u16 status) { char * s; if (suppress_linkstatus) return; switch (status) { case HERMES_LINKSTATUS_NOT_CONNECTED: s = "Not Connected"; break; case HERMES_LINKSTATUS_CONNECTED: s = "Connected"; break; case HERMES_LINKSTATUS_DISCONNECTED: s = "Disconnected"; break; case HERMES_LINKSTATUS_AP_CHANGE: s = "AP Changed"; break; case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: s = "AP Out of Range"; break; case HERMES_LINKSTATUS_AP_IN_RANGE: s = "AP In Range"; break; case HERMES_LINKSTATUS_ASSOC_FAILED: s = "Association Failed"; break; default: s = "UNKNOWN"; } printk(KERN_INFO "%s: New link status: %s (%04x)\n", dev->name, s, status); } /* Search scan results for requested BSSID, join it if found */ static void orinoco_join_ap(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, join_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; unsigned long flags; struct join_req { u8 bssid[ETH_ALEN]; __le16 channel; } __attribute__ ((packed)) req; const int atom_len = offsetof(struct prism2_scan_apinfo, atim); struct prism2_scan_apinfo *atom = NULL; int offset = 4; int found = 0; u8 *buf; u16 len; /* Allocate buffer for scan results */ buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); if (! buf) return; if (orinoco_lock(priv, &flags) != 0) goto fail_lock; /* Sanity checks in case user changed something in the meantime */ if (! priv->bssid_fixed) goto out; if (strlen(priv->desired_essid) == 0) goto out; /* Read scan results from the firmware */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SCANRESULTSTABLE, MAX_SCAN_LEN, &len, buf); if (err) { printk(KERN_ERR "%s: Cannot read scan results\n", dev->name); goto out; } len = HERMES_RECLEN_TO_BYTES(len); /* Go through the scan results looking for the channel of the AP * we were requested to join */ for (; offset + atom_len <= len; offset += atom_len) { atom = (struct prism2_scan_apinfo *) (buf + offset); if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) { found = 1; break; } } if (! found) { DEBUG(1, "%s: Requested AP not found in scan results\n", dev->name); goto out; } memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); req.channel = atom->channel; /* both are little-endian */ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, &req); if (err) printk(KERN_ERR "%s: Error issuing join request\n", dev->name); out: orinoco_unlock(priv, &flags); fail_lock: kfree(buf); } /* Send new BSSID to userspace */ static void orinoco_send_wevents(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, wevent_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; union iwreq_data wrqu; int err; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return; err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, wrqu.ap_addr.sa_data); if (err != 0) goto out; wrqu.ap_addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); out: orinoco_unlock(priv, &flags); } static inline void orinoco_clear_scan_results(struct orinoco_private *priv, unsigned long scan_age) { struct bss_element *bss; struct bss_element *tmp_bss; /* Blow away current list of scan results */ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { if (!scan_age || time_after(jiffies, bss->last_scanned + scan_age)) { list_move_tail(&bss->list, &priv->bss_free_list); /* Don't blow away ->list, just BSS data */ memset(bss, 0, sizeof(bss->bss)); bss->last_scanned = 0; } } } static int orinoco_process_scan_results(struct net_device *dev, unsigned char *buf, int len) { struct orinoco_private *priv = netdev_priv(dev); int offset; /* In the scan data */ union hermes_scan_info *atom; int atom_len; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: atom_len = sizeof(struct agere_scan_apinfo); offset = 0; break; case FIRMWARE_TYPE_SYMBOL: /* Lack of documentation necessitates this hack. * Different firmwares have 68 or 76 byte long atoms. * We try modulo first. If the length divides by both, * we check what would be the channel in the second * frame for a 68-byte atom. 76-byte atoms have 0 there. * Valid channel cannot be 0. */ if (len % 76) atom_len = 68; else if (len % 68) atom_len = 76; else if (len >= 1292 && buf[68] == 0) atom_len = 76; else atom_len = 68; offset = 0; break; case FIRMWARE_TYPE_INTERSIL: offset = 4; if (priv->has_hostscan) { atom_len = le16_to_cpup((__le16 *)buf); /* Sanity check for atom_len */ if (atom_len < sizeof(struct prism2_scan_apinfo)) { printk(KERN_ERR "%s: Invalid atom_len in scan " "data: %d\n", dev->name, atom_len); return -EIO; } } else atom_len = offsetof(struct prism2_scan_apinfo, atim); break; default: return -EOPNOTSUPP; } /* Check that we got an whole number of atoms */ if ((len - offset) % atom_len) { printk(KERN_ERR "%s: Unexpected scan data length %d, " "atom_len %d, offset %d\n", dev->name, len, atom_len, offset); return -EIO; } orinoco_clear_scan_results(priv, msecs_to_jiffies(15000)); /* Read the entries one by one */ for (; offset + atom_len <= len; offset += atom_len) { int found = 0; struct bss_element *bss = NULL; /* Get next atom */ atom = (union hermes_scan_info *) (buf + offset); /* Try to update an existing bss first */ list_for_each_entry(bss, &priv->bss_list, list) { if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid)) continue; if (le16_to_cpu(bss->bss.a.essid_len) != le16_to_cpu(atom->a.essid_len)) continue; if (memcmp(bss->bss.a.essid, atom->a.essid, le16_to_cpu(atom->a.essid_len))) continue; found = 1; break; } /* Grab a bss off the free list */ if (!found && !list_empty(&priv->bss_free_list)) { bss = list_entry(priv->bss_free_list.next, struct bss_element, list); list_del(priv->bss_free_list.next); list_add_tail(&bss->list, &priv->bss_list); } if (bss) { /* Always update the BSS to get latest beacon info */ memcpy(&bss->bss, atom, sizeof(bss->bss)); bss->last_scanned = jiffies; } } return 0; } static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); u16 infofid; struct { __le16 len; __le16 type; } __attribute__ ((packed)) info; int len, type; int err; /* This is an answer to an INQUIRE command that we did earlier, * or an information "event" generated by the card * The controller return to us a pseudo frame containing * the information in question - Jean II */ infofid = hermes_read_regn(hw, INFOFID); /* Read the info frame header - don't try too hard */ err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), infofid, 0); if (err) { printk(KERN_ERR "%s: error %d reading info frame. " "Frame dropped.\n", dev->name, err); return; } len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); type = le16_to_cpu(info.type); switch (type) { case HERMES_INQ_TALLIES: { struct hermes_tallies_frame tallies; struct iw_statistics *wstats = &priv->wstats; if (len > sizeof(tallies)) { printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", dev->name, len); len = sizeof(tallies); } err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len, infofid, sizeof(info)); if (err) break; /* Increment our various counters */ /* wstats->discard.nwid - no wrong BSSID stuff */ wstats->discard.code += le16_to_cpu(tallies.RxWEPUndecryptable); if (len == sizeof(tallies)) wstats->discard.code += le16_to_cpu(tallies.RxDiscards_WEPICVError) + le16_to_cpu(tallies.RxDiscards_WEPExcluded); wstats->discard.misc += le16_to_cpu(tallies.TxDiscardsWrongSA); wstats->discard.fragment += le16_to_cpu(tallies.RxMsgInBadMsgFragments); wstats->discard.retries += le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ } break; case HERMES_INQ_LINKSTATUS: { struct hermes_linkstatus linkstatus; u16 newstatus; int connected; if (priv->iw_mode == IW_MODE_MONITOR) break; if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", dev->name, len); break; } err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len, infofid, sizeof(info)); if (err) break; newstatus = le16_to_cpu(linkstatus.linkstatus); /* Symbol firmware uses "out of range" to signal that * the hostscan frame can be requested. */ if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && priv->firmware_type == FIRMWARE_TYPE_SYMBOL && priv->has_hostscan && priv->scan_inprogress) { hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); break; } connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); if (connected) netif_carrier_on(dev); else if (!ignore_disconnect) netif_carrier_off(dev); if (newstatus != priv->last_linkstatus) { priv->last_linkstatus = newstatus; print_linkstatus(dev, newstatus); /* The info frame contains only one word which is the * status (see hermes.h). The status is pretty boring * in itself, that's why we export the new BSSID... * Jean II */ schedule_work(&priv->wevent_work); } } break; case HERMES_INQ_SCAN: if (!priv->scan_inprogress && priv->bssid_fixed && priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { schedule_work(&priv->join_work); break; } /* fall through */ case HERMES_INQ_HOSTSCAN: case HERMES_INQ_HOSTSCAN_SYMBOL: { /* Result of a scanning. Contains information about * cells in the vicinity - Jean II */ union iwreq_data wrqu; unsigned char *buf; /* Scan is no longer in progress */ priv->scan_inprogress = 0; /* Sanity check */ if (len > 4096) { printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", dev->name, len); break; } /* Allocate buffer for results */ buf = kmalloc(len, GFP_ATOMIC); if (buf == NULL) /* No memory, so can't printk()... */ break; /* Read scan data */ err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, infofid, sizeof(info)); if (err) { kfree(buf); break; } #ifdef ORINOCO_DEBUG { int i; printk(KERN_DEBUG "Scan result [%02X", buf[0]); for(i = 1; i < (len * 2); i++) printk(":%02X", buf[i]); printk("]\n"); } #endif /* ORINOCO_DEBUG */ if (orinoco_process_scan_results(dev, buf, len) == 0) { /* Send an empty event to user space. * We don't send the received data on the event because * it would require us to do complex transcoding, and * we want to minimise the work done in the irq handler * Use a request to extract the data - Jean II */ wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); } kfree(buf); } break; case HERMES_INQ_SEC_STAT_AGERE: /* Security status (Agere specific) */ /* Ignore this frame for now */ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) break; /* fall through */ default: printk(KERN_DEBUG "%s: Unknown information frame received: " "type 0x%04x, length %d\n", dev->name, type, len); /* We don't actually do anything about it */ break; } } static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) { if (net_ratelimit()) printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name); } /********************************************************************/ /* Internal hardware control routines */ /********************************************************************/ int __orinoco_up(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; netif_carrier_off(dev); /* just to make sure */ err = __orinoco_program_rids(dev); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", dev->name, err); return err; } /* Fire things up again */ hermes_set_irqmask(hw, ORINOCO_INTEN); err = hermes_enable_port(hw, 0); if (err) { printk(KERN_ERR "%s: Error %d enabling MAC port\n", dev->name, err); return err; } netif_start_queue(dev); return 0; } int __orinoco_down(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; netif_stop_queue(dev); if (! priv->hw_unavailable) { if (! priv->broken_disableport) { err = hermes_disable_port(hw, 0); if (err) { /* Some firmwares (e.g. Intersil 1.3.x) seem * to have problems disabling the port, oh * well, too bad. */ printk(KERN_WARNING "%s: Error %d disabling MAC port\n", dev->name, err); priv->broken_disableport = 1; } } hermes_set_irqmask(hw, 0); hermes_write_regn(hw, EVACK, 0xffff); } /* firmware will have to reassociate */ netif_carrier_off(dev); priv->last_linkstatus = 0xffff; return 0; } static int orinoco_allocate_fid(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { /* Try workaround for old Symbol firmware bug */ printk(KERN_WARNING "%s: firmware ALLOC bug detected " "(old Symbol firmware?). Trying to work around... ", dev->name); priv->nicbuf_size = TX_NICBUF_SIZE_BUG; err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); if (err) printk("failed!\n"); else printk("ok.\n"); } return err; } int orinoco_reinit_firmware(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; err = hermes_init(hw); if (!err) err = orinoco_allocate_fid(dev); return err; } static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; if (priv->bitratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", priv->ndev->name, priv->bitratemode); return -EINVAL; } switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXRATECONTROL, bitrate_table[priv->bitratemode].agere_txratectrl); break; case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXRATECONTROL, bitrate_table[priv->bitratemode].intersil_txratectrl); break; default: BUG(); } return err; } /* Set fixed AP address */ static int __orinoco_hw_set_wap(struct orinoco_private *priv) { int roaming_flag; int err = 0; hermes_t *hw = &priv->hw; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* not supported */ break; case FIRMWARE_TYPE_INTERSIL: if (priv->bssid_fixed) roaming_flag = 2; else roaming_flag = 1; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFROAMINGMODE, roaming_flag); break; case FIRMWARE_TYPE_SYMBOL: err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFMANDATORYBSSID_SYMBOL, &priv->desired_bssid); break; } return err; } /* Change the WEP keys and/or the current keys. Can be called * either from __orinoco_hw_setup_wep() or directly from * orinoco_ioctl_setiwencode(). In the later case the association * with the AP is not broken (if the firmware can handle it), * which is needed for 802.1x implementations. */ static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFWEPKEYS_AGERE, &priv->keys); if (err) return err; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFTXKEY_AGERE, priv->tx_key); if (err) return err; break; case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: { int keylen; int i; /* Force uniform key length to work around firmware bugs */ keylen = le16_to_cpu(priv->keys[priv->tx_key].len); if (keylen > LARGE_KEY_SIZE) { printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", priv->ndev->name, priv->tx_key, keylen); return -E2BIG; } /* Write all 4 keys */ for(i = 0; i < ORINOCO_MAX_KEYS; i++) { err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), priv->keys[i].data); if (err) return err; } /* Write the index of the key used in transmission */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID, priv->tx_key); if (err) return err; } break; } return 0; } static int __orinoco_hw_setup_wep(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; int master_wep_flag; int auth_flag; if (priv->wep_on) __orinoco_hw_setup_wepkeys(priv); if (priv->wep_restrict) auth_flag = HERMES_AUTH_SHARED_KEY; else auth_flag = HERMES_AUTH_OPEN; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ if (priv->wep_on) { /* Enable the shared-key authentication. */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION_AGERE, auth_flag); } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPENABLED_AGERE, priv->wep_on); if (err) return err; break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ if (priv->wep_on) { if (priv->wep_restrict || (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | HERMES_WEP_EXCL_UNENCRYPTED; else master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION, auth_flag); if (err) return err; } else master_wep_flag = 0; if (priv->iw_mode == IW_MODE_MONITOR) master_wep_flag |= HERMES_WEP_HOST_DECRYPT; /* Master WEP setting : on/off */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPFLAGS_INTERSIL, master_wep_flag); if (err) return err; break; } return 0; } static int __orinoco_program_rids(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err; struct hermes_idstring idbuf; /* Set the MAC address */ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); if (err) { printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); return err; } /* Set up the link mode */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); if (err) { printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); return err; } /* Set the channel/frequency */ if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); if (err) { printk(KERN_ERR "%s: Error %d setting channel %d\n", dev->name, err, priv->channel); return err; } } if (priv->has_ibss) { u16 createibss; if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { printk(KERN_WARNING "%s: This firmware requires an " "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); /* With wvlan_cs, in this case, we would crash. * hopefully, this driver will behave better... * Jean II */ createibss = 0; } else { createibss = priv->createibss; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, createibss); if (err) { printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); return err; } } /* Set the desired BSSID */ err = __orinoco_hw_set_wap(priv); if (err) { printk(KERN_ERR "%s: Error %d setting AP address\n", dev->name, err); return err; } /* Set the desired ESSID */ idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err); return err; } err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err); return err; } /* Set the station name */ idbuf.len = cpu_to_le16(strlen(priv->nick)); memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), &idbuf); if (err) { printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); return err; } /* Set AP density */ if (priv->has_sensitivity) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density); if (err) { printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " "Disabling sensitivity control\n", dev->name, err); priv->has_sensitivity = 0; } } /* Set RTS threshold */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err); return err; } /* Set fragmentation threshold or MWO robustness */ if (priv->has_mwo) err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, priv->mwo_robust); else err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, priv->frag_thresh); if (err) { printk(KERN_ERR "%s: Error %d setting fragmentation\n", dev->name, err); return err; } /* Set bitrate */ err = __orinoco_hw_set_bitrate(priv); if (err) { printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err); return err; } /* Set power management */ if (priv->has_pm) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, priv->pm_on); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, priv->pm_mcast); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, priv->pm_period); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, priv->pm_timeout); if (err) { printk(KERN_ERR "%s: Error %d setting up PM\n", dev->name, err); return err; } } /* Set preamble - only for Symbol so far... */ if (priv->has_preamble) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, priv->preamble); if (err) { printk(KERN_ERR "%s: Error %d setting preamble\n", dev->name, err); return err; } } /* Set up encryption */ if (priv->has_wep) { err = __orinoco_hw_setup_wep(priv); if (err) { printk(KERN_ERR "%s: Error %d activating WEP\n", dev->name, err); return err; } } if (priv->iw_mode == IW_MODE_MONITOR) { /* Enable monitor mode */ dev->type = ARPHRD_IEEE80211; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_MONITOR, 0, NULL); } else { /* Disable monitor mode */ dev->type = ARPHRD_ETHER; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_STOP, 0, NULL); } if (err) return err; /* Set promiscuity / multicast*/ priv->promiscuous = 0; priv->mc_count = 0; /* FIXME: what about netif_tx_lock */ __orinoco_set_multicast_list(dev); return 0; } /* FIXME: return int? */ static void __orinoco_set_multicast_list(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err = 0; int promisc, mc_count; /* The Hermes doesn't seem to have an allmulti mode, so we go * into promiscuous mode and let the upper levels deal. */ if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || (dev->mc_count > MAX_MULTICAST(priv)) ) { promisc = 1; mc_count = 0; } else { promisc = 0; mc_count = dev->mc_count; } if (promisc != priv->promiscuous) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPROMISCUOUSMODE, promisc); if (err) { printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", dev->name, err); } else priv->promiscuous = promisc; } if (! promisc && (mc_count || priv->mc_count) ) { struct dev_mc_list *p = dev->mc_list; struct hermes_multicast mclist; int i; for (i = 0; i < mc_count; i++) { /* paranoia: is list shorter than mc_count? */ BUG_ON(! p); /* paranoia: bad address size in list? */ BUG_ON(p->dmi_addrlen != ETH_ALEN); memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); p = p->next; } if (p) printk(KERN_WARNING "%s: Multicast list is " "longer than mc_count\n", dev->name); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN), &mclist); if (err) printk(KERN_ERR "%s: Error %d setting multicast list.\n", dev->name, err); else priv->mc_count = mc_count; } } /* This must be called from user context, without locks held - use * schedule_work() */ static void orinoco_reset(struct work_struct *work) { struct orinoco_private *priv = container_of(work, struct orinoco_private, reset_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) /* When the hardware becomes available again, whatever * detects that is responsible for re-initializing * it. So no need for anything further */ return; netif_stop_queue(dev); /* Shut off interrupts. Depending on what state the hardware * is in, this might not work, but we'll try anyway */ hermes_set_irqmask(hw, 0); hermes_write_regn(hw, EVACK, 0xffff); priv->hw_unavailable++; priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ netif_carrier_off(dev); orinoco_unlock(priv, &flags); /* Scanning support: Cleanup of driver struct */ orinoco_clear_scan_results(priv, 0); priv->scan_inprogress = 0; if (priv->hard_reset) { err = (*priv->hard_reset)(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d " "performing hard reset\n", dev->name, err); goto disable; } } if (priv->do_fw_download) { err = orinoco_download(priv); if (err) priv->do_fw_download = 0; } err = orinoco_reinit_firmware(dev); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", dev->name, err); goto disable; } spin_lock_irq(&priv->lock); /* This has to be called from user context */ priv->hw_unavailable--; /* priv->open or priv->hw_unavailable might have changed while * we dropped the lock */ if (priv->open && (! priv->hw_unavailable)) { err = __orinoco_up(dev); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", dev->name, err); } else dev->trans_start = jiffies; } spin_unlock_irq(&priv->lock); return; disable: hermes_set_irqmask(hw, 0); netif_device_detach(dev); printk(KERN_ERR "%s: Device has been disabled!\n", dev->name); } /********************************************************************/ /* Interrupt handler */ /********************************************************************/ static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) { printk(KERN_DEBUG "%s: TICK\n", dev->name); } static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", dev->name); } irqreturn_t orinoco_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int count = MAX_IRQLOOPS_PER_IRQ; u16 evstat, events; /* These are used to detect a runaway interrupt situation */ /* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, * we panic and shut down the hardware */ static int last_irq_jiffy = 0; /* jiffies value the last time * we were called */ static int loops_this_jiffy = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { /* If hw is unavailable - we don't know if the irq was * for us or not */ return IRQ_HANDLED; } evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; if (! events) { orinoco_unlock(priv, &flags); return IRQ_NONE; } if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; last_irq_jiffy = jiffies; while (events && count--) { if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { printk(KERN_WARNING "%s: IRQ handler is looping too " "much! Resetting.\n", dev->name); /* Disable interrupts for now */ hermes_set_irqmask(hw, 0); schedule_work(&priv->reset_work); break; } /* Check the card hasn't been removed */ if (! hermes_present(hw)) { DEBUG(0, "orinoco_interrupt(): card removed\n"); break; } if (events & HERMES_EV_TICK) __orinoco_ev_tick(dev, hw); if (events & HERMES_EV_WTERR) __orinoco_ev_wterr(dev, hw); if (events & HERMES_EV_INFDROP) __orinoco_ev_infdrop(dev, hw); if (events & HERMES_EV_INFO) __orinoco_ev_info(dev, hw); if (events & HERMES_EV_RX) __orinoco_ev_rx(dev, hw); if (events & HERMES_EV_TXEXC) __orinoco_ev_txexc(dev, hw); if (events & HERMES_EV_TX) __orinoco_ev_tx(dev, hw); if (events & HERMES_EV_ALLOC) __orinoco_ev_alloc(dev, hw); hermes_write_regn(hw, EVACK, evstat); evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; }; orinoco_unlock(priv, &flags); return IRQ_HANDLED; } /********************************************************************/ /* Initialization */ /********************************************************************/ struct comp_id { u16 id, variant, major, minor; } __attribute__ ((packed)); static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) { if (nic_id->id < 0x8000) return FIRMWARE_TYPE_AGERE; else if (nic_id->id == 0x8000 && nic_id->major == 0) return FIRMWARE_TYPE_SYMBOL; else return FIRMWARE_TYPE_INTERSIL; } /* Set priv->firmware type, determine firmware properties */ static int determine_firmware(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err; struct comp_id nic_id, sta_id; unsigned int firmver; char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2))); /* Get the hardware version */ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); if (err) { printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n", dev->name, err); return err; } le16_to_cpus(&nic_id.id); le16_to_cpus(&nic_id.variant); le16_to_cpus(&nic_id.major); le16_to_cpus(&nic_id.minor); printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n", dev->name, nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); priv->firmware_type = determine_firmware_type(&nic_id); /* Get the firmware version */ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); if (err) { printk(KERN_ERR "%s: Cannot read station identity: error %d\n", dev->name, err); return err; } le16_to_cpus(&sta_id.id); le16_to_cpus(&sta_id.variant); le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", dev->name, sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); switch (sta_id.id) { case 0x15: printk(KERN_ERR "%s: Primary firmware is active\n", dev->name); return -ENODEV; case 0x14b: printk(KERN_ERR "%s: Tertiary firmware is active\n", dev->name); return -ENODEV; case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ case 0x21: /* Symbol Spectrum24 Trilogy */ break; default: printk(KERN_NOTICE "%s: Unknown station ID, please report\n", dev->name); break; } /* Default capabilities */ priv->has_sensitivity = 1; priv->has_mwo = 0; priv->has_preamble = 0; priv->has_port3 = 1; priv->has_ibss = 1; priv->has_wep = 0; priv->has_big_wep = 0; priv->has_alt_txcntl = 0; priv->do_fw_download = 0; /* Determine capabilities from the firmware version */ switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; priv->has_ibss = (firmver >= 0x60006); priv->has_wep = (firmver >= 0x40020); priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell Gold cards from the others? */ priv->has_mwo = (firmver >= 0x60000); priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->ibss_port = 1; priv->has_hostscan = (firmver >= 0x8000a); priv->do_fw_download = 1; priv->broken_monitor = (firmver >= 0x80000); priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * Tested CableTron firmware : 4.32 => Anton */ break; case FIRMWARE_TYPE_SYMBOL: /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ /* Intel MAC : 00:02:B3:* */ /* 3Com MAC : 00:50:DA:* */ memset(tmp, 0, sizeof(tmp)); /* Get the Symbol firmware version */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SECONDARYVERSION_SYMBOL, SYMBOL_MAX_VER_LEN, NULL, &tmp); if (err) { printk(KERN_WARNING "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n", dev->name, err); firmver = 0; tmp[0] = '\0'; } else { /* The firmware revision is a string, the format is * something like : "V2.20-01". * Quick and dirty parsing... - Jean II */ firmver = ((tmp[1] - '0') << 16) | ((tmp[3] - '0') << 12) | ((tmp[4] - '0') << 8) | ((tmp[6] - '0') << 4) | (tmp[7] - '0'); tmp[SYMBOL_MAX_VER_LEN] = '\0'; } snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, "Symbol %s", tmp); priv->has_ibss = (firmver >= 0x20000); priv->has_wep = (firmver >= 0x15012); priv->has_big_wep = (firmver >= 0x20000); priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || (firmver >= 0x29000 && firmver < 0x30000) || firmver >= 0x31000; priv->has_preamble = (firmver >= 0x20000); priv->ibss_port = 4; /* Symbol firmware is found on various cards, but * there has been no attempt to check firmware * download on non-spectrum_cs based cards. * * Given that the Agere firmware download works * differently, we should avoid doing a firmware * download with the Symbol algorithm on non-spectrum * cards. * * For now we can identify a spectrum_cs based card * because it has a firmware reset function. */ priv->do_fw_download = (priv->stop_fw != NULL); priv->broken_disableport = (firmver == 0x25013) || (firmver >= 0x30000 && firmver <= 0x31000); priv->has_hostscan = (firmver >= 0x31001) || (firmver >= 0x29057 && firmver < 0x30000); /* Tested with Intel firmware : 0x20015 => Jean II */ /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ break; case FIRMWARE_TYPE_INTERSIL: /* D-Link, Linksys, Adtron, ZoomAir, and many others... * Samsung, Compaq 100/200 and Proxim are slightly * different and less well tested */ /* D-Link MAC : 00:40:05:* */ /* Addtron MAC : 00:90:D1:* */ snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, "Intersil %d.%d.%d", sta_id.major, sta_id.minor, sta_id.variant); firmver = ((unsigned long)sta_id.major << 16) | ((unsigned long)sta_id.minor << 8) | sta_id.variant; priv->has_ibss = (firmver >= 0x000700); /* FIXME */ priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); priv->has_pm = (firmver >= 0x000700); priv->has_hostscan = (firmver >= 0x010301); if (firmver >= 0x000800) priv->ibss_port = 0; else { printk(KERN_NOTICE "%s: Intersil firmware earlier " "than v0.8.x - several features not supported\n", dev->name); priv->ibss_port = 1; } break; } printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name, priv->fw_name); return 0; } static int orinoco_init(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err = 0; struct hermes_idstring nickbuf; u16 reclen; int len; DECLARE_MAC_BUF(mac); /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN; /* Initialize the firmware */ err = hermes_init(hw); if (err != 0) { printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n", dev->name, err); goto out; } err = determine_firmware(dev); if (err != 0) { printk(KERN_ERR "%s: Incompatible firmware, aborting\n", dev->name); goto out; } if (priv->do_fw_download) { err = orinoco_download(priv); if (err) priv->do_fw_download = 0; /* Check firmware version again */ err = determine_firmware(dev); if (err != 0) { printk(KERN_ERR "%s: Incompatible firmware, aborting\n", dev->name); goto out; } } if (priv->has_port3) printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name); if (priv->has_ibss) printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n", dev->name); if (priv->has_wep) { printk(KERN_DEBUG "%s: WEP supported, ", dev->name); if (priv->has_big_wep) printk("104-bit key\n"); else printk("40-bit key\n"); } /* Get the MAC address */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, ETH_ALEN, NULL, dev->dev_addr); if (err) { printk(KERN_WARNING "%s: failed to read MAC address!\n", dev->name); goto out; } printk(KERN_DEBUG "%s: MAC address %s\n", dev->name, print_mac(mac, dev->dev_addr)); /* Get the station name */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, sizeof(nickbuf), &reclen, &nickbuf); if (err) { printk(KERN_ERR "%s: failed to read station name\n", dev->name); goto out; } if (nickbuf.len) len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); else len = min(IW_ESSID_MAX_SIZE, 2 * reclen); memcpy(priv->nick, &nickbuf.val, len); priv->nick[len] = '\0'; printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick); err = orinoco_allocate_fid(dev); if (err) { printk(KERN_ERR "%s: failed to allocate NIC buffer!\n", dev->name); goto out; } /* Get allowed channels */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, &priv->channel_mask); if (err) { printk(KERN_ERR "%s: failed to read channel list!\n", dev->name); goto out; } /* Get initial AP density */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &priv->ap_density); if (err || priv->ap_density < 1 || priv->ap_density > 3) { priv->has_sensitivity = 0; } /* Get initial RTS threshold */ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, &priv->rts_thresh); if (err) { printk(KERN_ERR "%s: failed to read RTS threshold!\n", dev->name); goto out; } /* Get initial fragmentation settings */ if (priv->has_mwo) err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, &priv->mwo_robust); else err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, &priv->frag_thresh); if (err) { printk(KERN_ERR "%s: failed to read fragmentation settings!\n", dev->name); goto out; } /* Power management setup */ if (priv->has_pm) { priv->pm_on = 0; priv->pm_mcast = 1; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, &priv->pm_period); if (err) { printk(KERN_ERR "%s: failed to read power management period!\n", dev->name); goto out; } err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &priv->pm_timeout); if (err) { printk(KERN_ERR "%s: failed to read power management timeout!\n", dev->name); goto out; } } /* Preamble setup */ if (priv->has_preamble) { err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, &priv->preamble); if (err) goto out; } /* Set up the default configuration */ priv->iw_mode = IW_MODE_INFRA; /* By default use IEEE/IBSS ad-hoc mode if we have it */ priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss); set_port_type(priv); priv->channel = 0; /* use firmware default */ priv->promiscuous = 0; priv->wep_on = 0; priv->tx_key = 0; /* Make the hardware available, as long as it hasn't been * removed elsewhere (e.g. by PCMCIA hot unplug) */ spin_lock_irq(&priv->lock); priv->hw_unavailable--; spin_unlock_irq(&priv->lock); printk(KERN_DEBUG "%s: ready\n", dev->name); out: return err; } struct net_device *alloc_orinocodev(int sizeof_card, struct device *device, int (*hard_reset)(struct orinoco_private *), int (*stop_fw)(struct orinoco_private *, int)) { struct net_device *dev; struct orinoco_private *priv; dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); if (! dev) return NULL; priv = netdev_priv(dev); priv->ndev = dev; if (sizeof_card) priv->card = (void *)((unsigned long)priv + sizeof(struct orinoco_private)); else priv->card = NULL; priv->dev = device; if (orinoco_bss_data_allocate(priv)) goto err_out_free; orinoco_bss_data_init(priv); /* Setup / override net_device fields */ dev->init = orinoco_init; dev->hard_start_xmit = orinoco_xmit; dev->tx_timeout = orinoco_tx_timeout; dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->get_stats = orinoco_get_stats; dev->ethtool_ops = &orinoco_ethtool_ops; dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def; #ifdef WIRELESS_SPY priv->wireless_data.spy_data = &priv->spy_data; dev->wireless_data = &priv->wireless_data; #endif dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; /* we use the default eth_mac_addr for setting the MAC addr */ /* Set up default callbacks */ dev->open = orinoco_open; dev->stop = orinoco_stop; priv->hard_reset = hard_reset; priv->stop_fw = stop_fw; spin_lock_init(&priv->lock); priv->open = 0; priv->hw_unavailable = 1; /* orinoco_init() must clear this * before anything else touches the * hardware */ INIT_WORK(&priv->reset_work, orinoco_reset); INIT_WORK(&priv->join_work, orinoco_join_ap); INIT_WORK(&priv->wevent_work, orinoco_send_wevents); netif_carrier_off(dev); priv->last_linkstatus = 0xffff; return dev; err_out_free: free_netdev(dev); return NULL; } void free_orinocodev(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); orinoco_bss_data_free(priv); free_netdev(dev); } /********************************************************************/ /* Wireless extensions */ /********************************************************************/ /* Return : < 0 -> error code ; >= 0 -> length */ static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]) { hermes_t *hw = &priv->hw; int err = 0; struct hermes_idstring essidbuf; char *p = (char *)(&essidbuf.val); int len; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (strlen(priv->desired_essid) > 0) { /* We read the desired SSID from the hardware rather than from priv->desired_essid, just in case the firmware is allowed to change it on us. I'm not sure about this */ /* My guess is that the OWNSSID should always be whatever * we set to the card, whereas CURRENT_SSID is the one that * may change... - Jean II */ u16 rid; *active = 1; rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID; err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } else { *active = 0; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, sizeof(essidbuf), NULL, &essidbuf); if (err) goto fail_unlock; } len = le16_to_cpu(essidbuf.len); BUG_ON(len > IW_ESSID_MAX_SIZE); memset(buf, 0, IW_ESSID_MAX_SIZE); memcpy(buf, p, len); err = len; fail_unlock: orinoco_unlock(priv, &flags); return err; } static long orinoco_hw_get_freq(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; u16 channel; long freq = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); if (err) goto out; /* Intersil firmware 1.3.5 returns 0 when the interface is down */ if (channel == 0) { err = -EBUSY; goto out; } if ( (channel < 1) || (channel > NUM_CHANNELS) ) { printk(KERN_WARNING "%s: Channel out of range (%d)!\n", priv->ndev->name, channel); err = -EBUSY; goto out; } freq = channel_frequency[channel-1] * 100000; out: orinoco_unlock(priv, &flags); if (err > 0) err = -EBUSY; return err ? err : freq; } static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, s32 *rates, int max) { hermes_t *hw = &priv->hw; struct hermes_idstring list; unsigned char *p = (unsigned char *)&list.val; int err = 0; int num; int i; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, sizeof(list), NULL, &list); orinoco_unlock(priv, &flags); if (err) return err; num = le16_to_cpu(list.len); *numrates = num; num = min(num, max); for (i = 0; i < num; i++) { rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ } return 0; } static int orinoco_ioctl_getname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int numrates; int err; err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); if (!err && (numrates > 2)) strcpy(name, "IEEE 802.11b"); else strcpy(name, "IEEE 802.11-DS"); return 0; } static int orinoco_ioctl_setwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* Enable automatic roaming - no sanity checks are needed */ if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { priv->bssid_fixed = 0; memset(priv->desired_bssid, 0, ETH_ALEN); /* "off" means keep existing connection */ if (ap_addr->sa_data[0] == 0) { __orinoco_hw_set_wap(priv); err = 0; } goto out; } if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " "support manual roaming\n", dev->name); err = -EOPNOTSUPP; goto out; } if (priv->iw_mode != IW_MODE_INFRA) { printk(KERN_WARNING "%s: Manual roaming supported only in " "managed mode\n", dev->name); err = -EOPNOTSUPP; goto out; } /* Intersil firmware hangs without Desired ESSID */ if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && strlen(priv->desired_essid) == 0) { printk(KERN_WARNING "%s: Desired ESSID must be set for " "manual roaming\n", dev->name); err = -EOPNOTSUPP; goto out; } /* Finally, enable manual roaming */ priv->bssid_fixed = 1; memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; ap_addr->sa_family = ARPHRD_ETHER; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, ap_addr->sa_data); orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_setmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (priv->iw_mode == *mode) return 0; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; switch (*mode) { case IW_MODE_ADHOC: if (!priv->has_ibss && !priv->has_port3) err = -EOPNOTSUPP; break; case IW_MODE_INFRA: break; case IW_MODE_MONITOR: if (priv->broken_monitor && !force_monitor) { printk(KERN_WARNING "%s: Monitor mode support is " "buggy in this firmware, not enabling\n", dev->name); err = -EOPNOTSUPP; } break; default: err = -EOPNOTSUPP; break; } if (err == -EINPROGRESS) { priv->iw_mode = *mode; set_port_type(priv); } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra) { struct orinoco_private *priv = netdev_priv(dev); *mode = priv->iw_mode; return 0; } static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_request_info *info, struct iw_point *rrq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int err = 0; struct iw_range *range = (struct iw_range *) extra; int numrates; int i, k; rrq->length = sizeof(struct iw_range); memset(range, 0, sizeof(struct iw_range)); range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 14; /* Set available channels/frequencies */ range->num_channels = NUM_CHANNELS; k = 0; for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { range->freq[k].i = i + 1; range->freq[k].m = channel_frequency[i] * 100000; range->freq[k].e = 1; k++; } if (k >= IW_MAX_FREQUENCIES) break; } range->num_frequency = k; range->sensitivity = 3; if (priv->has_wep) { range->max_encoding_tokens = ORINOCO_MAX_KEYS; range->encoding_size[0] = SMALL_KEY_SIZE; range->num_encoding_sizes = 1; if (priv->has_big_wep) { range->encoding_size[1] = LARGE_KEY_SIZE; range->num_encoding_sizes = 2; } } if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){ /* Quality stats meaningless in ad-hoc mode */ } else { range->max_qual.qual = 0x8b - 0x2f; range->max_qual.level = 0x2f - 0x95 - 1; range->max_qual.noise = 0x2f - 0x95 - 1; /* Need to get better values */ range->avg_qual.qual = 0x24; range->avg_qual.level = 0xC2; range->avg_qual.noise = 0x9E; } err = orinoco_hw_get_bitratelist(priv, &numrates, range->bitrate, IW_MAX_BITRATES); if (err) return err; range->num_bitrates = numrates; /* Set an indication of the max TCP throughput in bit/s that we can * expect using this interface. May be use for QoS stuff... * Jean II */ if (numrates > 2) range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ else range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ range->min_rts = 0; range->max_rts = 2347; range->min_frag = 256; range->max_frag = 2346; range->min_pmp = 0; range->max_pmp = 65535000; range->min_pmt = 0; range->max_pmt = 65535 * 1000; /* ??? */ range->pmp_flags = IW_POWER_PERIOD; range->pmt_flags = IW_POWER_TIMEOUT; range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R; range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; range->retry_flags = IW_RETRY_LIMIT; range->r_time_flags = IW_RETRY_LIFETIME; range->min_retry = 0; range->max_retry = 65535; /* ??? */ range->min_r_time = 0; range->max_r_time = 65535 * 1000; /* ??? */ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) range->scan_capa = IW_SCAN_CAPA_ESSID; else range->scan_capa = IW_SCAN_CAPA_NONE; /* Event capability (kernel) */ IW_EVENT_CAPA_SET_KERNEL(range->event_capa); /* Event capability (driver) */ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); return 0; } static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct orinoco_private *priv = netdev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; int setindex = priv->tx_key; int enable = priv->wep_on; int restricted = priv->wep_restrict; u16 xlen = 0; int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (! priv->has_wep) return -EOPNOTSUPP; if (erq->pointer) { /* We actually have a key to set - check its length */ if (erq->length > LARGE_KEY_SIZE) return -E2BIG; if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep ) return -E2BIG; } if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (erq->length > 0) { if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; /* Adjust key length to a supported value */ if (erq->length > SMALL_KEY_SIZE) { xlen = LARGE_KEY_SIZE; } else if (erq->length > 0) { xlen = SMALL_KEY_SIZE; } else xlen = 0; /* Switch on WEP if off */ if ((!enable) && (xlen > 0)) { setindex = index; enable = 1; } } else { /* Important note : if the user do "iwconfig eth0 enc off", * we will arrive there with an index of -1. This is valid * but need to be taken care off... Jean II */ if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { if((index != -1) || (erq->flags == 0)) { err = -EINVAL; goto out; } } else { /* Set the index : Check that the key is valid */ if(priv->keys[index].len == 0) { err = -EINVAL; goto out; } setindex = index; } } if (erq->flags & IW_ENCODE_DISABLED) enable = 0; if (erq->flags & IW_ENCODE_OPEN) restricted = 0; if (erq->flags & IW_ENCODE_RESTRICTED) restricted = 1; if (erq->pointer && erq->length > 0) { priv->keys[index].len = cpu_to_le16(xlen); memset(priv->keys[index].data, 0, sizeof(priv->keys[index].data)); memcpy(priv->keys[index].data, keybuf, erq->length); } priv->tx_key = setindex; /* Try fast key change if connected and only keys are changed */ if (priv->wep_on && enable && (priv->wep_restrict == restricted) && netif_carrier_ok(dev)) { err = __orinoco_hw_setup_wepkeys(priv); /* No need to commit if successful */ goto out; } priv->wep_on = enable; priv->wep_restrict = restricted; out: orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { struct orinoco_private *priv = netdev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; unsigned long flags; if (! priv->has_wep) return -EOPNOTSUPP; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; erq->flags = 0; if (! priv->wep_on) erq->flags |= IW_ENCODE_DISABLED; erq->flags |= index + 1; if (priv->wep_restrict) erq->flags |= IW_ENCODE_RESTRICTED; else erq->flags |= IW_ENCODE_OPEN; xlen = le16_to_cpu(priv->keys[index].len); erq->length = xlen; memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); orinoco_unlock(priv, &flags); return 0; } static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *essidbuf) { struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it * anyway... - Jean II */ /* Hum... Should not use Wireless Extension constant (may change), * should use our own... - Jean II */ if (erq->length > IW_ESSID_MAX_SIZE) return -E2BIG; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); /* If not ANY, get the new ESSID */ if (erq->flags) { memcpy(priv->desired_essid, essidbuf, erq->length); } orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *essidbuf) { struct orinoco_private *priv = netdev_priv(dev); int active; int err = 0; unsigned long flags; if (netif_running(dev)) { err = orinoco_hw_get_essid(priv, &active, essidbuf); if (err < 0) return err; erq->length = err; } else { if (orinoco_lock(priv, &flags) != 0) return -EBUSY; memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); erq->length = strlen(priv->desired_essid); orinoco_unlock(priv, &flags); } erq->flags = 1; return 0; } static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_request_info *info, struct iw_point *nrq, char *nickbuf) { struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) return -E2BIG; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; memset(priv->nick, 0, sizeof(priv->nick)); memcpy(priv->nick, nickbuf, nrq->length); orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_request_info *info, struct iw_point *nrq, char *nickbuf) { struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE); orinoco_unlock(priv, &flags); nrq->length = strlen(priv->nick); return 0; } static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *frq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int chan = -1; unsigned long flags; int err = -EINPROGRESS; /* Call commit handler */ /* In infrastructure mode the AP sets the channel */ if (priv->iw_mode == IW_MODE_INFRA) return -EBUSY; if ( (frq->e == 0) && (frq->m <= 1000) ) { /* Setting by channel number */ chan = frq->m; } else { /* Setting by frequency - search the table */ int mult = 1; int i; for (i = 0; i < (6 - frq->e); i++) mult *= 10; for (i = 0; i < NUM_CHANNELS; i++) if (frq->m == (channel_frequency[i] * mult)) chan = i+1; } if ( (chan < 1) || (chan > NUM_CHANNELS) || ! (priv->channel_mask & (1 << (chan-1)) ) ) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->channel = chan; if (priv->iw_mode == IW_MODE_MONITOR) { /* Fast channel change - no commit if successful */ hermes_t *hw = &priv->hw; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | HERMES_TEST_SET_CHANNEL, chan, NULL); } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *frq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int tmp; /* Locking done in there */ tmp = orinoco_hw_get_freq(priv); if (tmp < 0) { return tmp; } frq->m = tmp; frq->e = 1; return 0; } static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *srq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; u16 val; int err; unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val); orinoco_unlock(priv, &flags); if (err) return err; srq->value = val; srq->fixed = 0; /* auto */ return 0; } static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_request_info *info, struct iw_param *srq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int val = srq->value; unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; if ((val < 1) || (val > 3)) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->ap_density = val; orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int val = rrq->value; unsigned long flags; if (rrq->disabled) val = 2347; if ( (val < 0) || (val > 2347) ) return -EINVAL; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; priv->rts_thresh = val; orinoco_unlock(priv, &flags); return -EINPROGRESS; /* Call commit handler */ } static int orinoco_ioctl_getrts(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); rrq->value = priv->rts_thresh; rrq->disabled = (rrq->value == 2347); rrq->fixed = 1; return 0; } static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (priv->has_mwo) { if (frq->disabled) priv->mwo_robust = 0; else { if (frq->fixed) printk(KERN_WARNING "%s: Fixed fragmentation is " "not supported on this firmware. " "Using MWO robust instead.\n", dev->name); priv->mwo_robust = 1; } } else { if (frq->disabled) priv->frag_thresh = 2346; else { if ( (frq->value < 256) || (frq->value > 2346) ) err = -EINVAL; else priv->frag_thresh = frq->value & ~0x1; /* must be even */ } } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_request_info *info, struct iw_param *frq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; int err; u16 val; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; if (priv->has_mwo) { err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMWOROBUST_AGERE, &val); if (err) val = 0; frq->value = val ? 2347 : 0; frq->disabled = ! val; frq->fixed = 0; } else { err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, &val); if (err) val = 0; frq->value = val; frq->disabled = (val >= 2346); frq->fixed = 1; } orinoco_unlock(priv, &flags); return err; } static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) { struct orinoco_private *priv = netdev_priv(dev); int ratemode = -1; int bitrate; /* 100s of kilobits */ int i; unsigned long flags; /* As the user space doesn't know our highest rate, it uses -1 * to ask us to set the highest rate. Test it using "iwconfig * ethX rate auto" - Jean II */ if (rrq->value == -1) bitrate = 110; else { if (rrq->value % 100000) return -EINVAL; bitrate = rrq->value / 100000; } if ( (bitrate != 10) && (bitrate != 20) && (bitrate != 55) && (bitrate != 110) ) return -EINVAL;