From e61d98d8dad0048619bb138b0ff996422ffae53b Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:35 -0700 Subject: x64, x2apic/intr-remap: Intel vt-d, IOMMU code reorganization code reorganization of the generic Intel vt-d parsing related routines and linux iommu routines specific to Intel vt-d. drivers/pci/dmar.c now contains the generic vt-d parsing related routines drivers/pci/intel_iommu.c contains the iommu routines specific to vt-d Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dma_remapping.h | 155 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/dmar.c | 90 +++++++++++++++++++++++- drivers/pci/intel-iommu.c | 92 +++---------------------- drivers/pci/intel-iommu.h | 163 ++++---------------------------------------- 4 files changed, 264 insertions(+), 236 deletions(-) create mode 100644 drivers/pci/dma_remapping.h (limited to 'drivers') diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h new file mode 100644 index 00000000000..05aac8ef96c --- /dev/null +++ b/drivers/pci/dma_remapping.h @@ -0,0 +1,155 @@ +#ifndef _DMA_REMAPPING_H +#define _DMA_REMAPPING_H + +/* + * We need a fixed PAGE_SIZE of 4K irrespective of + * arch PAGE_SIZE for IOMMU page tables. + */ +#define PAGE_SHIFT_4K (12) +#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K) +#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K) +#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K) + +#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K) +#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) +#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) + + +/* + * 0: Present + * 1-11: Reserved + * 12-63: Context Ptr (12 - (haw-1)) + * 64-127: Reserved + */ +struct root_entry { + u64 val; + u64 rsvd1; +}; +#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry)) +static inline bool root_present(struct root_entry *root) +{ + return (root->val & 1); +} +static inline void set_root_present(struct root_entry *root) +{ + root->val |= 1; +} +static inline void set_root_value(struct root_entry *root, unsigned long value) +{ + root->val |= value & PAGE_MASK_4K; +} + +struct context_entry; +static inline struct context_entry * +get_context_addr_from_root(struct root_entry *root) +{ + return (struct context_entry *) + (root_present(root)?phys_to_virt( + root->val & PAGE_MASK_4K): + NULL); +} + +/* + * low 64 bits: + * 0: present + * 1: fault processing disable + * 2-3: translation type + * 12-63: address space root + * high 64 bits: + * 0-2: address width + * 3-6: aval + * 8-23: domain id + */ +struct context_entry { + u64 lo; + u64 hi; +}; +#define context_present(c) ((c).lo & 1) +#define context_fault_disable(c) (((c).lo >> 1) & 1) +#define context_translation_type(c) (((c).lo >> 2) & 3) +#define context_address_root(c) ((c).lo & PAGE_MASK_4K) +#define context_address_width(c) ((c).hi & 7) +#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) + +#define context_set_present(c) do {(c).lo |= 1;} while (0) +#define context_set_fault_enable(c) \ + do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) +#define context_set_translation_type(c, val) \ + do { \ + (c).lo &= (((u64)-1) << 4) | 3; \ + (c).lo |= ((val) & 3) << 2; \ + } while (0) +#define CONTEXT_TT_MULTI_LEVEL 0 +#define context_set_address_root(c, val) \ + do {(c).lo |= (val) & PAGE_MASK_4K;} while (0) +#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) +#define context_set_domain_id(c, val) \ + do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) +#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) + +/* + * 0: readable + * 1: writable + * 2-6: reserved + * 7: super page + * 8-11: available + * 12-63: Host physcial address + */ +struct dma_pte { + u64 val; +}; +#define dma_clear_pte(p) do {(p).val = 0;} while (0) + +#define DMA_PTE_READ (1) +#define DMA_PTE_WRITE (2) + +#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0) +#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0) +#define dma_set_pte_prot(p, prot) \ + do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) +#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K) +#define dma_set_pte_addr(p, addr) do {\ + (p).val |= ((addr) & PAGE_MASK_4K); } while (0) +#define dma_pte_present(p) (((p).val & 3) != 0) + +struct intel_iommu; + +struct dmar_domain { + int id; /* domain id */ + struct intel_iommu *iommu; /* back pointer to owning iommu */ + + struct list_head devices; /* all devices' list */ + struct iova_domain iovad; /* iova's that belong to this domain */ + + struct dma_pte *pgd; /* virtual address */ + spinlock_t mapping_lock; /* page table lock */ + int gaw; /* max guest address width */ + + /* adjusted guest address width, 0 is level 2 30-bit */ + int agaw; + +#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 + int flags; +}; + +/* PCI domain-device relationship */ +struct device_domain_info { + struct list_head link; /* link to domain siblings */ + struct list_head global; /* link to global list */ + u8 bus; /* PCI bus numer */ + u8 devfn; /* PCI devfn number */ + struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ + struct dmar_domain *domain; /* pointer to domain */ +}; + +extern int init_dmars(void); +extern void free_dmar_iommu(struct intel_iommu *iommu); + +#ifndef CONFIG_DMAR_GFX_WA +static inline void iommu_prepare_gfx_mapping(void) +{ + return; +} +#endif /* !CONFIG_DMAR_GFX_WA */ + +#endif diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index f941f609dbf..c00e387f5b7 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -19,9 +19,11 @@ * Author: Shaohua Li * Author: Anil S Keshavamurthy * - * This file implements early detection/parsing of DMA Remapping Devices + * This file implements early detection/parsing of Remapping Devices * reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI * tables. + * + * These routines are used by both DMA-remapping and Interrupt-remapping */ #include @@ -300,6 +302,37 @@ parse_dmar_table(void) return ret; } +int dmar_pci_device_match(struct pci_dev *devices[], int cnt, + struct pci_dev *dev) +{ + int index; + + while (dev) { + for (index = 0; index < cnt; index++) + if (dev == devices[index]) + return 1; + + /* Check our parent */ + dev = dev->bus->self; + } + + return 0; +} + +struct dmar_drhd_unit * +dmar_find_matched_drhd_unit(struct pci_dev *dev) +{ + struct dmar_drhd_unit *drhd = NULL; + + list_for_each_entry(drhd, &dmar_drhd_units, list) { + if (drhd->include_all || dmar_pci_device_match(drhd->devices, + drhd->devices_cnt, dev)) + return drhd; + } + + return NULL; +} + int __init dmar_table_init(void) { @@ -343,3 +376,58 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } + +struct intel_iommu *alloc_iommu(struct intel_iommu *iommu, + struct dmar_drhd_unit *drhd) +{ + int map_size; + u32 ver; + + iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K); + if (!iommu->reg) { + printk(KERN_ERR "IOMMU: can't map the region\n"); + goto error; + } + iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); + iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + + /* the registers might be more than one page */ + map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), + cap_max_fault_reg_offset(iommu->cap)); + map_size = PAGE_ALIGN_4K(map_size); + if (map_size > PAGE_SIZE_4K) { + iounmap(iommu->reg); + iommu->reg = ioremap(drhd->reg_base_addr, map_size); + if (!iommu->reg) { + printk(KERN_ERR "IOMMU: can't map the region\n"); + goto error; + } + } + + ver = readl(iommu->reg + DMAR_VER_REG); + pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", + drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), + iommu->cap, iommu->ecap); + + spin_lock_init(&iommu->register_lock); + + drhd->iommu = iommu; + return iommu; +error: + kfree(iommu); + return NULL; +} + +void free_iommu(struct intel_iommu *iommu) +{ + if (!iommu) + return; + +#ifdef CONFIG_DMAR + free_dmar_iommu(iommu); +#endif + + if (iommu->reg) + iounmap(iommu->reg); + kfree(iommu); +} diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index bb0642318a9..1c0270d3e2e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -990,6 +990,8 @@ static int iommu_init_domains(struct intel_iommu *iommu) return -ENOMEM; } + spin_lock_init(&iommu->lock); + /* * if Caching mode is set, then invalid translations are tagged * with domainid 0. Hence we need to pre-allocate it. @@ -998,62 +1000,15 @@ static int iommu_init_domains(struct intel_iommu *iommu) set_bit(0, iommu->domain_ids); return 0; } -static struct intel_iommu *alloc_iommu(struct intel_iommu *iommu, - struct dmar_drhd_unit *drhd) -{ - int ret; - int map_size; - u32 ver; - - iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K); - if (!iommu->reg) { - printk(KERN_ERR "IOMMU: can't map the region\n"); - goto error; - } - iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); - iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); - - /* the registers might be more than one page */ - map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), - cap_max_fault_reg_offset(iommu->cap)); - map_size = PAGE_ALIGN_4K(map_size); - if (map_size > PAGE_SIZE_4K) { - iounmap(iommu->reg); - iommu->reg = ioremap(drhd->reg_base_addr, map_size); - if (!iommu->reg) { - printk(KERN_ERR "IOMMU: can't map the region\n"); - goto error; - } - } - ver = readl(iommu->reg + DMAR_VER_REG); - pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", - drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), - iommu->cap, iommu->ecap); - ret = iommu_init_domains(iommu); - if (ret) - goto error_unmap; - spin_lock_init(&iommu->lock); - spin_lock_init(&iommu->register_lock); - - drhd->iommu = iommu; - return iommu; -error_unmap: - iounmap(iommu->reg); -error: - kfree(iommu); - return NULL; -} static void domain_exit(struct dmar_domain *domain); -static void free_iommu(struct intel_iommu *iommu) + +void free_dmar_iommu(struct intel_iommu *iommu) { struct dmar_domain *domain; int i; - if (!iommu) - return; - i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap)); for (; i < cap_ndoms(iommu->cap); ) { domain = iommu->domains[i]; @@ -1078,10 +1033,6 @@ static void free_iommu(struct intel_iommu *iommu) /* free context mapping */ free_context_table(iommu); - - if (iommu->reg) - iounmap(iommu->reg); - kfree(iommu); } static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) @@ -1426,37 +1377,6 @@ find_domain(struct pci_dev *pdev) return NULL; } -static int dmar_pci_device_match(struct pci_dev *devices[], int cnt, - struct pci_dev *dev) -{ - int index; - - while (dev) { - for (index = 0; index < cnt; index++) - if (dev == devices[index]) - return 1; - - /* Check our parent */ - dev = dev->bus->self; - } - - return 0; -} - -static struct dmar_drhd_unit * -dmar_find_matched_drhd_unit(struct pci_dev *dev) -{ - struct dmar_drhd_unit *drhd = NULL; - - list_for_each_entry(drhd, &dmar_drhd_units, list) { - if (drhd->include_all || dmar_pci_device_match(drhd->devices, - drhd->devices_cnt, dev)) - return drhd; - } - - return NULL; -} - /* domain is initialized */ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) { @@ -1764,6 +1684,10 @@ int __init init_dmars(void) goto error; } + ret = iommu_init_domains(iommu); + if (ret) + goto error; + /* * TBD: * we could share the same root & context tables diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index afc0ad96122..9e5e98c76c0 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -27,19 +27,7 @@ #include #include "iova.h" #include - -/* - * We need a fixed PAGE_SIZE of 4K irrespective of - * arch PAGE_SIZE for IOMMU page tables. - */ -#define PAGE_SHIFT_4K (12) -#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K) -#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K) -#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K) - -#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K) -#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK) -#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK) +#include "dma_remapping.h" /* * Intel IOMMU register specification per version 1.0 public spec. @@ -187,158 +175,31 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define dma_frcd_source_id(c) (c & 0xffff) #define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */ -/* - * 0: Present - * 1-11: Reserved - * 12-63: Context Ptr (12 - (haw-1)) - * 64-127: Reserved - */ -struct root_entry { - u64 val; - u64 rsvd1; -}; -#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry)) -static inline bool root_present(struct root_entry *root) -{ - return (root->val & 1); -} -static inline void set_root_present(struct root_entry *root) -{ - root->val |= 1; -} -static inline void set_root_value(struct root_entry *root, unsigned long value) -{ - root->val |= value & PAGE_MASK_4K; -} - -struct context_entry; -static inline struct context_entry * -get_context_addr_from_root(struct root_entry *root) -{ - return (struct context_entry *) - (root_present(root)?phys_to_virt( - root->val & PAGE_MASK_4K): - NULL); -} - -/* - * low 64 bits: - * 0: present - * 1: fault processing disable - * 2-3: translation type - * 12-63: address space root - * high 64 bits: - * 0-2: address width - * 3-6: aval - * 8-23: domain id - */ -struct context_entry { - u64 lo; - u64 hi; -}; -#define context_present(c) ((c).lo & 1) -#define context_fault_disable(c) (((c).lo >> 1) & 1) -#define context_translation_type(c) (((c).lo >> 2) & 3) -#define context_address_root(c) ((c).lo & PAGE_MASK_4K) -#define context_address_width(c) ((c).hi & 7) -#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1)) - -#define context_set_present(c) do {(c).lo |= 1;} while (0) -#define context_set_fault_enable(c) \ - do {(c).lo &= (((u64)-1) << 2) | 1;} while (0) -#define context_set_translation_type(c, val) \ - do { \ - (c).lo &= (((u64)-1) << 4) | 3; \ - (c).lo |= ((val) & 3) << 2; \ - } while (0) -#define CONTEXT_TT_MULTI_LEVEL 0 -#define context_set_address_root(c, val) \ - do {(c).lo |= (val) & PAGE_MASK_4K;} while (0) -#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0) -#define context_set_domain_id(c, val) \ - do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0) -#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0) - -/* - * 0: readable - * 1: writable - * 2-6: reserved - * 7: super page - * 8-11: available - * 12-63: Host physcial address - */ -struct dma_pte { - u64 val; -}; -#define dma_clear_pte(p) do {(p).val = 0;} while (0) - -#define DMA_PTE_READ (1) -#define DMA_PTE_WRITE (2) - -#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0) -#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0) -#define dma_set_pte_prot(p, prot) \ - do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0) -#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K) -#define dma_set_pte_addr(p, addr) do {\ - (p).val |= ((addr) & PAGE_MASK_4K); } while (0) -#define dma_pte_present(p) (((p).val & 3) != 0) - -struct intel_iommu; - -struct dmar_domain { - int id; /* domain id */ - struct intel_iommu *iommu; /* back pointer to owning iommu */ - - struct list_head devices; /* all devices' list */ - struct iova_domain iovad; /* iova's that belong to this domain */ - - struct dma_pte *pgd; /* virtual address */ - spinlock_t mapping_lock; /* page table lock */ - int gaw; /* max guest address width */ - - /* adjusted guest address width, 0 is level 2 30-bit */ - int agaw; - -#define DOMAIN_FLAG_MULTIPLE_DEVICES 1 - int flags; -}; - -/* PCI domain-device relationship */ -struct device_domain_info { - struct list_head link; /* link to domain siblings */ - struct list_head global; /* link to global list */ - u8 bus; /* PCI bus numer */ - u8 devfn; /* PCI devfn number */ - struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ - struct dmar_domain *domain; /* pointer to domain */ -}; - -extern int init_dmars(void); - struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; u64 ecap; - unsigned long *domain_ids; /* bitmap of domains */ - struct dmar_domain **domains; /* ptr to domains */ int seg; u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */ - spinlock_t lock; /* protect context, domain ids */ spinlock_t register_lock; /* protect register handling */ + +#ifdef CONFIG_DMAR + unsigned long *domain_ids; /* bitmap of domains */ + struct dmar_domain **domains; /* ptr to domains */ + spinlock_t lock; /* protect context, domain ids */ struct root_entry *root_entry; /* virtual address */ unsigned int irq; unsigned char name[7]; /* Device Name */ struct msi_msg saved_msg; struct sys_device sysdev; +#endif }; -#ifndef CONFIG_DMAR_GFX_WA -static inline void iommu_prepare_gfx_mapping(void) -{ - return; -} -#endif /* !CONFIG_DMAR_GFX_WA */ +extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); + +extern struct intel_iommu *alloc_iommu(struct intel_iommu *iommu, + struct dmar_drhd_unit *drhd); +extern void free_iommu(struct intel_iommu *iommu); #endif -- cgit v1.2.3 From c42d9f32443397aed2d37d37df161392e6a5862f Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:36 -0700 Subject: x64, x2apic/intr-remap: fix the need for sequential array allocation of iommus Clean up the intel-iommu code related to deferred iommu flush logic. There is no need to allocate all the iommu's as a sequential array. This will be used later in the interrupt-remapping patch series to allocate iommu much early and individually for each device remapping hardware unit. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 11 +++++++++-- drivers/pci/intel-iommu.c | 24 +++++++----------------- drivers/pci/intel-iommu.h | 4 ++-- 3 files changed, 18 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index c00e387f5b7..1a59423a8ed 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -377,11 +377,18 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } -struct intel_iommu *alloc_iommu(struct intel_iommu *iommu, - struct dmar_drhd_unit *drhd) +struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) { + struct intel_iommu *iommu; int map_size; u32 ver; + static int iommu_allocated = 0; + + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + if (!iommu) + return NULL; + + iommu->seq_id = iommu_allocated++; iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K); if (!iommu->reg) { diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 1c0270d3e2e..4d59a6a1f4d 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -58,8 +58,6 @@ static void flush_unmaps_timeout(unsigned long data); DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0); -static struct intel_iommu *g_iommus; - #define HIGH_WATER_MARK 250 struct deferred_flush_tables { int next; @@ -1649,8 +1647,6 @@ int __init init_dmars(void) * endfor */ for_each_drhd_unit(drhd) { - if (drhd->ignored) - continue; g_num_of_iommus++; /* * lock not needed as this is only incremented in the single @@ -1659,26 +1655,17 @@ int __init init_dmars(void) */ } - g_iommus = kzalloc(g_num_of_iommus * sizeof(*iommu), GFP_KERNEL); - if (!g_iommus) { - ret = -ENOMEM; - goto error; - } - deferred_flush = kzalloc(g_num_of_iommus * sizeof(struct deferred_flush_tables), GFP_KERNEL); if (!deferred_flush) { - kfree(g_iommus); ret = -ENOMEM; goto error; } - i = 0; for_each_drhd_unit(drhd) { if (drhd->ignored) continue; - iommu = alloc_iommu(&g_iommus[i], drhd); - i++; + iommu = alloc_iommu(drhd); if (!iommu) { ret = -ENOMEM; goto error; @@ -1770,7 +1757,6 @@ error: iommu = drhd->iommu; free_iommu(iommu); } - kfree(g_iommus); return ret; } @@ -1927,7 +1913,10 @@ static void flush_unmaps(void) /* just flush them all */ for (i = 0; i < g_num_of_iommus; i++) { if (deferred_flush[i].next) { - iommu_flush_iotlb_global(&g_iommus[i], 0); + struct intel_iommu *iommu = + deferred_flush[i].domain[0]->iommu; + + iommu_flush_iotlb_global(iommu, 0); for (j = 0; j < deferred_flush[i].next; j++) { __free_iova(&deferred_flush[i].domain[j]->iovad, deferred_flush[i].iova[j]); @@ -1957,7 +1946,8 @@ static void add_unmap(struct dmar_domain *dom, struct iova *iova) if (list_size == HIGH_WATER_MARK) flush_unmaps(); - iommu_id = dom->iommu - g_iommus; + iommu_id = dom->iommu->seq_id; + next = deferred_flush[iommu_id].next; deferred_flush[iommu_id].domain[next] = dom; deferred_flush[iommu_id].iova[next] = iova; diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 9e5e98c76c0..75c63f65b3f 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -182,6 +182,7 @@ struct intel_iommu { int seg; u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */ spinlock_t register_lock; /* protect register handling */ + int seq_id; /* sequence id of the iommu */ #ifdef CONFIG_DMAR unsigned long *domain_ids; /* bitmap of domains */ @@ -198,8 +199,7 @@ struct intel_iommu { extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); -extern struct intel_iommu *alloc_iommu(struct intel_iommu *iommu, - struct dmar_drhd_unit *drhd); +extern struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd); extern void free_iommu(struct intel_iommu *iommu); #endif -- cgit v1.2.3 From 1886e8a90a580f3ad343f2065c84c1b9e1dac9ef Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:37 -0700 Subject: x64, x2apic/intr-remap: code re-structuring, to be used by both DMA and Interrupt remapping Allocate the iommu during the parse of DMA remapping hardware definition structures. And also, introduce routines for device scope initialization which will be explicitly called during dma-remapping initialization. These will be used for enabling interrupt remapping separately from the existing DMA-remapping enabling sequence. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 89 ++++++++++++++++++++++++++++++++++++++--------- drivers/pci/intel-iommu.c | 10 +++--- drivers/pci/intel-iommu.h | 2 +- 3 files changed, 79 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 1a59423a8ed..158bc5bfcf7 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -174,19 +174,37 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) struct acpi_dmar_hardware_unit *drhd; struct dmar_drhd_unit *dmaru; int ret = 0; - static int include_all; dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); if (!dmaru) return -ENOMEM; + dmaru->hdr = header; drhd = (struct acpi_dmar_hardware_unit *)header; dmaru->reg_base_addr = drhd->address; dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ + ret = alloc_iommu(dmaru); + if (ret) { + kfree(dmaru); + return ret; + } + dmar_register_drhd_unit(dmaru); + return 0; +} + +static int __init +dmar_parse_dev(struct dmar_drhd_unit *dmaru) +{ + struct acpi_dmar_hardware_unit *drhd; + static int include_all; + int ret; + + drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr; + if (!dmaru->include_all) ret = dmar_parse_dev_scope((void *)(drhd + 1), - ((void *)drhd) + header->length, + ((void *)drhd) + drhd->header.length, &dmaru->devices_cnt, &dmaru->devices, drhd->segment); else { @@ -199,10 +217,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) include_all = 1; } - if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) + if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) { + list_del(&dmaru->list); kfree(dmaru); - else - dmar_register_drhd_unit(dmaru); + } return ret; } @@ -211,23 +229,35 @@ dmar_parse_one_rmrr(struct acpi_dmar_header *header) { struct acpi_dmar_reserved_memory *rmrr; struct dmar_rmrr_unit *rmrru; - int ret = 0; rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL); if (!rmrru) return -ENOMEM; + rmrru->hdr = header; rmrr = (struct acpi_dmar_reserved_memory *)header; rmrru->base_address = rmrr->base_address; rmrru->end_address = rmrr->end_address; + + dmar_register_rmrr_unit(rmrru); + return 0; +} + +static int __init +rmrr_parse_dev(struct dmar_rmrr_unit *rmrru) +{ + struct acpi_dmar_reserved_memory *rmrr; + int ret; + + rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr; ret = dmar_parse_dev_scope((void *)(rmrr + 1), - ((void *)rmrr) + header->length, + ((void *)rmrr) + rmrr->header.length, &rmrru->devices_cnt, &rmrru->devices, rmrr->segment); - if (ret || (rmrru->devices_cnt == 0)) + if (ret || (rmrru->devices_cnt == 0)) { + list_del(&rmrru->list); kfree(rmrru); - else - dmar_register_rmrr_unit(rmrru); + } return ret; } @@ -333,15 +363,42 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) return NULL; } +int __init dmar_dev_scope_init(void) +{ + struct dmar_drhd_unit *drhd; + struct dmar_rmrr_unit *rmrr; + int ret = -ENODEV; + + for_each_drhd_unit(drhd) { + ret = dmar_parse_dev(drhd); + if (ret) + return ret; + } + + for_each_rmrr_units(rmrr) { + ret = rmrr_parse_dev(rmrr); + if (ret) + return ret; + } + + return ret; +} + int __init dmar_table_init(void) { - + static int dmar_table_initialized; int ret; + if (dmar_table_initialized) + return 0; + + dmar_table_initialized = 1; + ret = parse_dmar_table(); if (ret) { - printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); + if (ret != -ENODEV) + printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); return ret; } @@ -377,7 +434,7 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } -struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) +int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; int map_size; @@ -386,7 +443,7 @@ struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) - return NULL; + return -ENOMEM; iommu->seq_id = iommu_allocated++; @@ -419,10 +476,10 @@ struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd) spin_lock_init(&iommu->register_lock); drhd->iommu = iommu; - return iommu; + return 0; error: kfree(iommu); - return NULL; + return -1; } void free_iommu(struct intel_iommu *iommu) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4d59a6a1f4d..218a1f357b4 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1665,11 +1665,8 @@ int __init init_dmars(void) for_each_drhd_unit(drhd) { if (drhd->ignored) continue; - iommu = alloc_iommu(drhd); - if (!iommu) { - ret = -ENOMEM; - goto error; - } + + iommu = drhd->iommu; ret = iommu_init_domains(iommu); if (ret) @@ -2324,6 +2321,9 @@ int __init intel_iommu_init(void) if (dmar_table_init()) return -ENODEV; + if (dmar_dev_scope_init()) + return -ENODEV; + iommu_init_mempool(); dmar_init_reserved_ranges(); diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 75c63f65b3f..371e3b9caf3 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -199,7 +199,7 @@ struct intel_iommu { extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); -extern struct intel_iommu *alloc_iommu(struct dmar_drhd_unit *drhd); +extern int alloc_iommu(struct dmar_drhd_unit *drhd); extern void free_iommu(struct intel_iommu *iommu); #endif -- cgit v1.2.3 From aaa9d1dd63bf89b62f4ea9f46de376ab1a3fbc6c Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:38 -0700 Subject: x64, x2apic/intr-remap: use CONFIG_DMAR for DMA-remapping specific code DMA remapping specific code covered with CONFIG_DMAR in the generic code which will also be used later for enabling Interrupt-remapping. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 158bc5bfcf7..5c99f997398 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -39,7 +39,6 @@ * these units are not supported by the architecture. */ LIST_HEAD(dmar_drhd_units); -LIST_HEAD(dmar_rmrr_units); static struct acpi_table_header * __initdata dmar_tbl; @@ -55,11 +54,6 @@ static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) list_add(&drhd->list, &dmar_drhd_units); } -static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr) -{ - list_add(&rmrr->list, &dmar_rmrr_units); -} - static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, struct pci_dev **dev, u16 segment) { @@ -224,6 +218,15 @@ dmar_parse_dev(struct dmar_drhd_unit *dmaru) return ret; } +#ifdef CONFIG_DMAR +LIST_HEAD(dmar_rmrr_units); + +static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr) +{ + list_add(&rmrr->list, &dmar_rmrr_units); +} + + static int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header) { @@ -260,6 +263,7 @@ rmrr_parse_dev(struct dmar_rmrr_unit *rmrru) } return ret; } +#endif static void __init dmar_table_print_dmar_entry(struct acpi_dmar_header *header) @@ -284,6 +288,7 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) } } + /** * parse_dmar_table - parses the DMA reporting table */ @@ -316,7 +321,9 @@ parse_dmar_table(void) ret = dmar_parse_one_drhd(entry_header); break; case ACPI_DMAR_TYPE_RESERVED_MEMORY: +#ifdef CONFIG_DMAR ret = dmar_parse_one_rmrr(entry_header); +#endif break; default: printk(KERN_WARNING PREFIX @@ -366,7 +373,6 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) int __init dmar_dev_scope_init(void) { struct dmar_drhd_unit *drhd; - struct dmar_rmrr_unit *rmrr; int ret = -ENODEV; for_each_drhd_unit(drhd) { @@ -375,11 +381,16 @@ int __init dmar_dev_scope_init(void) return ret; } - for_each_rmrr_units(rmrr) { - ret = rmrr_parse_dev(rmrr); - if (ret) - return ret; +#ifdef CONFIG_DMAR + { + struct dmar_rmrr_unit *rmrr; + for_each_rmrr_units(rmrr) { + ret = rmrr_parse_dev(rmrr); + if (ret) + return ret; + } } +#endif return ret; } @@ -407,10 +418,12 @@ int __init dmar_table_init(void) return -ENODEV; } +#ifdef CONFIG_DMAR if (list_empty(&dmar_rmrr_units)) { printk(KERN_INFO PREFIX "No RMRR found\n"); return -ENODEV; } +#endif return 0; } -- cgit v1.2.3 From 2d6b5f85bb4ca919d8ab0f30311309b53fb93bc3 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:39 -0700 Subject: x64, x2apic/intr-remap: Fix the need for RMRR in the DMA-remapping detection Presence of RMRR structures is not compulsory for enabling DMA-remapping. Signed-off-by: Suresh Siddha Signed-off-by: Yong Y Wang Cc: Yong Y Wang Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 5c99f997398..903f1701edf 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -419,10 +419,8 @@ int __init dmar_table_init(void) } #ifdef CONFIG_DMAR - if (list_empty(&dmar_rmrr_units)) { + if (list_empty(&dmar_rmrr_units)) printk(KERN_INFO PREFIX "No RMRR found\n"); - return -ENODEV; - } #endif return 0; -- cgit v1.2.3 From ad3ad3f6a2caebf56869b83b69e23eb9fa5e0ab6 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:40 -0700 Subject: x64, x2apic/intr-remap: parse ioapic scope under vt-d structures Parse the vt-d device scope structures to find the mapping between IO-APICs and the interrupt remapping hardware units. This will be used later for enabling Interrupt-remapping for IOAPIC devices. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/Makefile | 2 ++ drivers/pci/dmar.c | 3 ++ drivers/pci/intel-iommu.h | 2 ++ drivers/pci/intr_remapping.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/intr_remapping.h | 6 ++++ 5 files changed, 83 insertions(+) create mode 100644 drivers/pci/intr_remapping.c create mode 100644 drivers/pci/intr_remapping.h (limited to 'drivers') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4d1ce2e7361..1c409c7b0d5 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_HT_IRQ) += htirq.o # Build Intel IOMMU support obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o +obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o + # # Some architectures use the generic PCI setup functions # diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 903f1701edf..127764cfbe2 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -423,6 +423,9 @@ int __init dmar_table_init(void) printk(KERN_INFO PREFIX "No RMRR found\n"); #endif +#ifdef CONFIG_INTR_REMAP + parse_ioapics_under_ir(); +#endif return 0; } diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 371e3b9caf3..eb167e39b46 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -114,6 +114,8 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define ecap_max_iotlb_offset(e) \ (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) #define ecap_coherent(e) ((e) & 0x1) +#define ecap_eim_support(e) ((e >> 4) & 0x1) +#define ecap_ir_support(e) ((e >> 3) & 0x1) /* IOTLB_REG */ diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c new file mode 100644 index 00000000000..a80b87921c6 --- /dev/null +++ b/drivers/pci/intr_remapping.c @@ -0,0 +1,70 @@ +#include +#include +#include "intel-iommu.h" +#include "intr_remapping.h" + +static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; +static int ir_ioapic_num; + +static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, + struct intel_iommu *iommu) +{ + struct acpi_dmar_hardware_unit *drhd; + struct acpi_dmar_device_scope *scope; + void *start, *end; + + drhd = (struct acpi_dmar_hardware_unit *)header; + + start = (void *)(drhd + 1); + end = ((void *)drhd) + header->length; + + while (start < end) { + scope = start; + if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) { + if (ir_ioapic_num == MAX_IO_APICS) { + printk(KERN_WARNING "Exceeded Max IO APICS\n"); + return -1; + } + + printk(KERN_INFO "IOAPIC id %d under DRHD base" + " 0x%Lx\n", scope->enumeration_id, + drhd->address); + + ir_ioapic[ir_ioapic_num].iommu = iommu; + ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; + ir_ioapic_num++; + } + start += scope->length; + } + + return 0; +} + +/* + * Finds the assocaition between IOAPIC's and its Interrupt-remapping + * hardware unit. + */ +int __init parse_ioapics_under_ir(void) +{ + struct dmar_drhd_unit *drhd; + int ir_supported = 0; + + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (ecap_ir_support(iommu->ecap)) { + if (ir_parse_ioapic_scope(drhd->hdr, iommu)) + return -1; + + ir_supported = 1; + } + } + + if (ir_supported && ir_ioapic_num != nr_ioapics) { + printk(KERN_WARNING + "Not all IO-APIC's listed under remapping hardware\n"); + return -1; + } + + return ir_supported; +} diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h new file mode 100644 index 00000000000..c4a40b2f33f --- /dev/null +++ b/drivers/pci/intr_remapping.h @@ -0,0 +1,6 @@ +#include "intel-iommu.h" + +struct ioapic_scope { + struct intel_iommu *iommu; + unsigned int id; +}; -- cgit v1.2.3 From cf1337f0447e5be8e66daa944f0ea3bcac2b6179 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:41 -0700 Subject: x64, x2apic/intr-remap: move IOMMU_WAIT_OP() macro to intel-iommu.h move IOMMU_WAIT_OP() macro to header file. This will be used by both DMA-remapping and Intr-remapping. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/intel-iommu.c | 15 --------------- drivers/pci/intel-iommu.h | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 218a1f357b4..fb701d9dd8c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -49,8 +49,6 @@ #define DEFAULT_DOMAIN_ADDRESS_WIDTH 48 -#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */ - #define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) @@ -486,19 +484,6 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu) return 0; } -#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \ -{\ - cycles_t start_time = get_cycles();\ - while (1) {\ - sts = op (iommu->reg + offset);\ - if (cond)\ - break;\ - if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\ - panic("DMAR hardware is malfunctioning\n");\ - cpu_relax();\ - }\ -} - static void iommu_set_root_entry(struct intel_iommu *iommu) { void *addr; diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index eb167e39b46..3a650e8cba3 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -177,6 +177,21 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define dma_frcd_source_id(c) (c & 0xffff) #define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */ +#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */ + +#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \ +{\ + cycles_t start_time = get_cycles();\ + while (1) {\ + sts = op (iommu->reg + offset);\ + if (cond)\ + break;\ + if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\ + panic("DMAR hardware is malfunctioning\n");\ + cpu_relax();\ + }\ +} + struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; -- cgit v1.2.3 From fe962e90cb17a8426e144dee970e77ed789d98ee Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:42 -0700 Subject: x64, x2apic/intr-remap: Queued invalidation infrastructure (part of VT-d) Queued invalidation (part of Intel Virtualization Technology for Directed I/O architecture) infrastructure. This will be used for invalidating the interrupt entry cache in the case of Interrupt-remapping and IOTLB invalidation in the case of DMA-remapping. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/intel-iommu.c | 7 --- drivers/pci/intel-iommu.h | 61 +++++++++++++++++++ 3 files changed, 211 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 127764cfbe2..aba151ca6d2 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -28,6 +28,7 @@ #include #include +#include #include "iova.h" #include "intel-iommu.h" @@ -509,3 +510,152 @@ void free_iommu(struct intel_iommu *iommu) iounmap(iommu->reg); kfree(iommu); } + +/* + * Reclaim all the submitted descriptors which have completed its work. + */ +static inline void reclaim_free_desc(struct q_inval *qi) +{ + while (qi->desc_status[qi->free_tail] == QI_DONE) { + qi->desc_status[qi->free_tail] = QI_FREE; + qi->free_tail = (qi->free_tail + 1) % QI_LENGTH; + qi->free_cnt++; + } +} + +/* + * Submit the queued invalidation descriptor to the remapping + * hardware unit and wait for its completion. + */ +void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) +{ + struct q_inval *qi = iommu->qi; + struct qi_desc *hw, wait_desc; + int wait_index, index; + unsigned long flags; + + if (!qi) + return; + + hw = qi->desc; + + spin_lock(&qi->q_lock); + while (qi->free_cnt < 3) { + spin_unlock(&qi->q_lock); + cpu_relax(); + spin_lock(&qi->q_lock); + } + + index = qi->free_head; + wait_index = (index + 1) % QI_LENGTH; + + qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE; + + hw[index] = *desc; + + wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE; + wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]); + + hw[wait_index] = wait_desc; + + __iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc)); + __iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc)); + + qi->free_head = (qi->free_head + 2) % QI_LENGTH; + qi->free_cnt -= 2; + + spin_lock_irqsave(&iommu->register_lock, flags); + /* + * update the HW tail register indicating the presence of + * new descriptors. + */ + writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + while (qi->desc_status[wait_index] != QI_DONE) { + spin_unlock(&qi->q_lock); + cpu_relax(); + spin_lock(&qi->q_lock); + } + + qi->desc_status[index] = QI_DONE; + + reclaim_free_desc(qi); + spin_unlock(&qi->q_lock); +} + +/* + * Flush the global interrupt entry cache. + */ +void qi_global_iec(struct intel_iommu *iommu) +{ + struct qi_desc desc; + + desc.low = QI_IEC_TYPE; + desc.high = 0; + + qi_submit_sync(&desc, iommu); +} + +/* + * Enable Queued Invalidation interface. This is a must to support + * interrupt-remapping. Also used by DMA-remapping, which replaces + * register based IOTLB invalidation. + */ +int dmar_enable_qi(struct intel_iommu *iommu) +{ + u32 cmd, sts; + unsigned long flags; + struct q_inval *qi; + + if (!ecap_qis(iommu->ecap)) + return -ENOENT; + + /* + * queued invalidation is already setup and enabled. + */ + if (iommu->qi) + return 0; + + iommu->qi = kmalloc(sizeof(*qi), GFP_KERNEL); + if (!iommu->qi) + return -ENOMEM; + + qi = iommu->qi; + + qi->desc = (void *)(get_zeroed_page(GFP_KERNEL)); + if (!qi->desc) { + kfree(qi); + iommu->qi = 0; + return -ENOMEM; + } + + qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_KERNEL); + if (!qi->desc_status) { + free_page((unsigned long) qi->desc); + kfree(qi); + iommu->qi = 0; + return -ENOMEM; + } + + qi->free_head = qi->free_tail = 0; + qi->free_cnt = QI_LENGTH; + + spin_lock_init(&qi->q_lock); + + spin_lock_irqsave(&iommu->register_lock, flags); + /* write zero to the tail reg */ + writel(0, iommu->reg + DMAR_IQT_REG); + + dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc)); + + cmd = iommu->gcmd | DMA_GCMD_QIE; + iommu->gcmd |= DMA_GCMD_QIE; + writel(cmd, iommu->reg + DMAR_GCMD_REG); + + /* Make sure hardware complete it */ + IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + return 0; +} diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index fb701d9dd8c..347bf2e4716 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -181,13 +181,6 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } -static inline void __iommu_flush_cache( - struct intel_iommu *iommu, void *addr, int size) -{ - if (!ecap_coherent(iommu->ecap)) - clflush_cache_range(addr, size); -} - /* Gets context entry for a given bus and devfn */ static struct context_entry * device_to_context_entry(struct intel_iommu *iommu, u8 bus, u8 devfn) diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 3a650e8cba3..2983ce89535 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -27,6 +27,7 @@ #include #include "iova.h" #include +#include #include "dma_remapping.h" /* @@ -51,6 +52,10 @@ #define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */ #define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */ #define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */ +#define DMAR_IQH_REG 0x80 /* Invalidation queue head register */ +#define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */ +#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ +#define DMAR_ICS_REG 0x98 /* Invalidation complete status register */ #define OFFSET_STRIDE (9) /* @@ -114,6 +119,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define ecap_max_iotlb_offset(e) \ (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16) #define ecap_coherent(e) ((e) & 0x1) +#define ecap_qis(e) ((e) & 0x2) #define ecap_eim_support(e) ((e >> 4) & 0x1) #define ecap_ir_support(e) ((e >> 3) & 0x1) @@ -131,6 +137,17 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_TLB_IH_NONLEAF (((u64)1) << 6) #define DMA_TLB_MAX_SIZE (0x3f) +/* INVALID_DESC */ +#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3) +#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3) +#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3) +#define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7) +#define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6) +#define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16))) +#define DMA_ID_TLB_IH_NONLEAF (((u64)1) << 6) +#define DMA_ID_TLB_ADDR(addr) (addr) +#define DMA_ID_TLB_ADDR_MASK(mask) (mask) + /* PMEN_REG */ #define DMA_PMEN_EPM (((u32)1)<<31) #define DMA_PMEN_PRS (((u32)1)<<0) @@ -140,6 +157,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_GCMD_SRTP (((u32)1) << 30) #define DMA_GCMD_SFL (((u32)1) << 29) #define DMA_GCMD_EAFL (((u32)1) << 28) +#define DMA_GCMD_QIE (((u32)1) << 26) #define DMA_GCMD_WBF (((u32)1) << 27) /* GSTS_REG */ @@ -147,6 +165,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_GSTS_RTPS (((u32)1) << 30) #define DMA_GSTS_FLS (((u32)1) << 29) #define DMA_GSTS_AFLS (((u32)1) << 28) +#define DMA_GSTS_QIES (((u32)1) << 26) #define DMA_GSTS_WBFS (((u32)1) << 27) /* CCMD_REG */ @@ -192,6 +211,40 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) }\ } +#define QI_LENGTH 256 /* queue length */ + +enum { + QI_FREE, + QI_IN_USE, + QI_DONE +}; + +#define QI_CC_TYPE 0x1 +#define QI_IOTLB_TYPE 0x2 +#define QI_DIOTLB_TYPE 0x3 +#define QI_IEC_TYPE 0x4 +#define QI_IWD_TYPE 0x5 + +#define QI_IEC_SELECTIVE (((u64)1) << 4) +#define QI_IEC_IIDEX(idx) (((u64)(idx & 0xffff) << 32)) +#define QI_IEC_IM(m) (((u64)(m & 0x1f) << 27)) + +#define QI_IWD_STATUS_DATA(d) (((u64)d) << 32) +#define QI_IWD_STATUS_WRITE (((u64)1) << 5) + +struct qi_desc { + u64 low, high; +}; + +struct q_inval { + spinlock_t q_lock; + struct qi_desc *desc; /* invalidation queue */ + int *desc_status; /* desc status */ + int free_head; /* first free entry */ + int free_tail; /* last free entry */ + int free_cnt; +}; + struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; @@ -212,8 +265,16 @@ struct intel_iommu { struct msi_msg saved_msg; struct sys_device sysdev; #endif + struct q_inval *qi; /* Queued invalidation info */ }; +static inline void __iommu_flush_cache( + struct intel_iommu *iommu, void *addr, int size) +{ + if (!ecap_coherent(iommu->ecap)) + clflush_cache_range(addr, size); +} + extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); extern int alloc_iommu(struct dmar_drhd_unit *drhd); -- cgit v1.2.3 From 2ae21010694e56461a63bfc80e960090ce0a5ed9 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:43 -0700 Subject: x64, x2apic/intr-remap: Interrupt remapping infrastructure Interrupt remapping (part of Intel Virtualization Tech for directed I/O) infrastructure. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dma_remapping.h | 2 + drivers/pci/dmar.c | 16 +++++ drivers/pci/intel-iommu.c | 21 +++---- drivers/pci/intel-iommu.h | 24 +++++++- drivers/pci/intr_remapping.c | 137 +++++++++++++++++++++++++++++++++++++++++++ drivers/pci/intr_remapping.h | 2 + 6 files changed, 187 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h index 05aac8ef96c..bff5c65f81d 100644 --- a/drivers/pci/dma_remapping.h +++ b/drivers/pci/dma_remapping.h @@ -145,6 +145,8 @@ struct device_domain_info { extern int init_dmars(void); extern void free_dmar_iommu(struct intel_iommu *iommu); +extern int dmar_disabled; + #ifndef CONFIG_DMAR_GFX_WA static inline void iommu_prepare_gfx_mapping(void) { diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index aba151ca6d2..23a119e6485 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -449,6 +449,22 @@ int __init early_dmar_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } +void __init detect_intel_iommu(void) +{ + int ret; + + ret = early_dmar_detect(); + +#ifdef CONFIG_DMAR + { + if (ret && !no_iommu && !iommu_detected && !swiotlb && + !dmar_disabled) + iommu_detected = 1; + } +#endif +} + + int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 347bf2e4716..ffccf2341b9 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -76,7 +76,7 @@ static long list_size; static void domain_remove_dev_info(struct dmar_domain *domain); -static int dmar_disabled; +int dmar_disabled; static int __initdata dmar_map_gfx = 1; static int dmar_forcedac; static int intel_iommu_strict; @@ -2238,15 +2238,6 @@ static void __init iommu_exit_mempool(void) } -void __init detect_intel_iommu(void) -{ - if (swiotlb || no_iommu || iommu_detected || dmar_disabled) - return; - if (early_dmar_detect()) { - iommu_detected = 1; - } -} - static void __init init_no_remapping_devices(void) { struct dmar_drhd_unit *drhd; @@ -2293,15 +2284,19 @@ int __init intel_iommu_init(void) { int ret = 0; - if (no_iommu || swiotlb || dmar_disabled) - return -ENODEV; - if (dmar_table_init()) return -ENODEV; if (dmar_dev_scope_init()) return -ENODEV; + /* + * Check the need for DMA-remapping initialization now. + * Above initialization will also be used by Interrupt-remapping. + */ + if (no_iommu || swiotlb || dmar_disabled) + return -ENODEV; + iommu_init_mempool(); dmar_init_reserved_ranges(); diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index 2983ce89535..a81a74e2bd9 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -56,6 +56,7 @@ #define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */ #define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */ #define DMAR_ICS_REG 0x98 /* Invalidation complete status register */ +#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */ #define OFFSET_STRIDE (9) /* @@ -157,16 +158,20 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define DMA_GCMD_SRTP (((u32)1) << 30) #define DMA_GCMD_SFL (((u32)1) << 29) #define DMA_GCMD_EAFL (((u32)1) << 28) -#define DMA_GCMD_QIE (((u32)1) << 26) #define DMA_GCMD_WBF (((u32)1) << 27) +#define DMA_GCMD_QIE (((u32)1) << 26) +#define DMA_GCMD_SIRTP (((u32)1) << 24) +#define DMA_GCMD_IRE (((u32) 1) << 25) /* GSTS_REG */ #define DMA_GSTS_TES (((u32)1) << 31) #define DMA_GSTS_RTPS (((u32)1) << 30) #define DMA_GSTS_FLS (((u32)1) << 29) #define DMA_GSTS_AFLS (((u32)1) << 28) -#define DMA_GSTS_QIES (((u32)1) << 26) #define DMA_GSTS_WBFS (((u32)1) << 27) +#define DMA_GSTS_QIES (((u32)1) << 26) +#define DMA_GSTS_IRTPS (((u32)1) << 24) +#define DMA_GSTS_IRES (((u32)1) << 25) /* CCMD_REG */ #define DMA_CCMD_ICC (((u64)1) << 63) @@ -245,6 +250,16 @@ struct q_inval { int free_cnt; }; +#ifdef CONFIG_INTR_REMAP +/* 1MB - maximum possible interrupt remapping table size */ +#define INTR_REMAP_PAGE_ORDER 8 +#define INTR_REMAP_TABLE_REG_SIZE 0xf + +struct ir_table { + struct irte *base; +}; +#endif + struct intel_iommu { void __iomem *reg; /* Pointer to hardware regs, virtual addr */ u64 cap; @@ -266,6 +281,9 @@ struct intel_iommu { struct sys_device sysdev; #endif struct q_inval *qi; /* Queued invalidation info */ +#ifdef CONFIG_INTR_REMAP + struct ir_table *ir_table; /* Interrupt remapping info */ +#endif }; static inline void __iommu_flush_cache( @@ -279,5 +297,7 @@ extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev); extern int alloc_iommu(struct dmar_drhd_unit *drhd); extern void free_iommu(struct intel_iommu *iommu); +extern int dmar_enable_qi(struct intel_iommu *iommu); +extern void qi_global_iec(struct intel_iommu *iommu); #endif diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index a80b87921c6..3d10cdc9031 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -1,10 +1,147 @@ #include +#include +#include +#include #include #include "intel-iommu.h" #include "intr_remapping.h" static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static int ir_ioapic_num; +int intr_remapping_enabled; + +static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) +{ + u64 addr; + u32 cmd, sts; + unsigned long flags; + + addr = virt_to_phys((void *)iommu->ir_table->base); + + spin_lock_irqsave(&iommu->register_lock, flags); + + dmar_writeq(iommu->reg + DMAR_IRTA_REG, + (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE); + + /* Set interrupt-remapping table pointer */ + cmd = iommu->gcmd | DMA_GCMD_SIRTP; + writel(cmd, iommu->reg + DMAR_GCMD_REG); + + IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, + readl, (sts & DMA_GSTS_IRTPS), sts); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + /* + * global invalidation of interrupt entry cache before enabling + * interrupt-remapping. + */ + qi_global_iec(iommu); + + spin_lock_irqsave(&iommu->register_lock, flags); + + /* Enable interrupt-remapping */ + cmd = iommu->gcmd | DMA_GCMD_IRE; + iommu->gcmd |= DMA_GCMD_IRE; + writel(cmd, iommu->reg + DMAR_GCMD_REG); + + IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, + readl, (sts & DMA_GSTS_IRES), sts); + + spin_unlock_irqrestore(&iommu->register_lock, flags); +} + + +static int setup_intr_remapping(struct intel_iommu *iommu, int mode) +{ + struct ir_table *ir_table; + struct page *pages; + + ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), + GFP_KERNEL); + + if (!iommu->ir_table) + return -ENOMEM; + + pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); + + if (!pages) { + printk(KERN_ERR "failed to allocate pages of order %d\n", + INTR_REMAP_PAGE_ORDER); + kfree(iommu->ir_table); + return -ENOMEM; + } + + ir_table->base = page_address(pages); + + iommu_set_intr_remapping(iommu, mode); + return 0; +} + +int __init enable_intr_remapping(int eim) +{ + struct dmar_drhd_unit *drhd; + int setup = 0; + + /* + * check for the Interrupt-remapping support + */ + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (!ecap_ir_support(iommu->ecap)) + continue; + + if (eim && !ecap_eim_support(iommu->ecap)) { + printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, " + " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap); + return -1; + } + } + + /* + * Enable queued invalidation for all the DRHD's. + */ + for_each_drhd_unit(drhd) { + int ret; + struct intel_iommu *iommu = drhd->iommu; + ret = dmar_enable_qi(iommu); + + if (ret) { + printk(KERN_ERR "DRHD %Lx: failed to enable queued, " + " invalidation, ecap %Lx, ret %d\n", + drhd->reg_base_addr, iommu->ecap, ret); + return -1; + } + } + + /* + * Setup Interrupt-remapping for all the DRHD's now. + */ + for_each_drhd_unit(drhd) { + struct intel_iommu *iommu = drhd->iommu; + + if (!ecap_ir_support(iommu->ecap)) + continue; + + if (setup_intr_remapping(iommu, eim)) + goto error; + + setup = 1; + } + + if (!setup) + goto error; + + intr_remapping_enabled = 1; + + return 0; + +error: + /* + * handle error condition gracefully here! + */ + return -1; +} static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, struct intel_iommu *iommu) diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index c4a40b2f33f..05f2635bbe4 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h @@ -4,3 +4,5 @@ struct ioapic_scope { struct intel_iommu *iommu; unsigned int id; }; + +#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) -- cgit v1.2.3 From b6fcb33ad6c05f152a672f7c96c1fab006527b80 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:44 -0700 Subject: x64, x2apic/intr-remap: routines managing Interrupt remapping table entries. Routines handling the management of interrupt remapping table entries. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/intel-iommu.h | 4 + drivers/pci/intr_remapping.c | 243 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h index a81a74e2bd9..2142c01e014 100644 --- a/drivers/pci/intel-iommu.h +++ b/drivers/pci/intel-iommu.h @@ -123,6 +123,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val) #define ecap_qis(e) ((e) & 0x2) #define ecap_eim_support(e) ((e >> 4) & 0x1) #define ecap_ir_support(e) ((e >> 3) & 0x1) +#define ecap_max_handle_mask(e) ((e >> 20) & 0xf) /* IOTLB_REG */ @@ -255,6 +256,8 @@ struct q_inval { #define INTR_REMAP_PAGE_ORDER 8 #define INTR_REMAP_TABLE_REG_SIZE 0xf +#define INTR_REMAP_TABLE_ENTRIES 65536 + struct ir_table { struct irte *base; }; @@ -300,4 +303,5 @@ extern void free_iommu(struct intel_iommu *iommu); extern int dmar_enable_qi(struct intel_iommu *iommu); extern void qi_global_iec(struct intel_iommu *iommu); +extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); #endif diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 3d10cdc9031..bddb4b19b6c 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "intel-iommu.h" #include "intr_remapping.h" @@ -10,6 +11,248 @@ static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static int ir_ioapic_num; int intr_remapping_enabled; +static struct { + struct intel_iommu *iommu; + u16 irte_index; + u16 sub_handle; + u8 irte_mask; +} irq_2_iommu[NR_IRQS]; + +static DEFINE_SPINLOCK(irq_2_ir_lock); + +int irq_remapped(int irq) +{ + if (irq > NR_IRQS) + return 0; + + if (!irq_2_iommu[irq].iommu) + return 0; + + return 1; +} + +int get_irte(int irq, struct irte *entry) +{ + int index; + + if (!entry || irq > NR_IRQS) + return -1; + + spin_lock(&irq_2_ir_lock); + if (!irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + *entry = *(irq_2_iommu[irq].iommu->ir_table->base + index); + + spin_unlock(&irq_2_ir_lock); + return 0; +} + +int alloc_irte(struct intel_iommu *iommu, int irq, u16 count) +{ + struct ir_table *table = iommu->ir_table; + u16 index, start_index; + unsigned int mask = 0; + int i; + + if (!count) + return -1; + + /* + * start the IRTE search from index 0. + */ + index = start_index = 0; + + if (count > 1) { + count = __roundup_pow_of_two(count); + mask = ilog2(count); + } + + if (mask > ecap_max_handle_mask(iommu->ecap)) { + printk(KERN_ERR + "Requested mask %x exceeds the max invalidation handle" + " mask value %Lx\n", mask, + ecap_max_handle_mask(iommu->ecap)); + return -1; + } + + spin_lock(&irq_2_ir_lock); + do { + for (i = index; i < index + count; i++) + if (table->base[i].present) + break; + /* empty index found */ + if (i == index + count) + break; + + index = (index + count) % INTR_REMAP_TABLE_ENTRIES; + + if (index == start_index) { + spin_unlock(&irq_2_ir_lock); + printk(KERN_ERR "can't allocate an IRTE\n"); + return -1; + } + } while (1); + + for (i = index; i < index + count; i++) + table->base[i].present = 1; + + irq_2_iommu[irq].iommu = iommu; + irq_2_iommu[irq].irte_index = index; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = mask; + + spin_unlock(&irq_2_ir_lock); + + return index; +} + +static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask) +{ + struct qi_desc desc; + + desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask) + | QI_IEC_SELECTIVE; + desc.high = 0; + + qi_submit_sync(&desc, iommu); +} + +int map_irq_to_irte_handle(int irq, u16 *sub_handle) +{ + int index; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + *sub_handle = irq_2_iommu[irq].sub_handle; + index = irq_2_iommu[irq].irte_index; + spin_unlock(&irq_2_ir_lock); + return index; +} + +int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle) +{ + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + irq_2_iommu[irq].iommu = iommu; + irq_2_iommu[irq].irte_index = index; + irq_2_iommu[irq].sub_handle = subhandle; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index) +{ + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + irq_2_iommu[irq].iommu = NULL; + irq_2_iommu[irq].irte_index = 0; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int modify_irte(int irq, struct irte *irte_modified) +{ + int index; + struct irte *irte; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + irte = &iommu->ir_table->base[index]; + + set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1)); + __iommu_flush_cache(iommu, irte, sizeof(*irte)); + + qi_flush_iec(iommu, index, 0); + + spin_unlock(&irq_2_ir_lock); + return 0; +} + +int flush_irte(int irq) +{ + int index; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); + spin_unlock(&irq_2_ir_lock); + + return 0; +} + +int free_irte(int irq) +{ + int index, i; + struct irte *irte; + struct intel_iommu *iommu; + + spin_lock(&irq_2_ir_lock); + if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) { + spin_unlock(&irq_2_ir_lock); + return -1; + } + + iommu = irq_2_iommu[irq].iommu; + + index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle; + irte = &iommu->ir_table->base[index]; + + if (!irq_2_iommu[irq].sub_handle) { + for (i = 0; i < (1 << irq_2_iommu[irq].irte_mask); i++) + set_64bit((unsigned long *)irte, 0); + qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask); + } + + irq_2_iommu[irq].iommu = NULL; + irq_2_iommu[irq].irte_index = 0; + irq_2_iommu[irq].sub_handle = 0; + irq_2_iommu[irq].irte_mask = 0; + + spin_unlock(&irq_2_ir_lock); + + return 0; +} + static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) { u64 addr; -- cgit v1.2.3 From 1cb11583a6c4ceda7426eb36f7bf0419da8dfbc2 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:51 -0700 Subject: x64, x2apic/intr-remap: disable DMA-remapping if Interrupt-remapping is detected (temporary quirk) Interrupt-remapping enables queued invalidation. And once queued invalidation is enabled, IOTLB invalidation also needs to use the queued invalidation mechanism and the register based IOTLB invalidation doesn't work. For now, Support for IOTLB invalidation using queued invalidation is missing. Meanwhile, disable DMA-remapping, if Interrupt-remapping support is detected. For the meanwhile, if someone wants to really enable DMA-remapping, they can use nox2apic, which will disable interrupt-remapping and as such doesn't enable queued invalidation. And given that none of the release platforms support intr-remapping yet, we should be ok for this temporary hack. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/dmar.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 23a119e6485..bd2c01674f5 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -457,6 +457,31 @@ void __init detect_intel_iommu(void) #ifdef CONFIG_DMAR { + struct acpi_table_dmar *dmar; + /* + * for now we will disable dma-remapping when interrupt + * remapping is enabled. + * When support for queued invalidation for IOTLB invalidation + * is added, we will not need this any more. + */ + dmar = (struct acpi_table_dmar *) dmar_tbl; + if (ret && cpu_has_x2apic && dmar->flags & 0x1) { + printk(KERN_INFO + "Queued invalidation will be enabled to support " + "x2apic and Intr-remapping.\n"); + printk(KERN_INFO + "Disabling IOMMU detection, because of missing " + "queued invalidation support for IOTLB " + "invalidation\n"); + printk(KERN_INFO + "Use \"nox2apic\", if you want to use Intel " + " IOMMU for DMA-remapping and don't care about " + " x2apic support\n"); + + dmar_disabled = 1; + return; + } + if (ret && !no_iommu && !iommu_detected && !swiotlb && !dmar_disabled) iommu_detected = 1; -- cgit v1.2.3 From 89027d35aa5b8f45ce0f7fa0911db85b46563da0 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:56 -0700 Subject: x64, x2apic/intr-remap: IO-APIC support for interrupt-remapping IO-APIC support in the presence of interrupt-remapping infrastructure. IO-APIC RTE will be programmed with interrupt-remapping table entry(IRTE) index and the IRTE will contain information about the vector, cpu destination, trigger mode etc, which traditionally was present in the IO-APIC RTE. Introduce a new irq_chip for cleaner irq migration (in the process context as opposed to the current irq migration in the context of an interrupt. interrupt-remapping infrastructure will help us achieve this cleanly). For edge triggered, irq migration is a simple atomic update(of vector and cpu destination) of IRTE and flush the hardware cache. For level triggered, we need to modify the io-apic RTE aswell with the update vector information, along with modifying IRTE with vector and cpu destination. So irq migration for level triggered is little bit more complex compared to edge triggered migration. But the good news is, we use the same algorithm for level triggered migration as we have today, only difference being, we now initiate the irq migration from process context instead of the interrupt context. In future, when we do a directed EOI (combined with cpu EOI broadcast suppression) to the IO-APIC, level triggered irq migration will also be as simple as edge triggered migration and we can do the irq migration with a simple atomic update to IO-APIC RTE. TBD: some tests/changes needed in the presence of fixup_irqs() for level triggered irq migration. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/intr_remapping.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index bddb4b19b6c..32e55c7a980 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -220,6 +220,16 @@ int flush_irte(int irq) return 0; } +struct intel_iommu *map_ioapic_to_ir(int apic) +{ + int i; + + for (i = 0; i < MAX_IO_APICS; i++) + if (ir_ioapic[i].id == apic) + return ir_ioapic[i].iommu; + return NULL; +} + int free_irte(int irq) { int index, i; -- cgit v1.2.3 From 75c46fa61bc5b4ccd20a168ff325c58771248fcd Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 10 Jul 2008 11:16:57 -0700 Subject: x64, x2apic/intr-remap: MSI and MSI-X support for interrupt remapping infrastructure MSI and MSI-X support for interrupt remapping infrastructure. MSI address register will be programmed with interrupt-remapping table entry(IRTE) index and the IRTE will contain information about the vector, cpu destination, etc. For MSI-X, all the IRTE's will be consecutively allocated in the table, and the address registers will contain the starting index to the block and the data register will contain the subindex with in that block. This also introduces a new irq_chip for cleaner irq migration (in the process context as opposed to the current irq migration in the context of an interrupt. interrupt-remapping infrastructure will help us achieve this). As MSI is edge triggered, irq migration is a simple atomic update(of vector and cpu destination) of IRTE and flushing the hardware cache. Signed-off-by: Suresh Siddha Cc: akpm@linux-foundation.org Cc: arjan@linux.intel.com Cc: andi@firstfloor.org Cc: ebiederm@xmission.com Cc: jbarnes@virtuousgeek.org Cc: steiner@sgi.com Signed-off-by: Ingo Molnar --- drivers/pci/intr_remapping.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 32e55c7a980..bb642cc5e18 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -230,6 +230,17 @@ struct intel_iommu *map_ioapic_to_ir(int apic) return NULL; } +struct intel_iommu *map_dev_to_ir(struct pci_dev *dev) +{ + struct dmar_drhd_unit *drhd; + + drhd = dmar_find_matched_drhd_unit(dev); + if (!drhd) + return NULL; + + return drhd->iommu; +} + int free_irte(int irq) { int index, i; -- cgit v1.2.3 From 5e246b850df563224be26f1d409cf66fd6c968df Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 8 Aug 2008 17:12:47 +0200 Subject: ALSA: Kill snd_assert() in other places Kill snd_assert() in other places, either removed or replaced with if () with snd_BUG_ON(). Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- drivers/media/video/saa7134/saa7134-alsa.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 9929d20320b..26194a0ce92 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -488,10 +488,12 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, period_size = params_period_bytes(hw_params); periods = params_periods(hw_params); - snd_assert(period_size >= 0x100 && period_size <= 0x10000, - return -EINVAL); - snd_assert(periods >= 4, return -EINVAL); - snd_assert(period_size * periods <= 1024 * 1024, return -EINVAL); + if (period_size < 0x100 || period_size > 0x10000) + return -EINVAL; + if (periods < 4) + return -EINVAL; + if (period_size * periods > 1024 * 1024) + return -EINVAL; dev = saa7134->dev; @@ -942,7 +944,8 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) unsigned int idx; int err; - snd_assert(chip != NULL, return -EINVAL); + if (snd_BUG_ON(!chip)) + return -EINVAL; strcpy(card->mixername, "SAA7134 Mixer"); for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_controls); idx++) { -- cgit v1.2.3 From 466ae837424dcc538b1af2a0eaf53be32edcdbe7 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Mon, 4 Aug 2008 14:51:30 +0800 Subject: reduce tlb/cache flush times of agpgart memory allocation To reduce tlb/cache flush, makes agp memory allocation do one flush after all pages in a region are changed to uc. All agp drivers except agp-sgi uses agp_generic_alloc_page() for .agp_alloc_page, so the patch should work for them. agp-sgi is only for ia64, so not a problem too. Signed-off-by: Shaohua Li Cc: airlied@linux.ie Cc: Andrew Morton Cc: Arjan van de Ven Signed-off-by: Ingo Molnar --- drivers/char/agp/agp.h | 4 ++++ drivers/char/agp/generic.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 81e14bea54b..395168fb17e 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -30,6 +30,10 @@ #define _AGP_BACKEND_PRIV_H 1 #include /* for flush_agp_cache() */ +#ifndef map_page_into_agp_noflush +#define map_page_into_agp_noflush(page) map_page_into_agp(page) +#define map_page_into_agp_global_flush() +#endif #define PFX "agpgart: " diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index eaa1a355bb3..bf239b8ecac 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -274,6 +274,7 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, new->memory[i] = virt_to_gart(addr); new->page_count++; } + map_page_into_agp_global_flush(); new->bridge = bridge; return new; @@ -1186,7 +1187,8 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge) if (page == NULL) return NULL; - map_page_into_agp(page); + /* agp_allocate_memory will do flush */ + map_page_into_agp_noflush(page); get_page(page); atomic_inc(&agp_bridge->current_memory_agp); -- cgit v1.2.3 From 6d341675f8e715464849e5d5563a72c1d39e800d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Va=C5=A1ut?= Date: Sat, 16 Aug 2008 15:35:57 +0100 Subject: [ARM] 5199/1: PalmLD: PCMCIA driver PCMCIA driver for Palm LifeDrive Signed-off-by: Marek Vasut Signed-off-by: Russell King --- drivers/pcmcia/Makefile | 3 +- drivers/pcmcia/pxa2xx_palmld.c | 151 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 drivers/pcmcia/pxa2xx_palmld.c (limited to 'drivers') diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 269a9e913ba..c6d89fd3dcf 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -72,5 +72,6 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o pxa2xx_cs-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x270.o -pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o +pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o +pxa2xx_cs-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c new file mode 100644 index 00000000000..1736c67e547 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_palmld.c @@ -0,0 +1,151 @@ +/* + * linux/drivers/pcmcia/pxa2xx_palmld.c + * + * Driver for Palm LifeDrive PCMCIA + * + * Copyright (C) 2006 Alex Osborne + * Copyright (C) 2007-2008 Marek Vasut + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +#include +#include +#include "soc_common.h" + +static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_POWER, "PCMCIA PWR"); + if (ret) + goto err1; + ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_POWER, 0); + if (ret) + goto err2; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_RESET, "PCMCIA RST"); + if (ret) + goto err2; + ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_RESET, 1); + if (ret) + goto err3; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_READY, "PCMCIA RDY"); + if (ret) + goto err3; + ret = gpio_direction_input(GPIO_NR_PALMLD_PCMCIA_READY); + if (ret) + goto err4; + + skt->irq = IRQ_GPIO(GPIO_NR_PALMLD_PCMCIA_READY); + return 0; + +err4: + gpio_free(GPIO_NR_PALMLD_PCMCIA_READY); +err3: + gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET); +err2: + gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER); +err1: + return ret; +} + +static void palmld_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO_NR_PALMLD_PCMCIA_READY); + gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET); + gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER); +} + +static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = 1; /* always inserted */ + state->ready = !!gpio_get_value(GPIO_NR_PALMLD_PCMCIA_READY); + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int palmld_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + gpio_set_value(GPIO_NR_PALMLD_PCMCIA_POWER, 1); + gpio_set_value(GPIO_NR_PALMLD_PCMCIA_RESET, + !!(state->flags & SS_RESET)); + + return 0; +} + +static void palmld_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void palmld_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level palmld_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 2, + + .hw_init = palmld_pcmcia_hw_init, + .hw_shutdown = palmld_pcmcia_hw_shutdown, + + .socket_state = palmld_pcmcia_socket_state, + .configure_socket = palmld_pcmcia_configure_socket, + + .socket_init = palmld_pcmcia_socket_init, + .socket_suspend = palmld_pcmcia_socket_suspend, +}; + +static struct platform_device *palmld_pcmcia_device; + +static int __init palmld_pcmcia_init(void) +{ + int ret; + + if (!machine_is_palmld()) + return -ENODEV; + + palmld_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!palmld_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(palmld_pcmcia_device, &palmld_pcmcia_ops, + sizeof(palmld_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(palmld_pcmcia_device); + + if (ret) + platform_device_put(palmld_pcmcia_device); + + return ret; +} + +static void __exit palmld_pcmcia_exit(void) +{ + platform_device_unregister(palmld_pcmcia_device); +} + +module_init(palmld_pcmcia_init); +module_exit(palmld_pcmcia_exit); + +MODULE_AUTHOR("Alex Osborne ," + " Marek Vasut "); +MODULE_DESCRIPTION("PCMCIA support for Palm LifeDrive"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ccdffb9a88b2907b159538d7bfd6256621db4f84 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 26 Jul 2008 14:26:06 +0200 Subject: r8169: get ethtool settings through the generic mii helper It avoids to report unsupported link capabilities with the fast-ethernet only 8101/8102. Signed-off-by: Francois Romieu Tested-by: Martin Capitanio Fixed-by: Ivan Vecera Cc: Edward Hsu --- drivers/net/r8169.c | 99 +++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index a3e3895e503..dac2677eeed 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -370,8 +370,9 @@ struct ring_info { }; enum features { - RTL_FEATURE_WOL = (1 << 0), - RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_GMII = (1 << 2), }; struct rtl8169_private { @@ -406,13 +407,15 @@ struct rtl8169_private { struct vlan_group *vlgrp; #endif int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); - void (*get_settings)(struct net_device *, struct ethtool_cmd *); + int (*get_settings)(struct net_device *, struct ethtool_cmd *); void (*phy_reset_enable)(void __iomem *); void (*hw_start)(struct net_device *); unsigned int (*phy_reset_pending)(void __iomem *); unsigned int (*link_ok)(void __iomem *); struct delayed_work task; unsigned features; + + struct mii_if_info mii; }; MODULE_AUTHOR("Realtek and the Linux r8169 crew "); @@ -482,6 +485,23 @@ static int mdio_read(void __iomem *ioaddr, int reg_addr) return value; } +static void rtl_mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + mdio_write(ioaddr, location, val); +} + +static int rtl_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + return mdio_read(ioaddr, location); +} + static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr) { RTL_W16(IntrMask, 0x0000); @@ -850,7 +870,7 @@ static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, #endif -static void rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) { struct rtl8169_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; @@ -867,65 +887,29 @@ static void rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) cmd->speed = SPEED_1000; cmd->duplex = DUPLEX_FULL; /* Always set */ + + return 0; } -static void rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) { struct rtl8169_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - u8 status; - - cmd->supported = SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP; - - cmd->autoneg = 1; - cmd->advertising = ADVERTISED_TP | ADVERTISED_Autoneg; - - if (tp->phy_auto_nego_reg & ADVERTISE_10HALF) - cmd->advertising |= ADVERTISED_10baseT_Half; - if (tp->phy_auto_nego_reg & ADVERTISE_10FULL) - cmd->advertising |= ADVERTISED_10baseT_Full; - if (tp->phy_auto_nego_reg & ADVERTISE_100HALF) - cmd->advertising |= ADVERTISED_100baseT_Half; - if (tp->phy_auto_nego_reg & ADVERTISE_100FULL) - cmd->advertising |= ADVERTISED_100baseT_Full; - if (tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL) - cmd->advertising |= ADVERTISED_1000baseT_Full; - - status = RTL_R8(PHYstatus); - - if (status & _1000bpsF) - cmd->speed = SPEED_1000; - else if (status & _100bps) - cmd->speed = SPEED_100; - else if (status & _10bps) - cmd->speed = SPEED_10; - - if (status & TxFlowCtrl) - cmd->advertising |= ADVERTISED_Asym_Pause; - if (status & RxFlowCtrl) - cmd->advertising |= ADVERTISED_Pause; - - cmd->duplex = ((status & _1000bpsF) || (status & FullDup)) ? - DUPLEX_FULL : DUPLEX_HALF; + + return mii_ethtool_gset(&tp->mii, cmd); } static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct rtl8169_private *tp = netdev_priv(dev); unsigned long flags; + int rc; spin_lock_irqsave(&tp->lock, flags); - tp->get_settings(dev, cmd); + rc = tp->get_settings(dev, cmd); spin_unlock_irqrestore(&tp->lock, flags); - return 0; + return rc; } static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, @@ -1513,7 +1497,7 @@ static const struct rtl_cfg_info { unsigned int align; u16 intr_event; u16 napi_event; - unsigned msi; + unsigned features; } rtl_cfg_infos [] = { [RTL_CFG_0] = { .hw_start = rtl_hw_start_8169, @@ -1522,7 +1506,7 @@ static const struct rtl_cfg_info { .intr_event = SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxOK | RxErr, .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, - .msi = 0 + .features = RTL_FEATURE_GMII }, [RTL_CFG_1] = { .hw_start = rtl_hw_start_8168, @@ -1531,7 +1515,7 @@ static const struct rtl_cfg_info { .intr_event = SYSErr | LinkChg | RxOverflow | TxErr | TxOK | RxOK | RxErr, .napi_event = TxErr | TxOK | RxOK | RxOverflow, - .msi = RTL_FEATURE_MSI + .features = RTL_FEATURE_GMII | RTL_FEATURE_MSI }, [RTL_CFG_2] = { .hw_start = rtl_hw_start_8101, @@ -1540,7 +1524,7 @@ static const struct rtl_cfg_info { .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | RxFIFOOver | TxErr | TxOK | RxOK | RxErr, .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, - .msi = RTL_FEATURE_MSI + .features = RTL_FEATURE_MSI } }; @@ -1552,7 +1536,7 @@ static unsigned rtl_try_msi(struct pci_dev *pdev, void __iomem *ioaddr, u8 cfg2; cfg2 = RTL_R8(Config2) & ~MSIEnable; - if (cfg->msi) { + if (cfg->features & RTL_FEATURE_MSI) { if (pci_enable_msi(pdev)) { dev_info(&pdev->dev, "no MSI. Back to INTx.\n"); } else { @@ -1578,6 +1562,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) const struct rtl_cfg_info *cfg = rtl_cfg_infos + ent->driver_data; const unsigned int region = cfg->region; struct rtl8169_private *tp; + struct mii_if_info *mii; struct net_device *dev; void __iomem *ioaddr; unsigned int i; @@ -1602,6 +1587,14 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->pci_dev = pdev; tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT); + mii = &tp->mii; + mii->dev = dev; + mii->mdio_read = rtl_mdio_read; + mii->mdio_write = rtl_mdio_write; + mii->phy_id_mask = 0x1f; + mii->reg_num_mask = 0x1f; + mii->supports_gmii = !!(cfg->features & RTL_FEATURE_GMII); + /* enable device (incl. PCI PM wakeup and hotplug setup) */ rc = pci_enable_device(pdev); if (rc < 0) { -- cgit v1.2.3 From 458a9f617adfb2fc5f38e7673339115c4ba3290f Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 2 Aug 2008 15:50:02 +0200 Subject: r8169: Tx performance tweak helper Signed-off-by: Francois Romieu Cc: Edward Hsu --- drivers/net/r8169.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index dac2677eeed..26fa6e0c6d9 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -2054,12 +2054,20 @@ static void rtl_hw_start_8169(struct net_device *dev) RTL_W16(IntrMask, tp->intr_event); } +static void rtl_tx_performance_tweak(struct pci_dev *pdev, u8 force) +{ + u8 ctl; + + pci_read_config_byte(pdev, 0x69, &ctl); + ctl = (ctl & ~0x70) | force; + pci_write_config_byte(pdev, 0x69, ctl); +} + static void rtl_hw_start_8168(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; struct pci_dev *pdev = tp->pci_dev; - u8 ctl; RTL_W8(Cfg9346, Cfg9346_Unlock); @@ -2073,10 +2081,7 @@ static void rtl_hw_start_8168(struct net_device *dev) RTL_W16(CPlusCmd, tp->cp_cmd); - /* Tx performance tweak. */ - pci_read_config_byte(pdev, 0x69, &ctl); - ctl = (ctl & ~0x70) | 0x50; - pci_write_config_byte(pdev, 0x69, ctl); + rtl_tx_performance_tweak(pdev, 0x50); RTL_W16(IntrMitigate, 0x5151); -- cgit v1.2.3 From 9c14ceafa5ca7f57225a43fb0785c56ddc7f1823 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 5 Jul 2008 00:21:15 +0200 Subject: r8169: use pci_find_capability for the PCI-E features Signed-off-by: Francois Romieu Cc: Edward Hsu --- drivers/net/r8169.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 26fa6e0c6d9..ae149a93062 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -61,6 +61,7 @@ static const int multicast_filter_limit = 32; /* MAC address length */ #define MAC_ADDR_LEN 6 +#define MAX_READ_REQUEST_SHIFT 12 #define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ #define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ #define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ @@ -412,6 +413,7 @@ struct rtl8169_private { void (*hw_start)(struct net_device *); unsigned int (*phy_reset_pending)(void __iomem *); unsigned int (*link_ok)(void __iomem *); + int pcie_cap; struct delayed_work task; unsigned features; @@ -1663,6 +1665,10 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_res_4; } + tp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!tp->pcie_cap && netif_msg_probe(tp)) + dev_info(&pdev->dev, "no PCI Express capability\n"); + /* Unneeded ? Don't mess with Mrs. Murphy. */ rtl8169_irq_mask_and_ack(ioaddr); @@ -2054,13 +2060,19 @@ static void rtl_hw_start_8169(struct net_device *dev) RTL_W16(IntrMask, tp->intr_event); } -static void rtl_tx_performance_tweak(struct pci_dev *pdev, u8 force) +static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force) { - u8 ctl; + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + if (cap) { + u16 ctl; - pci_read_config_byte(pdev, 0x69, &ctl); - ctl = (ctl & ~0x70) | force; - pci_write_config_byte(pdev, 0x69, ctl); + pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl); + ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force; + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl); + } } static void rtl_hw_start_8168(struct net_device *dev) @@ -2081,7 +2093,7 @@ static void rtl_hw_start_8168(struct net_device *dev) RTL_W16(CPlusCmd, tp->cp_cmd); - rtl_tx_performance_tweak(pdev, 0x50); + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); RTL_W16(IntrMitigate, 0x5151); @@ -2116,8 +2128,12 @@ static void rtl_hw_start_8101(struct net_device *dev) if ((tp->mac_version == RTL_GIGA_MAC_VER_13) || (tp->mac_version == RTL_GIGA_MAC_VER_16)) { - pci_write_config_word(pdev, 0x68, 0x00); - pci_write_config_word(pdev, 0x69, 0x08); + int cap = tp->pcie_cap; + + if (cap) { + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_NOSNOOP_EN); + } } RTL_W8(Cfg9346, Cfg9346_Unlock); -- cgit v1.2.3 From f162a5d1b326d54b0be7e3100f69763d8a707721 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sun, 1 Jun 2008 22:37:49 +0200 Subject: r8169: add 8168/8101 registers description Signed-off-by: Francois Romieu Cc: Edward Hsu --- drivers/net/r8169.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index ae149a93062..600540e3670 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -197,9 +197,6 @@ enum rtl_registers { Config5 = 0x56, MultiIntr = 0x5c, PHYAR = 0x60, - TBICSR = 0x64, - TBI_ANAR = 0x68, - TBI_LPAR = 0x6a, PHYstatus = 0x6c, RxMaxSize = 0xda, CPlusCmd = 0xe0, @@ -213,6 +210,32 @@ enum rtl_registers { FuncForceEvent = 0xfc, }; +enum rtl8110_registers { + TBICSR = 0x64, + TBI_ANAR = 0x68, + TBI_LPAR = 0x6a, +}; + +enum rtl8168_8101_registers { + CSIDR = 0x64, + CSIAR = 0x68, +#define CSIAR_FLAG 0x80000000 +#define CSIAR_WRITE_CMD 0x80000000 +#define CSIAR_BYTE_ENABLE 0x0f +#define CSIAR_BYTE_ENABLE_SHIFT 12 +#define CSIAR_ADDR_MASK 0x0fff + + EPHYAR = 0x80, +#define EPHYAR_FLAG 0x80000000 +#define EPHYAR_WRITE_CMD 0x80000000 +#define EPHYAR_REG_MASK 0x1f +#define EPHYAR_REG_SHIFT 16 +#define EPHYAR_DATA_MASK 0xffff + DBG_REG = 0xd1, +#define FIX_NAK_1 (1 << 4) +#define FIX_NAK_2 (1 << 3) +}; + enum rtl_register_content { /* InterruptStatusBits */ SYSErr = 0x8000, @@ -266,7 +289,13 @@ enum rtl_register_content { TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ /* Config1 register p.24 */ + LEDS1 = (1 << 7), + LEDS0 = (1 << 6), MSIEnable = (1 << 5), /* Enable Message Signaled Interrupt */ + Speed_down = (1 << 4), + MEMMAP = (1 << 3), + IOMAP = (1 << 2), + VPD = (1 << 1), PMEnable = (1 << 0), /* Power Management Enable */ /* Config2 register p. 25 */ @@ -276,6 +305,7 @@ enum rtl_register_content { /* Config3 register p.25 */ MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */ /* Config5 register p.27 */ BWF = (1 << 6), /* Accept Broadcast wakeup frame */ @@ -293,7 +323,16 @@ enum rtl_register_content { TBINwComplete = 0x01000000, /* CPlusCmd p.31 */ - PktCntrDisable = (1 << 7), // 8168 + EnableBist = (1 << 15), // 8168 8101 + Mac_dbgo_oe = (1 << 14), // 8168 8101 + Normal_mode = (1 << 13), // unused + Force_half_dup = (1 << 12), // 8168 8101 + Force_rxflow_en = (1 << 11), // 8168 8101 + Force_txflow_en = (1 << 10), // 8168 8101 + Cxpl_dbg_sel = (1 << 9), // 8168 8101 + ASF = (1 << 8), // 8168 8101 + PktCntrDisable = (1 << 7), // 8168 8101 + Mac_dbgo_sel = 0x001c, // 8168 RxVlan = (1 << 6), RxChkSum = (1 << 5), PCIDAC = (1 << 4), -- cgit v1.2.3 From dacf815434a4d5f5b45687873df46927c64cfb19 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 2 Aug 2008 20:44:13 +0200 Subject: r8169: add hw start helpers for the 8168 and the 8101 This commit triggers three 'defined but not used' warnings but I prefer avoiding to tie these helpers to a specific change in the hw start sequences of the 8168 or of the 8101. Signed-off-by: Francois Romieu Cc: Edward Hsu --- drivers/net/r8169.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 600540e3670..f4ad9ff2686 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -526,6 +526,11 @@ static int mdio_read(void __iomem *ioaddr, int reg_addr) return value; } +static void mdio_patch(void __iomem *ioaddr, int reg_addr, int value) +{ + mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); +} + static void rtl_mdio_write(struct net_device *dev, int phy_id, int location, int val) { @@ -543,6 +548,72 @@ static int rtl_mdio_read(struct net_device *dev, int phy_id, int location) return mdio_read(ioaddr, location); } +static void rtl_ephy_write(void __iomem *ioaddr, int reg_addr, int value) +{ + unsigned int i; + + RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG)) + break; + udelay(10); + } +} + +static u16 rtl_ephy_read(void __iomem *ioaddr, int reg_addr) +{ + u16 value = 0xffff; + unsigned int i; + + RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(EPHYAR) & EPHYAR_FLAG) { + value = RTL_R32(EPHYAR) & EPHYAR_DATA_MASK; + break; + } + udelay(10); + } + + return value; +} + +static void rtl_csi_write(void __iomem *ioaddr, int addr, int value) +{ + unsigned int i; + + RTL_W32(CSIDR, value); + RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(CSIAR) & CSIAR_FLAG)) + break; + udelay(10); + } +} + +static u32 rtl_csi_read(void __iomem *ioaddr, int addr) +{ + u32 value = ~0x00; + unsigned int i; + + RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(CSIAR) & CSIAR_FLAG) { + value = RTL_R32(CSIDR); + break; + } + udelay(10); + } + + return value; +} + static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr) { RTL_W16(IntrMask, 0x0000); @@ -2114,6 +2185,31 @@ static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force) } } +static void rtl_csi_access_enable(void __iomem *ioaddr) +{ + u32 csi; + + csi = rtl_csi_read(ioaddr, 0x070c) & 0x00ffffff; + rtl_csi_write(ioaddr, 0x070c, csi | 0x27000000); +} + +struct ephy_info { + unsigned int offset; + u16 mask; + u16 bits; +}; + +static void rtl_ephy_init(void __iomem *ioaddr, struct ephy_info *e, int len) +{ + u16 w; + + while (len-- > 0) { + w = (rtl_ephy_read(ioaddr, e->offset) & ~e->mask) | e->bits; + rtl_ephy_write(ioaddr, e->offset, w); + e++; + } +} + static void rtl_hw_start_8168(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); -- cgit v1.2.3 From 2857ffb7b8913ef713533ac5783abd70a20529e4 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 2 Aug 2008 21:08:49 +0200 Subject: r8169: additional 8101 and 8102 support Signed-off-by: Ivan Vecera Signed-off-by: Francois Romieu Cc: Edward Hsu --- drivers/net/r8169.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index f4ad9ff2686..bbaf8a55080 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -96,6 +96,10 @@ enum mac_version { RTL_GIGA_MAC_VER_04 = 0x04, // 8169SB RTL_GIGA_MAC_VER_05 = 0x05, // 8110SCd RTL_GIGA_MAC_VER_06 = 0x06, // 8110SCe + RTL_GIGA_MAC_VER_07 = 0x07, // 8102e + RTL_GIGA_MAC_VER_08 = 0x08, // 8102e + RTL_GIGA_MAC_VER_09 = 0x09, // 8102e + RTL_GIGA_MAC_VER_10 = 0x0a, // 8101e RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb @@ -122,6 +126,10 @@ static const struct { _R("RTL8169sb/8110sb", RTL_GIGA_MAC_VER_04, 0xff7e1880), // 8169SB _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_05, 0xff7e1880), // 8110SCd _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8110SCe + _R("RTL8102e", RTL_GIGA_MAC_VER_07, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_08, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_09, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_10, 0xff7e1880), // PCI-E _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_11, 0xff7e1880), // PCI-E _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_12, 0xff7e1880), // PCI-E _R("RTL8101e", RTL_GIGA_MAC_VER_13, 0xff7e1880), // PCI-E 8139 @@ -837,8 +845,12 @@ static int rtl8169_set_speed_xmii(struct net_device *dev, } } - /* The 8100e/8101e do Fast Ethernet only. */ - if ((tp->mac_version == RTL_GIGA_MAC_VER_13) || + /* The 8100e/8101e/8102e do Fast Ethernet only. */ + if ((tp->mac_version == RTL_GIGA_MAC_VER_07) || + (tp->mac_version == RTL_GIGA_MAC_VER_08) || + (tp->mac_version == RTL_GIGA_MAC_VER_09) || + (tp->mac_version == RTL_GIGA_MAC_VER_10) || + (tp->mac_version == RTL_GIGA_MAC_VER_13) || (tp->mac_version == RTL_GIGA_MAC_VER_14) || (tp->mac_version == RTL_GIGA_MAC_VER_15) || (tp->mac_version == RTL_GIGA_MAC_VER_16)) { @@ -1212,8 +1224,17 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp, { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 }, /* 8101 family. */ + { 0x7cf00000, 0x34a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x24a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x34900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x24900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x34800000, RTL_GIGA_MAC_VER_07 }, + { 0x7cf00000, 0x24800000, RTL_GIGA_MAC_VER_07 }, { 0x7cf00000, 0x34000000, RTL_GIGA_MAC_VER_13 }, + { 0x7cf00000, 0x34300000, RTL_GIGA_MAC_VER_10 }, { 0x7cf00000, 0x34200000, RTL_GIGA_MAC_VER_16 }, + { 0x7c800000, 0x34800000, RTL_GIGA_MAC_VER_09 }, + { 0x7c800000, 0x24800000, RTL_GIGA_MAC_VER_09 }, { 0x7c800000, 0x34000000, RTL_GIGA_MAC_VER_16 }, /* FIXME: where did these entries come from ? -- FR */ { 0xfc800000, 0x38800000, RTL_GIGA_MAC_VER_15 }, @@ -1375,6 +1396,22 @@ static void rtl8168cx_hw_phy_config(void __iomem *ioaddr) rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); } +static void rtl8102e_hw_phy_config(void __iomem *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_patch(ioaddr, 0x11, 1 << 12); + mdio_patch(ioaddr, 0x19, 1 << 13); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + static void rtl_hw_phy_config(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); @@ -1392,6 +1429,11 @@ static void rtl_hw_phy_config(struct net_device *dev) case RTL_GIGA_MAC_VER_04: rtl8169sb_hw_phy_config(ioaddr); break; + case RTL_GIGA_MAC_VER_07: + case RTL_GIGA_MAC_VER_08: + case RTL_GIGA_MAC_VER_09: + rtl8102e_hw_phy_config(ioaddr); + break; case RTL_GIGA_MAC_VER_18: rtl8168cp_hw_phy_config(ioaddr); break; @@ -2255,6 +2297,70 @@ static void rtl_hw_start_8168(struct net_device *dev) RTL_W16(IntrMask, tp->intr_event); } +#define R810X_CPCMD_QUIRK_MASK (\ + EnableBist | \ + Mac_dbgo_oe | \ + Force_half_dup | \ + Force_half_dup | \ + Force_txflow_en | \ + Cxpl_dbg_sel | \ + ASF | \ + PktCntrDisable | \ + PCIDAC | \ + PCIMulRW) + +static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev) +{ + static struct ephy_info e_info_8102e_1[] = { + { 0x01, 0, 0x6e65 }, + { 0x02, 0, 0x091f }, + { 0x03, 0, 0xc2f9 }, + { 0x06, 0, 0xafb5 }, + { 0x07, 0, 0x0e00 }, + { 0x19, 0, 0xec80 }, + { 0x01, 0, 0x2e65 }, + { 0x01, 0, 0x6e65 } + }; + u8 cfg1; + + rtl_csi_access_enable(ioaddr); + + RTL_W8(DBG_REG, FIX_NAK_1); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, + LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + cfg1 = RTL_R8(Config1); + if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) + RTL_W8(Config1, cfg1 & ~LEDS0); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); + + rtl_ephy_init(ioaddr, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1)); +} + +static void rtl_hw_start_8102e_2(void __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_csi_access_enable(ioaddr); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8102e_3(void __iomem *ioaddr, struct pci_dev *pdev) +{ + rtl_hw_start_8102e_2(ioaddr, pdev); + + rtl_ephy_write(ioaddr, 0x03, 0xc2f9); +} + static void rtl_hw_start_8101(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); @@ -2271,6 +2377,20 @@ static void rtl_hw_start_8101(struct net_device *dev) } } + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_07: + rtl_hw_start_8102e_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_08: + rtl_hw_start_8102e_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_09: + rtl_hw_start_8102e_2(ioaddr, pdev); + break; + } + RTL_W8(Cfg9346, Cfg9346_Unlock); RTL_W8(EarlyTxThres, EarlyTxThld); -- cgit v1.2.3 From d9105c2b01eedb620cae96073dde4f760367817f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Va=C5=A1ut?= Date: Sun, 3 Aug 2008 21:34:08 +0100 Subject: [ARM] 5184/1: Split ucb1400_ts into core and touchscreen This patch splits ucb1400_ts into ucb1400_ts and ucb1400_core. Since this chip supports more features than only touchscreen, it was necessary to prepare it for feature addition. The previous functionality is preserved by applying this patch. [Build fixes for non-ARM by Stephen Rothwell and Takashi Iwai] Signed-off-by: Marek Vasut Signed-off-by: Russell King --- drivers/input/touchscreen/Kconfig | 1 + drivers/input/touchscreen/ucb1400_ts.c | 382 ++++++++++++--------------------- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/ucb1400_core.c | 106 +++++++++ 5 files changed, 251 insertions(+), 248 deletions(-) create mode 100644 drivers/mfd/ucb1400_core.c (limited to 'drivers') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6e60a97a234..fcabff9b39b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -220,6 +220,7 @@ config TOUCHSCREEN_ATMEL_TSADCC config TOUCHSCREEN_UCB1400 tristate "Philips UCB1400 touchscreen" select AC97_BUS + depends on UCB1400_CORE help This enables support for the Philips UCB1400 touchscreen interface. The UCB1400 is an AC97 audio codec. The touchscreen interface diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index bce018e45bc..54986627def 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -5,6 +5,10 @@ * Created: September 25, 2006 * Copyright: MontaVista Software, Inc. * + * Spliting done by: Marek Vasut + * If something doesnt work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -25,124 +29,16 @@ #include #include #include - -#include -#include - - -/* - * Interesting UCB1400 AC-link registers - */ - -#define UCB_IE_RIS 0x5e -#define UCB_IE_FAL 0x60 -#define UCB_IE_STATUS 0x62 -#define UCB_IE_CLEAR 0x62 -#define UCB_IE_ADC (1 << 11) -#define UCB_IE_TSPX (1 << 12) - -#define UCB_TS_CR 0x64 -#define UCB_TS_CR_TSMX_POW (1 << 0) -#define UCB_TS_CR_TSPX_POW (1 << 1) -#define UCB_TS_CR_TSMY_POW (1 << 2) -#define UCB_TS_CR_TSPY_POW (1 << 3) -#define UCB_TS_CR_TSMX_GND (1 << 4) -#define UCB_TS_CR_TSPX_GND (1 << 5) -#define UCB_TS_CR_TSMY_GND (1 << 6) -#define UCB_TS_CR_TSPY_GND (1 << 7) -#define UCB_TS_CR_MODE_INT (0 << 8) -#define UCB_TS_CR_MODE_PRES (1 << 8) -#define UCB_TS_CR_MODE_POS (2 << 8) -#define UCB_TS_CR_BIAS_ENA (1 << 11) -#define UCB_TS_CR_TSPX_LOW (1 << 12) -#define UCB_TS_CR_TSMX_LOW (1 << 13) - -#define UCB_ADC_CR 0x66 -#define UCB_ADC_SYNC_ENA (1 << 0) -#define UCB_ADC_VREFBYP_CON (1 << 1) -#define UCB_ADC_INP_TSPX (0 << 2) -#define UCB_ADC_INP_TSMX (1 << 2) -#define UCB_ADC_INP_TSPY (2 << 2) -#define UCB_ADC_INP_TSMY (3 << 2) -#define UCB_ADC_INP_AD0 (4 << 2) -#define UCB_ADC_INP_AD1 (5 << 2) -#define UCB_ADC_INP_AD2 (6 << 2) -#define UCB_ADC_INP_AD3 (7 << 2) -#define UCB_ADC_EXT_REF (1 << 5) -#define UCB_ADC_START (1 << 7) -#define UCB_ADC_ENA (1 << 15) - -#define UCB_ADC_DATA 0x68 -#define UCB_ADC_DAT_VALID (1 << 15) -#define UCB_ADC_DAT_VALUE(x) ((x) & 0x3ff) - -#define UCB_ID 0x7e -#define UCB_ID_1400 0x4304 - - -struct ucb1400 { - struct snd_ac97 *ac97; - struct input_dev *ts_idev; - - int irq; - - wait_queue_head_t ts_wait; - struct task_struct *ts_task; - - unsigned int irq_pending; /* not bit field shared */ - unsigned int ts_restart:1; - unsigned int adcsync:1; -}; +#include static int adcsync; static int ts_delay = 55; /* us */ static int ts_delay_pressure; /* us */ -static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg) -{ - return ucb->ac97->bus->ops->read(ucb->ac97, reg); -} - -static inline void ucb1400_reg_write(struct ucb1400 *ucb, u16 reg, u16 val) -{ - ucb->ac97->bus->ops->write(ucb->ac97, reg, val); -} - -static inline void ucb1400_adc_enable(struct ucb1400 *ucb) -{ - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); -} - -static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) -{ - unsigned int val; - - if (ucb->adcsync) - adc_channel |= UCB_ADC_SYNC_ENA; - - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel); - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | UCB_ADC_START); - - for (;;) { - val = ucb1400_reg_read(ucb, UCB_ADC_DATA); - if (val & UCB_ADC_DAT_VALID) - break; - /* yield to other processes */ - schedule_timeout_uninterruptible(1); - } - - return UCB_ADC_DAT_VALUE(val); -} - -static inline void ucb1400_adc_disable(struct ucb1400 *ucb) -{ - ucb1400_reg_write(ucb, UCB_ADC_CR, 0); -} - /* Switch to interrupt mode. */ -static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb) +static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ac97, UCB_TS_CR, UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_INT); @@ -152,14 +48,14 @@ static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb) * Switch to pressure mode, and read pressure. We don't need to wait * here, since both plates are being driven. */ -static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); udelay(ts_delay_pressure); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); } /* @@ -168,21 +64,21 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise. */ -static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); udelay(ts_delay); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync); } /* @@ -191,63 +87,63 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise. */ -static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); udelay(ts_delay); - return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX); + return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync); } /* * Switch to X plate resistance mode. Set MX to ground, PX to * supply. Measure current. */ -static inline unsigned int ucb1400_ts_read_xres(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - return ucb1400_adc_read(ucb, 0); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); } /* * Switch to Y plate resistance mode. Set MY to ground, PY to * supply. Measure current. */ -static inline unsigned int ucb1400_ts_read_yres(struct ucb1400 *ucb) +static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) { - ucb1400_reg_write(ucb, UCB_TS_CR, + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); - return ucb1400_adc_read(ucb, 0); + return ucb1400_adc_read(ucb->ac97, 0, adcsync); } -static inline int ucb1400_ts_pen_down(struct ucb1400 *ucb) +static inline int ucb1400_ts_pen_down(struct snd_ac97 *ac97) { - unsigned short val = ucb1400_reg_read(ucb, UCB_TS_CR); - return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); + unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); + return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); } -static inline void ucb1400_ts_irq_enable(struct ucb1400 *ucb) +static inline void ucb1400_ts_irq_enable(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_IE_CLEAR, UCB_IE_TSPX); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); - ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX); } -static inline void ucb1400_ts_irq_disable(struct ucb1400 *ucb) +static inline void ucb1400_ts_irq_disable(struct snd_ac97 *ac97) { - ucb1400_reg_write(ucb, UCB_IE_FAL, 0); + ucb1400_reg_write(ac97, UCB_IE_FAL, 0); } static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y) @@ -264,25 +160,24 @@ static void ucb1400_ts_event_release(struct input_dev *idev) input_sync(idev); } -static void ucb1400_handle_pending_irq(struct ucb1400 *ucb) +static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb) { unsigned int isr; - isr = ucb1400_reg_read(ucb, UCB_IE_STATUS); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); - if (isr & UCB_IE_TSPX) - ucb1400_ts_irq_disable(ucb); - else + if (isr & UCB_IE_TSPX) { + ucb1400_ts_irq_disable(ucb->ac97); + enable_irq(ucb->irq); + } else printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr); - - enable_irq(ucb->irq); } static int ucb1400_ts_thread(void *_ucb) { - struct ucb1400 *ucb = _ucb; + struct ucb1400_ts *ucb = _ucb; struct task_struct *tsk = current; int valid = 0; struct sched_param param = { .sched_priority = 1 }; @@ -301,19 +196,19 @@ static int ucb1400_ts_thread(void *_ucb) ucb1400_handle_pending_irq(ucb); } - ucb1400_adc_enable(ucb); + ucb1400_adc_enable(ucb->ac97); x = ucb1400_ts_read_xpos(ucb); y = ucb1400_ts_read_ypos(ucb); p = ucb1400_ts_read_pressure(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); /* Switch back to interrupt mode. */ - ucb1400_ts_mode_int(ucb); + ucb1400_ts_mode_int(ucb->ac97); msleep(10); - if (ucb1400_ts_pen_down(ucb)) { - ucb1400_ts_irq_enable(ucb); + if (ucb1400_ts_pen_down(ucb->ac97)) { + ucb1400_ts_irq_enable(ucb->ac97); /* * If we spat out a valid sample set last time, @@ -332,8 +227,8 @@ static int ucb1400_ts_thread(void *_ucb) } wait_event_freezable_timeout(ucb->ts_wait, - ucb->irq_pending || ucb->ts_restart || kthread_should_stop(), - timeout); + ucb->irq_pending || ucb->ts_restart || + kthread_should_stop(), timeout); } /* Send the "pen off" if we are stopping with the pen still active */ @@ -356,7 +251,7 @@ static int ucb1400_ts_thread(void *_ucb) */ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) { - struct ucb1400 *ucb = devid; + struct ucb1400_ts *ucb = devid; if (irqnr == ucb->irq) { disable_irq(ucb->irq); @@ -369,7 +264,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) static int ucb1400_ts_open(struct input_dev *idev) { - struct ucb1400 *ucb = input_get_drvdata(idev); + struct ucb1400_ts *ucb = input_get_drvdata(idev); int ret = 0; BUG_ON(ucb->ts_task); @@ -385,34 +280,14 @@ static int ucb1400_ts_open(struct input_dev *idev) static void ucb1400_ts_close(struct input_dev *idev) { - struct ucb1400 *ucb = input_get_drvdata(idev); + struct ucb1400_ts *ucb = input_get_drvdata(idev); if (ucb->ts_task) kthread_stop(ucb->ts_task); - ucb1400_ts_irq_disable(ucb); - ucb1400_reg_write(ucb, UCB_TS_CR, 0); -} - -#ifdef CONFIG_PM -static int ucb1400_ts_resume(struct device *dev) -{ - struct ucb1400 *ucb = dev_get_drvdata(dev); - - if (ucb->ts_task) { - /* - * Restart the TS thread to ensure the - * TS interrupt mode is set up again - * after sleep. - */ - ucb->ts_restart = 1; - wake_up(&ucb->ts_wait); - } - return 0; + ucb1400_ts_irq_disable(ucb->ac97); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); } -#else -#define ucb1400_ts_resume NULL -#endif #ifndef NO_IRQ #define NO_IRQ 0 @@ -422,25 +297,26 @@ static int ucb1400_ts_resume(struct device *dev) * Try to probe our interrupt, rather than relying on lots of * hard-coded machine dependencies. */ -static int ucb1400_detect_irq(struct ucb1400 *ucb) +static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb) { unsigned long mask, timeout; mask = probe_irq_on(); /* Enable the ADC interrupt. */ - ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); - ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); /* Cause an ADC interrupt. */ - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); /* Wait for the conversion to complete. */ timeout = jiffies + HZ/2; - while (!(ucb1400_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) { + while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) & + UCB_ADC_DAT_VALID)) { cpu_relax(); if (time_after(jiffies, timeout)) { printk(KERN_ERR "ucb1400: timed out in IRQ probe\n"); @@ -448,13 +324,13 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return -ENODEV; } } - ucb1400_reg_write(ucb, UCB_ADC_CR, 0); + ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0); /* Disable and clear interrupt. */ - ucb1400_reg_write(ucb, UCB_IE_RIS, 0); - ucb1400_reg_write(ucb, UCB_IE_FAL, 0); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); /* Read triggered interrupt. */ ucb->irq = probe_irq_off(mask); @@ -464,36 +340,25 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return 0; } -static int ucb1400_ts_probe(struct device *dev) +static int ucb1400_ts_probe(struct platform_device *dev) { - struct ucb1400 *ucb; - struct input_dev *idev; - int error, id, x_res, y_res; + int error, x_res, y_res; + struct ucb1400_ts *ucb = dev->dev.platform_data; - ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); - idev = input_allocate_device(); - if (!ucb || !idev) { + ucb->ts_idev = input_allocate_device(); + if (!ucb->ts_idev) { error = -ENOMEM; - goto err_free_devs; + goto err; } - ucb->ts_idev = idev; - ucb->adcsync = adcsync; - ucb->ac97 = to_ac97_t(dev); - init_waitqueue_head(&ucb->ts_wait); - - id = ucb1400_reg_read(ucb, UCB_ID); - if (id != UCB_ID_1400) { - error = -ENODEV; - goto err_free_devs; - } - - error = ucb1400_detect_irq(ucb); + error = ucb1400_ts_detect_irq(ucb); if (error) { printk(KERN_ERR "UCB1400: IRQ probe failed\n"); goto err_free_devs; } + init_waitqueue_head(&ucb->ts_wait); + error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING, "UCB1400", ucb); if (error) { @@ -503,80 +368,101 @@ static int ucb1400_ts_probe(struct device *dev) } printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); - input_set_drvdata(idev, ucb); + input_set_drvdata(ucb->ts_idev, ucb); - idev->dev.parent = dev; - idev->name = "UCB1400 touchscreen interface"; - idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1); - idev->id.product = id; - idev->open = ucb1400_ts_open; - idev->close = ucb1400_ts_close; - idev->evbit[0] = BIT_MASK(EV_ABS); + ucb->ts_idev->dev.parent = &dev->dev; + ucb->ts_idev->name = "UCB1400 touchscreen interface"; + ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97, + AC97_VENDOR_ID1); + ucb->ts_idev->id.product = ucb->id; + ucb->ts_idev->open = ucb1400_ts_open; + ucb->ts_idev->close = ucb1400_ts_close; + ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS); - ucb1400_adc_enable(ucb); + ucb1400_adc_enable(ucb->ac97); x_res = ucb1400_ts_read_xres(ucb); y_res = ucb1400_ts_read_yres(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res); - input_set_abs_params(idev, ABS_X, 0, x_res, 0, 0); - input_set_abs_params(idev, ABS_Y, 0, y_res, 0, 0); - input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0); + input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0); - error = input_register_device(idev); + error = input_register_device(ucb->ts_idev); if (error) goto err_free_irq; - dev_set_drvdata(dev, ucb); return 0; - err_free_irq: +err_free_irq: free_irq(ucb->irq, ucb); - err_free_devs: - input_free_device(idev); - kfree(ucb); +err_free_devs: + input_free_device(ucb->ts_idev); +err: return error; + } -static int ucb1400_ts_remove(struct device *dev) +static int ucb1400_ts_remove(struct platform_device *dev) { - struct ucb1400 *ucb = dev_get_drvdata(dev); + struct ucb1400_ts *ucb = dev->dev.platform_data; free_irq(ucb->irq, ucb); input_unregister_device(ucb->ts_idev); - dev_set_drvdata(dev, NULL); - kfree(ucb); return 0; } -static struct device_driver ucb1400_ts_driver = { - .name = "ucb1400_ts", - .owner = THIS_MODULE, - .bus = &ac97_bus_type, - .probe = ucb1400_ts_probe, - .remove = ucb1400_ts_remove, - .resume = ucb1400_ts_resume, +#ifdef CONFIG_PM +static int ucb1400_ts_resume(struct platform_device *dev) +{ + struct ucb1400_ts *ucb = platform_get_drvdata(dev); + + if (ucb->ts_task) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ucb->ts_restart = 1; + wake_up(&ucb->ts_wait); + } + return 0; +} +#else +#define ucb1400_ts_resume NULL +#endif + +static struct platform_driver ucb1400_ts_driver = { + .probe = ucb1400_ts_probe, + .remove = ucb1400_ts_remove, + .resume = ucb1400_ts_resume, + .driver = { + .name = "ucb1400_ts", + }, }; static int __init ucb1400_ts_init(void) { - return driver_register(&ucb1400_ts_driver); + return platform_driver_register(&ucb1400_ts_driver); } static void __exit ucb1400_ts_exit(void) { - driver_unregister(&ucb1400_ts_driver); + platform_driver_unregister(&ucb1400_ts_driver); } module_param(adcsync, bool, 0444); MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); module_param(ts_delay, int, 0444); -MODULE_PARM_DESC(ts_delay, "Delay between panel setup and position read. Default = 55us."); +MODULE_PARM_DESC(ts_delay, "Delay between panel setup and" + " position read. Default = 55us."); module_param(ts_delay_pressure, int, 0444); MODULE_PARM_DESC(ts_delay_pressure, - "delay between panel setup and pressure read. Default = 0us."); + "delay between panel setup and pressure read." + " Default = 0us."); module_init(ucb1400_ts_init); module_exit(ucb1400_ts_exit); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 883e7ea31de..371d22a98f1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -50,6 +50,15 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is handled by the leds-pasic3 and ds1wm drivers. +config UCB1400_CORE + tristate "Philips UCB1400 Core driver" + help + This enables support for the Philips UCB1400 core functions. + The UCB1400 is an AC97 audio codec. + + To compile this driver as a module, choose M here: the + module will be called ucb1400_core. + config MFD_TC6393XB bool "Support Toshiba TC6393XB" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 33daa2f45dd..f7cfd5b4119 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o ifeq ($(CONFIG_SA1100_ASSABET),y) obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o endif +obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c new file mode 100644 index 00000000000..178159e264c --- /dev/null +++ b/drivers/mfd/ucb1400_core.c @@ -0,0 +1,106 @@ +/* + * Core functions for: + * Philips UCB1400 multifunction chip + * + * Based on ucb1400_ts.c: + * Author: Nicolas Pitre + * Created: September 25, 2006 + * Copyright: MontaVista Software, Inc. + * + * Spliting done by: Marek Vasut + * If something doesnt work and it worked before spliting, e-mail me, + * dont bother Nicolas please ;-) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This code is heavily based on ucb1x00-*.c copyrighted by Russell King + * covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has + * been made separate from ucb1x00-core/ucb1x00-ts on Russell's request. + */ + +#include +#include + +static int ucb1400_core_probe(struct device *dev) +{ + int err; + struct ucb1400 *ucb; + struct ucb1400_ts ucb_ts; + struct snd_ac97 *ac97; + + memset(&ucb_ts, 0, sizeof(ucb_ts)); + + ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); + if (!ucb) { + err = -ENOMEM; + goto err; + } + + dev_set_drvdata(dev, ucb); + + ac97 = to_ac97_t(dev); + + ucb_ts.id = ucb1400_reg_read(ac97, UCB_ID); + if (ucb_ts.id != UCB_ID_1400) { + err = -ENODEV; + goto err0; + } + + /* TOUCHSCREEN */ + ucb_ts.ac97 = ac97; + ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1); + if (!ucb->ucb1400_ts) { + err = -ENOMEM; + goto err0; + } + err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts, + sizeof(ucb_ts)); + if (err) + goto err1; + err = platform_device_add(ucb->ucb1400_ts); + if (err) + goto err1; + + return 0; + +err1: + platform_device_put(ucb->ucb1400_ts); +err0: + kfree(ucb); +err: + return err; +} + +static int ucb1400_core_remove(struct device *dev) +{ + struct ucb1400 *ucb = dev_get_drvdata(dev); + + platform_device_unregister(ucb->ucb1400_ts); + kfree(ucb); + return 0; +} + +static struct device_driver ucb1400_core_driver = { + .name = "ucb1400_core", + .bus = &ac97_bus_type, + .probe = ucb1400_core_probe, + .remove = ucb1400_core_remove, +}; + +static int __init ucb1400_core_init(void) +{ + return driver_register(&ucb1400_core_driver); +} + +static void __exit ucb1400_core_exit(void) +{ + driver_unregister(&ucb1400_core_driver); +} + +module_init(ucb1400_core_init); +module_exit(ucb1400_core_exit); + +MODULE_DESCRIPTION("Philips UCB1400 driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9326d61bf64c4293f834e86c11f52db5be9798d6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 21 Aug 2008 13:46:25 +0200 Subject: Revert "reduce tlb/cache flush times of agpgart memory allocation" This reverts commit 466ae837424dcc538b1af2a0eaf53be32edcdbe7. --- drivers/char/agp/agp.h | 4 ---- drivers/char/agp/generic.c | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 395168fb17e..81e14bea54b 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -30,10 +30,6 @@ #define _AGP_BACKEND_PRIV_H 1 #include /* for flush_agp_cache() */ -#ifndef map_page_into_agp_noflush -#define map_page_into_agp_noflush(page) map_page_into_agp(page) -#define map_page_into_agp_global_flush() -#endif #define PFX "agpgart: " diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index bf239b8ecac..eaa1a355bb3 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -274,7 +274,6 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, new->memory[i] = virt_to_gart(addr); new->page_count++; } - map_page_into_agp_global_flush(); new->bridge = bridge; return new; @@ -1187,8 +1186,7 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge) if (page == NULL) return NULL; - /* agp_allocate_memory will do flush */ - map_page_into_agp_noflush(page); + map_page_into_agp(page); get_page(page); atomic_inc(&agp_bridge->current_memory_agp); -- cgit v1.2.3 From 37acee10f49cf3caa323e05675e8dc9221ef4fd8 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 21 Aug 2008 10:46:11 +0800 Subject: agp: generic_alloc_pages() Add agp_generic_alloc_pages(), it uses new pageattr array interface API. Signed-off-by: Dave Airlie Signed-off-by: Shaohua Li Signed-off-by: Ingo Molnar --- drivers/char/agp/agp.h | 3 +++ drivers/char/agp/generic.c | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/char/agp/intel-agp.c | 14 ++++++++++++++ 3 files changed, 59 insertions(+) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 81e14bea54b..55d2c9d1417 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -116,6 +116,7 @@ struct agp_bridge_driver { struct agp_memory *(*alloc_by_type) (size_t, int); void (*free_by_type)(struct agp_memory *); void *(*agp_alloc_page)(struct agp_bridge_data *); + int (*agp_alloc_pages)(struct agp_bridge_data *, struct agp_memory *, size_t); void (*agp_destroy_page)(void *, int flags); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); @@ -274,6 +275,8 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type); struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); void agp_generic_free_by_type(struct agp_memory *curr); void *agp_generic_alloc_page(struct agp_bridge_data *bridge); +int agp_generic_alloc_pages(struct agp_bridge_data *agp_bridge, + struct agp_memory *memory, size_t page_count); void agp_generic_destroy_page(void *addr, int flags); void agp_free_key(int key); int agp_num_entries(void); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index eaa1a355bb3..13a5577ee43 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -264,6 +264,15 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, if (new == NULL) return NULL; + if (bridge->driver->agp_alloc_pages) { + if (bridge->driver->agp_alloc_pages(bridge, new, page_count)) { + agp_free_memory(new); + return NULL; + } + new->bridge = bridge; + return new; + } + for (i = 0; i < page_count; i++) { void *addr = bridge->driver->agp_alloc_page(bridge); @@ -1178,6 +1187,39 @@ EXPORT_SYMBOL(agp_generic_alloc_user); * against a maximum value. */ +int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages) +{ + struct page * page; + int i, ret = -ENOMEM; + + for (i = 0; i < num_pages; i++) { + page = alloc_page(GFP_KERNEL | GFP_DMA32); + /* agp_free_memory() needs gart address */ + if (page == NULL) + goto out; + +#ifndef CONFIG_X86 + map_page_into_agp(page); +#endif + get_page(page); + atomic_inc(&agp_bridge->current_memory_agp); + + /* set_memory_array_uc() needs virtual address */ + mem->memory[i] = (unsigned long)page_address(page); + mem->page_count++; + } + +#ifdef CONFIG_X86 + set_memory_array_uc(mem->memory, num_pages); +#endif + ret = 0; +out: + for (i = 0; i < mem->page_count; i++) + mem->memory[i] = virt_to_gart((void *)mem->memory[i]); + return ret; +} +EXPORT_SYMBOL(agp_generic_alloc_pages); + void *agp_generic_alloc_page(struct agp_bridge_data *bridge) { struct page * page; diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index df702642ab8..2cff0976a66 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1703,6 +1703,7 @@ static const struct agp_bridge_driver intel_generic_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1728,6 +1729,7 @@ static const struct agp_bridge_driver intel_810_driver = { .alloc_by_type = intel_i810_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1752,6 +1754,7 @@ static const struct agp_bridge_driver intel_815_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1777,6 +1780,7 @@ static const struct agp_bridge_driver intel_830_driver = { .alloc_by_type = intel_i830_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i830_chipset_flush, @@ -1802,6 +1806,7 @@ static const struct agp_bridge_driver intel_820_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1826,6 +1831,7 @@ static const struct agp_bridge_driver intel_830mp_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1850,6 +1856,7 @@ static const struct agp_bridge_driver intel_840_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1874,6 +1881,7 @@ static const struct agp_bridge_driver intel_845_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .chipset_flush = intel_i830_chipset_flush, @@ -1899,6 +1907,7 @@ static const struct agp_bridge_driver intel_850_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1923,6 +1932,7 @@ static const struct agp_bridge_driver intel_860_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1948,6 +1958,7 @@ static const struct agp_bridge_driver intel_915_driver = { .alloc_by_type = intel_i830_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, @@ -1974,6 +1985,7 @@ static const struct agp_bridge_driver intel_i965_driver = { .alloc_by_type = intel_i830_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, @@ -1999,6 +2011,7 @@ static const struct agp_bridge_driver intel_7505_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -2024,6 +2037,7 @@ static const struct agp_bridge_driver intel_g33_driver = { .alloc_by_type = intel_i830_alloc_by_type, .free_by_type = intel_i810_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, -- cgit v1.2.3 From bd07928c0f9c3dbe53baf28eae4de4c579772c4c Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 21 Aug 2008 10:46:17 +0800 Subject: agp: add agp_generic_destroy_pages() Add agp_generic_destroy_pages(), it uses new pageattr array interface API. Signed-off-by: Dave Airlie Signed-off-by: Shaohua Li Signed-off-by: Ingo Molnar --- drivers/char/agp/agp.h | 2 ++ drivers/char/agp/generic.c | 55 +++++++++++++++++++++++++++++++++++++------- drivers/char/agp/intel-agp.c | 14 +++++++++++ 3 files changed, 63 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 55d2c9d1417..338b0b4dce9 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -118,6 +118,7 @@ struct agp_bridge_driver { void *(*agp_alloc_page)(struct agp_bridge_data *); int (*agp_alloc_pages)(struct agp_bridge_data *, struct agp_memory *, size_t); void (*agp_destroy_page)(void *, int flags); + void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); }; @@ -278,6 +279,7 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge); int agp_generic_alloc_pages(struct agp_bridge_data *agp_bridge, struct agp_memory *memory, size_t page_count); void agp_generic_destroy_page(void *addr, int flags); +void agp_generic_destroy_pages(struct agp_memory *memory); void agp_free_key(int key); int agp_num_entries(void); u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 13a5577ee43..dd370be00d6 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -201,14 +201,22 @@ void agp_free_memory(struct agp_memory *curr) return; } if (curr->page_count != 0) { - for (i = 0; i < curr->page_count; i++) { - curr->memory[i] = (unsigned long)gart_to_virt(curr->memory[i]); - curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], - AGP_PAGE_DESTROY_UNMAP); - } - for (i = 0; i < curr->page_count; i++) { - curr->bridge->driver->agp_destroy_page((void *)curr->memory[i], - AGP_PAGE_DESTROY_FREE); + if (curr->bridge->driver->agp_destroy_pages) { + curr->bridge->driver->agp_destroy_pages(curr); + } else { + + for (i = 0; i < curr->page_count; i++) { + curr->memory[i] = (unsigned long)gart_to_virt( + curr->memory[i]); + curr->bridge->driver->agp_destroy_page( + (void *)curr->memory[i], + AGP_PAGE_DESTROY_UNMAP); + } + for (i = 0; i < curr->page_count; i++) { + curr->bridge->driver->agp_destroy_page( + (void *)curr->memory[i], + AGP_PAGE_DESTROY_FREE); + } } } agp_free_key(curr->key); @@ -1236,6 +1244,37 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge) } EXPORT_SYMBOL(agp_generic_alloc_page); +void agp_generic_destroy_pages(struct agp_memory *mem) +{ + int i; + void *addr; + struct page *page; + + if (!mem) + return; + + for (i = 0; i < mem->page_count; i++) + mem->memory[i] = (unsigned long)gart_to_virt(mem->memory[i]); + +#ifdef CONFIG_X86 + set_memory_array_wb(mem->memory, mem->page_count); +#endif + + for (i = 0; i < mem->page_count; i++) { + addr = (void *)mem->memory[i]; + page = virt_to_page(addr); + +#ifndef CONFIG_X86 + unmap_page_from_agp(page); +#endif + + put_page(page); + free_page((unsigned long)addr); + atomic_dec(&agp_bridge->current_memory_agp); + mem->memory[i] = 0; + } +} +EXPORT_SYMBOL(agp_generic_destroy_pages); void agp_generic_destroy_page(void *addr, int flags) { diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 2cff0976a66..290a1cf6392 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1705,6 +1705,7 @@ static const struct agp_bridge_driver intel_generic_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1731,6 +1732,7 @@ static const struct agp_bridge_driver intel_810_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1756,6 +1758,7 @@ static const struct agp_bridge_driver intel_815_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1782,6 +1785,7 @@ static const struct agp_bridge_driver intel_830_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i830_chipset_flush, }; @@ -1808,6 +1812,7 @@ static const struct agp_bridge_driver intel_820_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1833,6 +1838,7 @@ static const struct agp_bridge_driver intel_830mp_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1858,6 +1864,7 @@ static const struct agp_bridge_driver intel_840_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1883,6 +1890,7 @@ static const struct agp_bridge_driver intel_845_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .chipset_flush = intel_i830_chipset_flush, }; @@ -1909,6 +1917,7 @@ static const struct agp_bridge_driver intel_850_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1934,6 +1943,7 @@ static const struct agp_bridge_driver intel_860_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -1960,6 +1970,7 @@ static const struct agp_bridge_driver intel_915_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, }; @@ -1987,6 +1998,7 @@ static const struct agp_bridge_driver intel_i965_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, }; @@ -2013,6 +2025,7 @@ static const struct agp_bridge_driver intel_7505_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -2039,6 +2052,7 @@ static const struct agp_bridge_driver intel_g33_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, }; -- cgit v1.2.3 From 5f310b63781f6777bf4e812570560ec0f8ea42d8 Mon Sep 17 00:00:00 2001 From: Rene Herman Date: Thu, 21 Aug 2008 19:15:46 +0200 Subject: agp: enable optimized agp_alloc_pages methods The pageattr-array patch that you currently have in tip/master only enables it for intel-agp, not the others. The attached enables it for all drivers currently directly using agp_generic_alloc_page() and agp_generic_destroy_page() (ocal driver is amd-k7-agp). The new agp_generic_alloc_pages() interface uses the also new pageattr array interface API. This makes all AGP drivers that up to now used generic_{alloc,destroy}_page() use it. Signed-off-by: Rene Herman Signed-off-by: Ingo Molnar --- drivers/char/agp/alpha-agp.c | 2 ++ drivers/char/agp/amd-k7-agp.c | 2 ++ drivers/char/agp/amd64-agp.c | 2 ++ drivers/char/agp/ati-agp.c | 2 ++ drivers/char/agp/efficeon-agp.c | 2 ++ drivers/char/agp/hp-agp.c | 2 ++ drivers/char/agp/i460-agp.c | 2 ++ drivers/char/agp/nvidia-agp.c | 2 ++ drivers/char/agp/parisc-agp.c | 2 ++ drivers/char/agp/sis-agp.c | 2 ++ drivers/char/agp/sworks-agp.c | 2 ++ drivers/char/agp/uninorth-agp.c | 4 ++++ drivers/char/agp/via-agp.c | 4 ++++ 13 files changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index 5da89f6c6c2..5ea4da8e995 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -143,7 +143,9 @@ struct agp_bridge_driver alpha_core_agp_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index e280531843b..603a986e96a 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -386,7 +386,9 @@ static const struct agp_bridge_driver amd_irongate_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 7495c522d8e..2812ee2b165 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -224,7 +224,9 @@ static const struct agp_bridge_driver amd_8151_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 6ecbcafb34b..ae2791b926b 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -418,7 +418,9 @@ static const struct agp_bridge_driver ati_generic_bridge = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 8ca6f262ef8..453543a1f29 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -335,7 +335,9 @@ static const struct agp_bridge_driver efficeon_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 80d7317f85c..183ac3fe44f 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -435,7 +435,9 @@ const struct agp_bridge_driver hp_zx1_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, }; diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index e587eebebc6..10da687d131 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -575,7 +575,9 @@ const struct agp_bridge_driver intel_i460_driver = { .insert_memory = i460_insert_memory_small_io_page, .remove_memory = i460_remove_memory_small_io_page, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, #endif .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index eaceb61ba2d..dc70d377181 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -312,7 +312,9 @@ static const struct agp_bridge_driver nvidia_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 8c42dcc5958..f2492ecf082 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -224,7 +224,9 @@ static const struct agp_bridge_driver parisc_agp_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, }; diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 2587ef96a96..6c3837a0184 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -140,7 +140,9 @@ static struct agp_bridge_driver sis_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 2fb27fe4c10..6224df8b7f0 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -437,7 +437,9 @@ static const struct agp_bridge_driver sworks_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index eef72709ec5..2accc974470 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -509,7 +509,9 @@ const struct agp_bridge_driver uninorth_agp_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, }; @@ -534,7 +536,9 @@ const struct agp_bridge_driver u3_agp_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_paged = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, .needs_scratch_page = true, diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 7b36476dff4..9f4d49e1b59 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -190,7 +190,9 @@ static const struct agp_bridge_driver via_agp3_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; @@ -214,7 +216,9 @@ static const struct agp_bridge_driver via_driver = { .alloc_by_type = agp_generic_alloc_by_type, .free_by_type = agp_generic_free_by_type, .agp_alloc_page = agp_generic_alloc_page, + .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -- cgit v1.2.3 From 38e3b0d86eaa0bf90a74677b6d6c442ec66daa0c Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 16 Jul 2008 11:44:18 -0300 Subject: rtl818x: merge tx/rx descriptor flags Tx/Rx descriptor flags are common between rtl818x devices, only with additions for newer chips, thus use same flags in the code. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: John W. Linville --- drivers/net/wireless/rtl8180.h | 31 ------------------------------- drivers/net/wireless/rtl8180_dev.c | 34 +++++++++++++++++----------------- drivers/net/wireless/rtl8187.h | 6 ------ drivers/net/wireless/rtl8187_dev.c | 10 +++++----- drivers/net/wireless/rtl818x.h | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl8180.h b/drivers/net/wireless/rtl8180.h index 082a11f93be..8721282a818 100644 --- a/drivers/net/wireless/rtl8180.h +++ b/drivers/net/wireless/rtl8180.h @@ -24,20 +24,6 @@ #define ANAPARAM_PWR1_SHIFT 20 #define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) -enum rtl8180_tx_desc_flags { - RTL8180_TX_DESC_FLAG_NO_ENC = (1 << 15), - RTL8180_TX_DESC_FLAG_TX_OK = (1 << 15), - RTL8180_TX_DESC_FLAG_SPLCP = (1 << 16), - RTL8180_TX_DESC_FLAG_RX_UNDER = (1 << 16), - RTL8180_TX_DESC_FLAG_MOREFRAG = (1 << 17), - RTL8180_TX_DESC_FLAG_CTS = (1 << 18), - RTL8180_TX_DESC_FLAG_RTS = (1 << 23), - RTL8180_TX_DESC_FLAG_LS = (1 << 28), - RTL8180_TX_DESC_FLAG_FS = (1 << 29), - RTL8180_TX_DESC_FLAG_DMA = (1 << 30), - RTL8180_TX_DESC_FLAG_OWN = (1 << 31) -}; - struct rtl8180_tx_desc { __le32 flags; __le16 rts_duration; @@ -52,23 +38,6 @@ struct rtl8180_tx_desc { u32 reserved[2]; } __attribute__ ((packed)); -enum rtl8180_rx_desc_flags { - RTL8180_RX_DESC_FLAG_ICV_ERR = (1 << 12), - RTL8180_RX_DESC_FLAG_CRC32_ERR = (1 << 13), - RTL8180_RX_DESC_FLAG_PM = (1 << 14), - RTL8180_RX_DESC_FLAG_RX_ERR = (1 << 15), - RTL8180_RX_DESC_FLAG_BCAST = (1 << 16), - RTL8180_RX_DESC_FLAG_PAM = (1 << 17), - RTL8180_RX_DESC_FLAG_MCAST = (1 << 18), - RTL8180_RX_DESC_FLAG_SPLCP = (1 << 25), - RTL8180_RX_DESC_FLAG_FOF = (1 << 26), - RTL8180_RX_DESC_FLAG_DMA_FAIL = (1 << 27), - RTL8180_RX_DESC_FLAG_LS = (1 << 28), - RTL8180_RX_DESC_FLAG_FS = (1 << 29), - RTL8180_RX_DESC_FLAG_EOR = (1 << 30), - RTL8180_RX_DESC_FLAG_OWN = (1 << 31) -}; - struct rtl8180_rx_desc { __le32 flags; __le32 flags2; diff --git a/drivers/net/wireless/rtl8180_dev.c b/drivers/net/wireless/rtl8180_dev.c index b7172a12c05..861c76a65d6 100644 --- a/drivers/net/wireless/rtl8180_dev.c +++ b/drivers/net/wireless/rtl8180_dev.c @@ -110,12 +110,12 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; u32 flags = le32_to_cpu(entry->flags); - if (flags & RTL8180_RX_DESC_FLAG_OWN) + if (flags & RTL818X_RX_DESC_FLAG_OWN) return; - if (unlikely(flags & (RTL8180_RX_DESC_FLAG_DMA_FAIL | - RTL8180_RX_DESC_FLAG_FOF | - RTL8180_RX_DESC_FLAG_RX_ERR))) + if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | + RTL818X_RX_DESC_FLAG_FOF | + RTL818X_RX_DESC_FLAG_RX_ERR))) goto done; else { u32 flags2 = le32_to_cpu(entry->flags2); @@ -140,7 +140,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.band = dev->conf.channel->band; rx_status.mactime = le64_to_cpu(entry->tsft); rx_status.flag |= RX_FLAG_TSFT; - if (flags & RTL8180_RX_DESC_FLAG_CRC32_ERR) + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; ieee80211_rx_irqsafe(dev, skb, &rx_status); @@ -154,10 +154,10 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) done: entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); - entry->flags = cpu_to_le32(RTL8180_RX_DESC_FLAG_OWN | + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); if (priv->rx_idx == 31) - entry->flags |= cpu_to_le32(RTL8180_RX_DESC_FLAG_EOR); + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); priv->rx_idx = (priv->rx_idx + 1) % 32; } } @@ -173,7 +173,7 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) struct ieee80211_tx_info *info; u32 flags = le32_to_cpu(entry->flags); - if (flags & RTL8180_TX_DESC_FLAG_OWN) + if (flags & RTL818X_TX_DESC_FLAG_OWN) return; ring->idx = (ring->idx + 1) % ring->entries; @@ -185,7 +185,7 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) memset(&info->status, 0, sizeof(info->status)); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - if (flags & RTL8180_TX_DESC_FLAG_TX_OK) + if (flags & RTL818X_TX_DESC_FLAG_TX_OK) info->flags |= IEEE80211_TX_STAT_ACK; else info->status.excessive_retries = 1; @@ -252,20 +252,20 @@ static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - tx_flags = RTL8180_TX_DESC_FLAG_OWN | RTL8180_TX_DESC_FLAG_FS | - RTL8180_TX_DESC_FLAG_LS | + tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | + RTL818X_TX_DESC_FLAG_LS | (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | skb->len; if (priv->r8185) - tx_flags |= RTL8180_TX_DESC_FLAG_DMA | - RTL8180_TX_DESC_FLAG_NO_ENC; + tx_flags |= RTL818X_TX_DESC_FLAG_DMA | + RTL818X_TX_DESC_FLAG_NO_ENC; if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) { - tx_flags |= RTL8180_TX_DESC_FLAG_RTS; + tx_flags |= RTL818X_TX_DESC_FLAG_RTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) { - tx_flags |= RTL8180_TX_DESC_FLAG_CTS; + tx_flags |= RTL818X_TX_DESC_FLAG_CTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } @@ -446,10 +446,10 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); entry->rx_buf = cpu_to_le32(*mapping); - entry->flags = cpu_to_le32(RTL8180_RX_DESC_FLAG_OWN | + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); } - entry->flags |= cpu_to_le32(RTL8180_RX_DESC_FLAG_EOR); + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); return 0; } diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h index 5a9515c9996..e82bb4d289e 100644 --- a/drivers/net/wireless/rtl8187.h +++ b/drivers/net/wireless/rtl8187.h @@ -58,12 +58,6 @@ struct rtl8187b_rx_hdr { /* {rtl8187,rtl8187b}_tx_info is in skb */ -/* Tx flags are common between rtl8187 and rtl8187b */ -#define RTL8187_TX_FLAG_NO_ENCRYPT (1 << 15) -#define RTL8187_TX_FLAG_MORE_FRAG (1 << 17) -#define RTL8187_TX_FLAG_CTS (1 << 18) -#define RTL8187_TX_FLAG_RTS (1 << 23) - struct rtl8187_tx_hdr { __le32 flags; __le16 rts_duration; diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index ca5deb6244e..060a2650535 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -187,18 +187,18 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) } flags = skb->len; - flags |= RTL8187_TX_FLAG_NO_ENCRYPT; + flags |= RTL818X_TX_DESC_FLAG_NO_ENC; flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) - flags |= RTL8187_TX_FLAG_MORE_FRAG; + flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) { - flags |= RTL8187_TX_FLAG_RTS; + flags |= RTL818X_TX_DESC_FLAG_RTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; rts_dur = ieee80211_rts_duration(dev, priv->vif, skb->len, info); } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) { - flags |= RTL8187_TX_FLAG_CTS; + flags |= RTL818X_TX_DESC_FLAG_CTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; } @@ -354,7 +354,7 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.flag |= RX_FLAG_TSFT; - if (flags & (1 << 13)) + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; ieee80211_rx_irqsafe(dev, skb, &rx_status); diff --git a/drivers/net/wireless/rtl818x.h b/drivers/net/wireless/rtl818x.h index 00900fe16fc..3538b15211b 100644 --- a/drivers/net/wireless/rtl818x.h +++ b/drivers/net/wireless/rtl818x.h @@ -193,4 +193,39 @@ struct rtl818x_rf_ops { void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *); }; +/* Tx/Rx flags are common between RTL818X chips */ + +enum rtl818x_tx_desc_flags { + RTL818X_TX_DESC_FLAG_NO_ENC = (1 << 15), + RTL818X_TX_DESC_FLAG_TX_OK = (1 << 15), + RTL818X_TX_DESC_FLAG_SPLCP = (1 << 16), + RTL818X_TX_DESC_FLAG_RX_UNDER = (1 << 16), + RTL818X_TX_DESC_FLAG_MOREFRAG = (1 << 17), + RTL818X_TX_DESC_FLAG_CTS = (1 << 18), + RTL818X_TX_DESC_FLAG_RTS = (1 << 23), + RTL818X_TX_DESC_FLAG_LS = (1 << 28), + RTL818X_TX_DESC_FLAG_FS = (1 << 29), + RTL818X_TX_DESC_FLAG_DMA = (1 << 30), + RTL818X_TX_DESC_FLAG_OWN = (1 << 31) +}; + +enum rtl818x_rx_desc_flags { + RTL818X_RX_DESC_FLAG_ICV_ERR = (1 << 12), + RTL818X_RX_DESC_FLAG_CRC32_ERR = (1 << 13), + RTL818X_RX_DESC_FLAG_PM = (1 << 14), + RTL818X_RX_DESC_FLAG_RX_ERR = (1 << 15), + RTL818X_RX_DESC_FLAG_BCAST = (1 << 16), + RTL818X_RX_DESC_FLAG_PAM = (1 << 17), + RTL818X_RX_DESC_FLAG_MCAST = (1 << 18), + RTL818X_RX_DESC_FLAG_QOS = (1 << 19), /* RTL8187(B) only */ + RTL818X_RX_DESC_FLAG_TRSW = (1 << 24), /* RTL8187(B) only */ + RTL818X_RX_DESC_FLAG_SPLCP = (1 << 25), + RTL818X_RX_DESC_FLAG_FOF = (1 << 26), + RTL818X_RX_DESC_FLAG_DMA_FAIL = (1 << 27), + RTL818X_RX_DESC_FLAG_LS = (1 << 28), + RTL818X_RX_DESC_FLAG_FS = (1 << 29), + RTL818X_RX_DESC_FLAG_EOR = (1 << 30), + RTL818X_RX_DESC_FLAG_OWN = (1 << 31) +}; + #endif /* RTL818X_H */ -- cgit v1.2.3 From 1556c0f22df77800d2e99342ce354a4ce94c5a0f Mon Sep 17 00:00:00 2001 From: Brian Cavagnolo Date: Mon, 21 Jul 2008 11:02:46 -0700 Subject: libertas: support boot commands to write persistent firmware and bootloader Add locking and non-locking versions of if_usb_prog_firmware to support programming firmware after and before driver bring-up respectively. Add more suitable error codes for firmware programming process. Add capability checks for persistent features before attempting to use them. Based on patches from Brajesh Dave and Priyank Singh. Signed-off-by: Brian Cavagnolo Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cmd.c | 21 +++++-- drivers/net/wireless/libertas/defs.h | 6 +- drivers/net/wireless/libertas/if_usb.c | 112 +++++++++++++++++++++++++++------ drivers/net/wireless/libertas/if_usb.h | 5 ++ 4 files changed, 119 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 75427e61898..af5fd709887 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1033,9 +1033,9 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, return ret; } -int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) +static int __lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) { int ret; @@ -1054,6 +1054,19 @@ int lbs_mesh_config_send(struct lbs_private *priv, return ret; } +int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + + if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) + return -EOPNOTSUPP; + + ret = __lbs_mesh_config_send(priv, cmd, action, type); + return ret; +} + /* This function is the CMD_MESH_CONFIG legacy function. It only handles the * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG * are all handled by preparing a struct cmd_ds_mesh_config and passing it to @@ -1095,7 +1108,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan) action, priv->mesh_tlv, chan, escape_essid(priv->mesh_ssid, priv->mesh_ssid_len)); - return lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); + return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); } static int lbs_cmd_bcn_ctrl(struct lbs_private * priv, diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 12e687550bc..4b2428ac222 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -243,6 +243,9 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in #define CMD_F_HOSTCMD (1 << 0) #define FW_CAPINFO_WPA (1 << 0) +#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) +#define FW_CAPINFO_BOOT2_UPGRADE (1<<14) +#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) #define KEY_LEN_WPA_AES 16 #define KEY_LEN_WPA_TKIP 32 @@ -316,7 +319,8 @@ enum PS_STATE { enum DNLD_STATE { DNLD_RES_RECEIVED, DNLD_DATA_SENT, - DNLD_CMD_SENT + DNLD_CMD_SENT, + DNLD_BOOTCMD_SENT, }; /** LBS_MEDIA_STATE */ diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 632c291404a..b5013ce31b9 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -39,7 +39,10 @@ MODULE_DEVICE_TABLE(usb, if_usb_table); static void if_usb_receive(struct urb *urb); static void if_usb_receive_fwload(struct urb *urb); -static int if_usb_prog_firmware(struct if_usb_card *cardp); +static int __if_usb_prog_firmware(struct if_usb_card *cardp, + const char *fwname, int cmd); +static int if_usb_prog_firmware(struct if_usb_card *cardp, + const char *fwname, int cmd); static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, uint8_t *payload, uint16_t nb); static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, @@ -66,10 +69,10 @@ static void if_usb_write_bulk_callback(struct urb *urb) lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", urb->actual_length); - /* Used for both firmware TX and regular TX. priv isn't - * valid at firmware load time. + /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not + * passed up to the lbs level. */ - if (priv) + if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) lbs_host_to_card_done(priv); } else { /* print the failure status number for debug */ @@ -231,7 +234,7 @@ static int if_usb_probe(struct usb_interface *intf, } /* Upload firmware */ - if (if_usb_prog_firmware(cardp)) { + if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) { lbs_deb_usbd(&udev->dev, "FW upload failed\n"); goto err_prog_firmware; } @@ -510,7 +513,7 @@ static void if_usb_receive_fwload(struct urb *urb) if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { kfree_skb(skb); if_usb_submit_rx_urb_fwload(cardp); - cardp->bootcmdresp = 1; + cardp->bootcmdresp = BOOT_CMD_RESP_OK; lbs_deb_usbd(&cardp->udev->dev, "Received valid boot command response\n"); return; @@ -526,7 +529,9 @@ static void if_usb_receive_fwload(struct urb *urb) lbs_pr_info("boot cmd response wrong magic number (0x%x)\n", le32_to_cpu(bootcmdresp.magic)); } - } else if (bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) { + } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { lbs_pr_info("boot cmd response cmd_tag error (%d)\n", bootcmdresp.cmd); } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { @@ -564,8 +569,8 @@ static void if_usb_receive_fwload(struct urb *urb) kfree_skb(skb); - /* reschedule timer for 200ms hence */ - mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); + /* Give device 5s to either write firmware to its RAM or eeprom */ + mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); if (cardp->fwfinalblk) { cardp->fwdnldover = 1; @@ -809,7 +814,54 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) } -static int if_usb_prog_firmware(struct if_usb_card *cardp) +/** +* @brief This function programs the firmware subject to cmd +* +* @param cardp the if_usb_card descriptor +* fwname firmware or boot2 image file name +* cmd either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW, +* or BOOT_CMD_UPDATE_BOOT2. +* @return 0 or error code +*/ +static int if_usb_prog_firmware(struct if_usb_card *cardp, + const char *fwname, int cmd) +{ + struct lbs_private *priv = cardp->priv; + unsigned long flags, caps; + int ret; + + caps = priv->fwcapinfo; + if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) || + ((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE))) + return -EOPNOTSUPP; + + /* Ensure main thread is idle. */ + spin_lock_irqsave(&priv->driver_lock, flags); + while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + if (wait_event_interruptible(priv->waitq, + (priv->cur_cmd == NULL && + priv->dnld_sent == DNLD_RES_RECEIVED))) { + return -ERESTARTSYS; + } + spin_lock_irqsave(&priv->driver_lock, flags); + } + priv->dnld_sent = DNLD_BOOTCMD_SENT; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = __if_usb_prog_firmware(cardp, fwname, cmd); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->dnld_sent = DNLD_RES_RECEIVED; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + wake_up_interruptible(&priv->waitq); + + return ret; +} + +static int __if_usb_prog_firmware(struct if_usb_card *cardp, + const char *fwname, int cmd) { int i = 0; static int reset_count = 10; @@ -817,20 +869,32 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp) lbs_deb_enter(LBS_DEB_USB); - if ((ret = request_firmware(&cardp->fw, lbs_fw_name, - &cardp->udev->dev)) < 0) { + ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev); + if (ret < 0) { lbs_pr_err("request_firmware() failed with %#x\n", ret); - lbs_pr_err("firmware %s not found\n", lbs_fw_name); + lbs_pr_err("firmware %s not found\n", fwname); goto done; } - if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) + if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { + ret = -EINVAL; goto release_fw; + } + + /* Cancel any pending usb business */ + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + + cardp->fwlastblksent = 0; + cardp->fwdnldover = 0; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + cardp->bootcmdresp = 0; restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); - ret = -1; + ret = -EIO; goto release_fw; } @@ -838,8 +902,7 @@ restart: do { int j = 0; i++; - /* Issue Boot command = 1, Boot from Download-FW */ - if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); + if_usb_issue_boot_command(cardp, cmd); /* wait for command response */ do { j++; @@ -847,12 +910,21 @@ restart: } while (cardp->bootcmdresp == 0 && j < 10); } while (cardp->bootcmdresp == 0 && i < 5); - if (cardp->bootcmdresp <= 0) { + if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { + /* Return to normal operation */ + ret = -EOPNOTSUPP; + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + if (if_usb_submit_rx_urb(cardp) < 0) + ret = -EIO; + goto release_fw; + } else if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } - return -1; + ret = -EIO; + goto release_fw; } i = 0; @@ -882,7 +954,7 @@ restart: } lbs_pr_info("FW download failure, time = %d ms\n", i * 100); - ret = -1; + ret = -EIO; goto release_fw; } diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h index 5771a83a43f..5ba0aee0eb2 100644 --- a/drivers/net/wireless/libertas/if_usb.h +++ b/drivers/net/wireless/libertas/if_usb.h @@ -30,6 +30,7 @@ struct bootcmd #define BOOT_CMD_RESP_OK 0x0001 #define BOOT_CMD_RESP_FAIL 0x0000 +#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 struct bootcmdresp { @@ -50,6 +51,10 @@ struct if_usb_card { uint8_t ep_in; uint8_t ep_out; + /* bootcmdresp == 0 means command is pending + * bootcmdresp < 0 means error + * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware + */ int8_t bootcmdresp; int ep_in_size; -- cgit v1.2.3 From 1ff41eb0d9a937957d481d4f058a91230851ae17 Mon Sep 17 00:00:00 2001 From: Brian Cavagnolo Date: Mon, 21 Jul 2008 11:03:16 -0700 Subject: libertas: add sysfs hooks to update boot2 and persistent firmware To use these features, copy the boot2 and firmware images to /lib/firmware and: echo > /sys/class/net/ethX/lbs_flash_boot2 echo > /sys/class/net/ethX/lbs_flash_fw Signed-off-by: Brian Cavagnolo Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_usb.c | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index b5013ce31b9..7b02d612b07 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -51,6 +51,62 @@ static void if_usb_free(struct if_usb_card *cardp); static int if_usb_submit_rx_urb(struct if_usb_card *cardp); static int if_usb_reset_device(struct if_usb_card *cardp); +/* sysfs hooks */ + +/** + * Set function to write firmware to device's persistent memory + */ +static ssize_t if_usb_firmware_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->priv; + struct if_usb_card *cardp = priv->card; + char fwname[FIRMWARE_NAME_MAX]; + int ret; + + sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */ + ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_FW); + if (ret == 0) + return count; + + return ret; +} + +/** + * lbs_flash_fw attribute to be exported per ethX interface through sysfs + * (/sys/class/net/ethX/lbs_flash_fw). Use this like so to write firmware to + * the device's persistent memory: + * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_fw + */ +static DEVICE_ATTR(lbs_flash_fw, 0200, NULL, if_usb_firmware_set); + +/** + * Set function to write firmware to device's persistent memory + */ +static ssize_t if_usb_boot2_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->priv; + struct if_usb_card *cardp = priv->card; + char fwname[FIRMWARE_NAME_MAX]; + int ret; + + sscanf(buf, "%29s", fwname); /* FIRMWARE_NAME_MAX - 1 = 29 */ + ret = if_usb_prog_firmware(cardp, fwname, BOOT_CMD_UPDATE_BOOT2); + if (ret == 0) + return count; + + return ret; +} + +/** + * lbs_flash_boot2 attribute to be exported per ethX interface through sysfs + * (/sys/class/net/ethX/lbs_flash_boot2). Use this like so to write firmware + * to the device's persistent memory: + * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_boot2 + */ +static DEVICE_ATTR(lbs_flash_boot2, 0200, NULL, if_usb_boot2_set); + /** * @brief call back function to handle the status of the URB * @param urb pointer to urb structure @@ -263,6 +319,12 @@ static int if_usb_probe(struct usb_interface *intf, usb_get_dev(udev); usb_set_intfdata(intf, cardp); + if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_fw)) + lbs_pr_err("cannot register lbs_flash_fw attribute\n"); + + if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2)) + lbs_pr_err("cannot register lbs_flash_boot2 attribute\n"); + return 0; err_start_card: @@ -288,6 +350,9 @@ static void if_usb_disconnect(struct usb_interface *intf) lbs_deb_enter(LBS_DEB_MAIN); + device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2); + device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_fw); + cardp->surprise_removed = 1; if (priv) { -- cgit v1.2.3 From 8e5f3d0aaa3ae5305613553f869727b0361cd472 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Thu, 17 Jul 2008 13:12:24 -0700 Subject: ath5k: add Mesh Point support This enables draft-802.11s Mesh Point operation. For that we need mesh beaconing. Tested with AR5212/AR5213 PCI card against Zydas and b43 mesh nodes. Signed-off-by: Andrey Yurovsky Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 4 +++- drivers/net/wireless/ath5k/hw.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index b20a45aa868..8bc9b92ad6a 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1432,7 +1432,8 @@ ath5k_beaconq_config(struct ath5k_softc *sc) ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); if (ret) return ret; - if (sc->opmode == IEEE80211_IF_TYPE_AP) { + if (sc->opmode == IEEE80211_IF_TYPE_AP || + sc->opmode == IEEE80211_IF_TYPE_MESH_POINT) { /* * Always burst out beacon and CAB traffic * (aifs = cwmin = cwmax = 0) @@ -2932,6 +2933,7 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, if (sc->opmode != IEEE80211_IF_TYPE_STA) rfilt |= AR5K_RX_FILTER_PROBEREQ; if (sc->opmode != IEEE80211_IF_TYPE_AP && + sc->opmode != IEEE80211_IF_TYPE_MESH_POINT && test_bit(ATH_STAT_PROMISC, sc->status)) rfilt |= AR5K_RX_FILTER_PROM; if (sc->opmode == IEEE80211_IF_TYPE_STA || diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index ad1a5b422c8..b335d332305 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -2387,6 +2387,7 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah) break; case IEEE80211_IF_TYPE_AP: + case IEEE80211_IF_TYPE_MESH_POINT: pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_NO_PSPOLL : 0); -- cgit v1.2.3 From b9740c2396851178b99c03c2260a0adfe7f52e36 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 23 Jul 2008 19:17:01 +0200 Subject: rt2x00mac: In error case stop netdev queue, free skb and return NETDEV_TX_OK It is not allowed to use NETDEV_TX_BUSY in tx path anymore. Signed-off-by: Daniel Wagner Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 43 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index d0650738863..4b9fde903cd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -37,6 +37,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, struct ieee80211_tx_info *rts_info; struct sk_buff *skb; int size; + int retval = 0; if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) size = sizeof(struct ieee80211_cts); @@ -44,9 +45,9 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, size = sizeof(struct ieee80211_rts); skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); - if (!skb) { + if (unlikely(!skb)) { WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); - return NETDEV_TX_BUSY; + return -ENOMEM; } skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); @@ -82,13 +83,13 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, frag_skb->data, size, tx_info, (struct ieee80211_rts *)(skb->data)); - if (rt2x00queue_write_tx_frame(queue, skb)) { + retval = rt2x00queue_write_tx_frame(queue, skb); + if (retval) { dev_kfree_skb_any(skb); WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); - return NETDEV_TX_BUSY; } - return NETDEV_TX_OK; + return retval; } int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) @@ -106,11 +107,8 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * Note that we can only stop the TX queues inside the TX path * due to possible race conditions in mac80211. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) { - ieee80211_stop_queues(hw); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } + if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + goto exit_fail; /* * Determine which queue to put packet on. @@ -141,26 +139,25 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if ((tx_info->flags & (IEEE80211_TX_CTL_USE_RTS_CTS | IEEE80211_TX_CTL_USE_CTS_PROTECT)) && !rt2x00dev->ops->hw->set_rts_threshold) { - if (rt2x00queue_available(queue) <= 1) { - ieee80211_stop_queue(rt2x00dev->hw, qid); - return NETDEV_TX_BUSY; - } - - if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) { - ieee80211_stop_queue(rt2x00dev->hw, qid); - return NETDEV_TX_BUSY; - } - } + if (rt2x00queue_available(queue) <= 1) + goto exit_fail; - if (rt2x00queue_write_tx_frame(queue, skb)) { - ieee80211_stop_queue(rt2x00dev->hw, qid); - return NETDEV_TX_BUSY; + if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) + goto exit_fail; } + if (rt2x00queue_write_tx_frame(queue, skb)) + goto exit_fail; + if (rt2x00queue_threshold(queue)) ieee80211_stop_queue(rt2x00dev->hw, qid); return NETDEV_TX_OK; + + exit_fail: + ieee80211_stop_queue(rt2x00dev->hw, qid); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(rt2x00mac_tx); -- cgit v1.2.3 From d7dc100374df0c21afd8a198336ecd7999697159 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 23 Jul 2008 13:17:35 +0200 Subject: Ath5k: unify resets There were 3 code copy and pastes of reset. Unify the resets and place in separate function. Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 124 +++++++++++++------------------------- 1 file changed, 42 insertions(+), 82 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 8bc9b92ad6a..e8564095b2f 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -162,7 +162,8 @@ static struct pci_driver ath5k_pci_driver = { * Prototypes - MAC 802.11 stack related functions */ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); -static int ath5k_reset(struct ieee80211_hw *hw); +static int ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel); +static int ath5k_reset_wake(struct ath5k_softc *sc); static int ath5k_start(struct ieee80211_hw *hw); static void ath5k_stop(struct ieee80211_hw *hw); static int ath5k_add_interface(struct ieee80211_hw *hw, @@ -977,9 +978,6 @@ ath5k_getchannels(struct ieee80211_hw *hw) static int ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) { - struct ath5k_hw *ah = sc->ah; - int ret; - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "(%u MHz) -> (%u MHz)\n", sc->curchan->center_freq, chan->center_freq); @@ -995,41 +993,7 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) * hardware at the new frequency, and then re-enable * the relevant bits of the h/w. */ - ath5k_hw_set_intr(ah, 0); /* disable interrupts */ - ath5k_txq_cleanup(sc); /* clear pending tx frames */ - ath5k_rx_stop(sc); /* turn off frame recv */ - ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); - if (ret) { - ATH5K_ERR(sc, "%s: unable to reset channel " - "(%u Mhz)\n", __func__, chan->center_freq); - return ret; - } - - ath5k_hw_set_txpower_limit(sc->ah, 0); - - /* - * Re-enable rx framework. - */ - ret = ath5k_rx_start(sc); - if (ret) { - ATH5K_ERR(sc, "%s: unable to restart recv logic\n", - __func__); - return ret; - } - - /* - * Change channels and update the h/w rate map - * if we're switching; e.g. 11a to 11b/g. - * - * XXX needed? - */ -/* ath5k_chan_change(sc, chan); */ - - ath5k_beacon_config(sc); - /* - * Re-enable interrupts. - */ - ath5k_hw_set_intr(ah, sc->imask); + return ath5k_reset(sc, true, true); } return 0; @@ -2217,36 +2181,13 @@ ath5k_init(struct ath5k_softc *sc) */ sc->curchan = sc->hw->conf.channel; sc->curband = &sc->sbands[sc->curchan->band]; - ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false); - if (ret) { - ATH5K_ERR(sc, "unable to reset hardware: %d\n", ret); - goto done; - } - /* - * This is needed only to setup initial state - * but it's best done after a reset. - */ - ath5k_hw_set_txpower_limit(sc->ah, 0); - - /* - * Setup the hardware after reset: the key cache - * is filled as needed and the receive engine is - * set going. Frame transmit is handled entirely - * in the frame output path; there's nothing to do - * here except setup the interrupt mask. - */ - ret = ath5k_rx_start(sc); - if (ret) - goto done; - - /* - * Enable interrupts. - */ sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; + ret = ath5k_reset(sc, false, false); + if (ret) + goto done; - ath5k_hw_set_intr(sc->ah, sc->imask); /* Set ack to be sent at low bit-rates */ ath5k_hw_set_ack_bitrate_high(sc->ah, false); @@ -2446,7 +2387,7 @@ ath5k_tasklet_reset(unsigned long data) { struct ath5k_softc *sc = (void *)data; - ath5k_reset(sc->hw); + ath5k_reset_wake(sc); } /* @@ -2469,7 +2410,7 @@ ath5k_calibrate(unsigned long data) * to load new gain values. */ ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n"); - ath5k_reset(sc->hw); + ath5k_reset_wake(sc); } if (ath5k_hw_phy_calibrate(ah, sc->curchan)) ATH5K_ERR(sc, "calibration of channel %u failed\n", @@ -2670,48 +2611,67 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } static int -ath5k_reset(struct ieee80211_hw *hw) +ath5k_reset(struct ath5k_softc *sc, bool stop, bool change_channel) { - struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; int ret; ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n"); - ath5k_hw_set_intr(ah, 0); - ath5k_txq_cleanup(sc); - ath5k_rx_stop(sc); - + if (stop) { + ath5k_hw_set_intr(ah, 0); + ath5k_txq_cleanup(sc); + ath5k_rx_stop(sc); + } ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); - if (unlikely(ret)) { + if (ret) { ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret); goto err; } + + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ ath5k_hw_set_txpower_limit(sc->ah, 0); ret = ath5k_rx_start(sc); - if (unlikely(ret)) { + if (ret) { ATH5K_ERR(sc, "can't start recv logic\n"); goto err; } + /* - * We may be doing a reset in response to an ioctl - * that changes the channel so update any state that - * might change as a result. + * Change channels and update the h/w rate map if we're switching; + * e.g. 11a to 11b/g. + * + * We may be doing a reset in response to an ioctl that changes the + * channel so update any state that might change as a result. * * XXX needed? */ /* ath5k_chan_change(sc, c); */ - ath5k_beacon_config(sc); - /* intrs are started by ath5k_beacon_config */ - ieee80211_wake_queues(hw); + ath5k_beacon_config(sc); + /* intrs are enabled by ath5k_beacon_config */ return 0; err: return ret; } +static int +ath5k_reset_wake(struct ath5k_softc *sc) +{ + int ret; + + ret = ath5k_reset(sc, true, true); + if (!ret) + ieee80211_wake_queues(sc->hw); + + return ret; +} + static int ath5k_start(struct ieee80211_hw *hw) { return ath5k_init(hw->priv); @@ -2820,7 +2780,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_unlock(&sc->lock); - return ath5k_reset(hw); + return ath5k_reset_wake(sc); unlock: mutex_unlock(&sc->lock); return ret; -- cgit v1.2.3 From 63266a653589e1a237527479f10212ea77ce7844 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 30 Jul 2008 17:12:58 +0200 Subject: ath5k: rates cleanup cleanup the rates structures used by ath5k. instead of separate driver and mac80211 rate structures we now setup a static ieee80211_rate array and use it directly. no conversion between two different rate structures has to be done any more. a lot of unused and confusing junk was deleted. renamed ath5k_getchannels into ath5k_setup_bands because this is what it does. rewrote it to copy the bitrates correctly for each band. this is necessary for running different hardware with the same driver (e.g. 5211 and 5212 based cards). add special handling of rates for AR5211 chipsets: it uses different rate codes for CCK rates (which are actually like the other chips but with a 0xF mask). setup a hardware code to rate index reverse mapping table for getting the rate index of received frames. the rates for control frames which have to be set in ath5k_hw_write_rate_duration are now in one single array. drivers/net/wireless/ath5k/ath5k.h: Changes-licensed-under: ISC drivers/net/wireless/ath5k/base.c: Changes-licensed-under: 3-Clause-BSD drivers/net/wireless/ath5k/base.h: Changes-licensed-under: 3-Clause-BSD drivers/net/wireless/ath5k/hw.c: Changes-licensed-under: ISC Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 162 +++++------------------ drivers/net/wireless/ath5k/base.c | 254 +++++++++++++++++-------------------- drivers/net/wireless/ath5k/base.h | 8 +- drivers/net/wireless/ath5k/hw.c | 76 +++-------- 4 files changed, 171 insertions(+), 329 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index 9102eea3c8b..c1b49787366 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -271,11 +271,6 @@ enum ath5k_driver_mode { /* adding this flag to rate_code enables short preamble, see ar5212_reg.h */ #define AR5K_SET_SHORT_PREAMBLE 0x04 -#define HAS_SHPREAMBLE(_ix) \ - (rt->rates[_ix].modulation == IEEE80211_RATE_SHORT_PREAMBLE) -#define SHPREAMBLE_FLAG(_ix) \ - (HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0) - /****************\ TX DEFINITIONS @@ -568,152 +563,61 @@ struct ath5k_athchan_2ghz { u16 a2_athchan; }; + /* * Rate definitions - * TODO: Clean them up or move them on mac80211 -most of these infos are - * used by the rate control algorytm on MadWiFi. */ -/* Max number of rates on the rate table and what it seems - * Atheros hardware supports */ -#define AR5K_MAX_RATES 32 - /** - * struct ath5k_rate - rate structure - * @valid: is this a valid rate for rate control (remove) - * @modulation: respective mac80211 modulation - * @rate_kbps: rate in kbit/s - * @rate_code: hardware rate value, used in &struct ath5k_desc, on RX on - * &struct ath5k_rx_status.rs_rate and on TX on - * &struct ath5k_tx_status.ts_rate. Seems the ar5xxx harware supports - * up to 32 rates, indexed by 1-32. This means we really only need - * 6 bits for the rate_code. - * @dot11_rate: respective IEEE-802.11 rate value - * @control_rate: index of rate assumed to be used to send control frames. - * This can be used to set override the value on the rate duration - * registers. This is only useful if we can override in the harware at - * what rate we want to send control frames at. Note that IEEE-802.11 - * Ch. 9.6 (after IEEE 802.11g changes) defines the rate at which we - * should send ACK/CTS, if we change this value we can be breaking - * the spec. + * Seems the ar5xxx harware supports up to 32 rates, indexed by 1-32. * - * This structure is used to get the RX rate or set the TX rate on the + * The rate code is used to get the RX rate or set the TX rate on the * hardware descriptors. It is also used for internal modulation control * and settings. * - * On RX after the &struct ath5k_desc is parsed by the appropriate - * ah_proc_rx_desc() the respective hardware rate value is set in - * &struct ath5k_rx_status.rs_rate. On TX the desired rate is set in - * &struct ath5k_tx_status.ts_rate which is later used to setup the - * &struct ath5k_desc correctly. This is the hardware rate map we are - * aware of: + * This is the hardware rate map we are aware of: * - * rate_code 1 2 3 4 5 6 7 8 + * rate_code 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 * rate_kbps 3000 1000 ? ? ? 2000 500 48000 * - * rate_code 9 10 11 12 13 14 15 16 + * rate_code 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 * rate_kbps 24000 12000 6000 54000 36000 18000 9000 ? * * rate_code 17 18 19 20 21 22 23 24 * rate_kbps ? ? ? ? ? ? ? 11000 * * rate_code 25 26 27 28 29 30 31 32 - * rate_kbps 5500 2000 1000 ? ? ? ? ? + * rate_kbps 5500 2000 1000 11000S 5500S 2000S ? ? * + * "S" indicates CCK rates with short preamble. + * + * AR5211 has different rate codes for CCK (802.11B) rates. It only uses the + * lowest 4 bits, so they are the same as below with a 0xF mask. + * (0xB, 0xA, 0x9 and 0x8 for 1M, 2M, 5.5M and 11M). + * We handle this in ath5k_setup_bands(). */ -struct ath5k_rate { - u8 valid; - u32 modulation; - u16 rate_kbps; - u8 rate_code; - u8 dot11_rate; - u8 control_rate; -}; - -/* XXX: GRR all this stuff to get leds blinking ??? (check out setcurmode) */ -struct ath5k_rate_table { - u16 rate_count; - u8 rate_code_to_index[AR5K_MAX_RATES]; /* Back-mapping */ - struct ath5k_rate rates[AR5K_MAX_RATES]; -}; - -/* - * Rate tables... - * TODO: CLEAN THIS !!! - */ -#define AR5K_RATES_11A { 8, { \ - 255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0, \ - 7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255, \ - 255, 255, 255, 255, 255, 255, 255, 255 }, { \ - { 1, 0, 6000, 11, 140, 0 }, \ - { 1, 0, 9000, 15, 18, 0 }, \ - { 1, 0, 12000, 10, 152, 2 }, \ - { 1, 0, 18000, 14, 36, 2 }, \ - { 1, 0, 24000, 9, 176, 4 }, \ - { 1, 0, 36000, 13, 72, 4 }, \ - { 1, 0, 48000, 8, 96, 4 }, \ - { 1, 0, 54000, 12, 108, 4 } } \ -} - -#define AR5K_RATES_11B { 4, { \ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, \ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, \ - 3, 2, 1, 0, 255, 255, 255, 255 }, { \ - { 1, 0, 1000, 27, 130, 0 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 2000, 26, 132, 1 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 5500, 25, 139, 1 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 11000, 24, 150, 1 } } \ -} - -#define AR5K_RATES_11G { 12, { \ - 255, 255, 255, 255, 255, 255, 255, 255, 10, 8, 6, 4, \ - 11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255, \ - 3, 2, 1, 0, 255, 255, 255, 255 }, { \ - { 1, 0, 1000, 27, 2, 0 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 2000, 26, 4, 1 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 5500, 25, 11, 1 }, \ - { 1, IEEE80211_RATE_SHORT_PREAMBLE, 11000, 24, 22, 1 }, \ - { 0, 0, 6000, 11, 12, 4 }, \ - { 0, 0, 9000, 15, 18, 4 }, \ - { 1, 0, 12000, 10, 24, 6 }, \ - { 1, 0, 18000, 14, 36, 6 }, \ - { 1, 0, 24000, 9, 48, 8 }, \ - { 1, 0, 36000, 13, 72, 8 }, \ - { 1, 0, 48000, 8, 96, 8 }, \ - { 1, 0, 54000, 12, 108, 8 } } \ -} +#define AR5K_MAX_RATES 32 -#define AR5K_RATES_TURBO { 8, { \ - 255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0, \ - 7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255, \ - 255, 255, 255, 255, 255, 255, 255, 255 }, { \ - { 1, MODULATION_TURBO, 6000, 11, 140, 0 }, \ - { 1, MODULATION_TURBO, 9000, 15, 18, 0 }, \ - { 1, MODULATION_TURBO, 12000, 10, 152, 2 }, \ - { 1, MODULATION_TURBO, 18000, 14, 36, 2 }, \ - { 1, MODULATION_TURBO, 24000, 9, 176, 4 }, \ - { 1, MODULATION_TURBO, 36000, 13, 72, 4 }, \ - { 1, MODULATION_TURBO, 48000, 8, 96, 4 }, \ - { 1, MODULATION_TURBO, 54000, 12, 108, 4 } } \ -} +/* B */ +#define ATH5K_RATE_CODE_1M 0x1B +#define ATH5K_RATE_CODE_2M 0x1A +#define ATH5K_RATE_CODE_5_5M 0x19 +#define ATH5K_RATE_CODE_11M 0x18 +/* A and G */ +#define ATH5K_RATE_CODE_6M 0x0B +#define ATH5K_RATE_CODE_9M 0x0F +#define ATH5K_RATE_CODE_12M 0x0A +#define ATH5K_RATE_CODE_18M 0x0E +#define ATH5K_RATE_CODE_24M 0x09 +#define ATH5K_RATE_CODE_36M 0x0D +#define ATH5K_RATE_CODE_48M 0x08 +#define ATH5K_RATE_CODE_54M 0x0C +/* XR */ +#define ATH5K_RATE_CODE_XR_500K 0x07 +#define ATH5K_RATE_CODE_XR_1M 0x02 +#define ATH5K_RATE_CODE_XR_2M 0x06 +#define ATH5K_RATE_CODE_XR_3M 0x01 -#define AR5K_RATES_XR { 12, { \ - 255, 3, 1, 255, 255, 255, 2, 0, 10, 8, 6, 4, \ - 11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255, \ - 255, 255, 255, 255, 255, 255, 255, 255 }, { \ - { 1, MODULATION_XR, 500, 7, 129, 0 }, \ - { 1, MODULATION_XR, 1000, 2, 139, 1 }, \ - { 1, MODULATION_XR, 2000, 6, 150, 2 }, \ - { 1, MODULATION_XR, 3000, 1, 150, 3 }, \ - { 1, 0, 6000, 11, 140, 4 }, \ - { 1, 0, 9000, 15, 18, 4 }, \ - { 1, 0, 12000, 10, 152, 6 }, \ - { 1, 0, 18000, 14, 36, 6 }, \ - { 1, 0, 24000, 9, 176, 8 }, \ - { 1, 0, 36000, 13, 72, 8 }, \ - { 1, 0, 48000, 8, 96, 8 }, \ - { 1, 0, 54000, 12, 108, 8 } } \ -} /* * Crypto definitions diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index e8564095b2f..114520258b7 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -132,6 +132,48 @@ static struct ath5k_srev_name srev_names[] = { { "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN }, }; +static struct ieee80211_rate ath5k_rates[] = { + { .bitrate = 10, + .hw_value = ATH5K_RATE_CODE_1M, }, + { .bitrate = 20, + .hw_value = ATH5K_RATE_CODE_2M, + .hw_value_short = ATH5K_RATE_CODE_2M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = ATH5K_RATE_CODE_5_5M, + .hw_value_short = ATH5K_RATE_CODE_5_5M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = ATH5K_RATE_CODE_11M, + .hw_value_short = ATH5K_RATE_CODE_11M | AR5K_SET_SHORT_PREAMBLE, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = ATH5K_RATE_CODE_6M, + .flags = 0 }, + { .bitrate = 90, + .hw_value = ATH5K_RATE_CODE_9M, + .flags = 0 }, + { .bitrate = 120, + .hw_value = ATH5K_RATE_CODE_12M, + .flags = 0 }, + { .bitrate = 180, + .hw_value = ATH5K_RATE_CODE_18M, + .flags = 0 }, + { .bitrate = 240, + .hw_value = ATH5K_RATE_CODE_24M, + .flags = 0 }, + { .bitrate = 360, + .hw_value = ATH5K_RATE_CODE_36M, + .flags = 0 }, + { .bitrate = 480, + .hw_value = ATH5K_RATE_CODE_48M, + .flags = 0 }, + { .bitrate = 540, + .hw_value = ATH5K_RATE_CODE_54M, + .flags = 0 }, + /* XR missing */ +}; + /* * Prototypes - PCI stack related functions */ @@ -219,20 +261,16 @@ static void ath5k_detach(struct pci_dev *pdev, struct ieee80211_hw *hw); /* Channel/mode setup */ static inline short ath5k_ieee2mhz(short chan); -static unsigned int ath5k_copy_rates(struct ieee80211_rate *rates, - const struct ath5k_rate_table *rt, - unsigned int max); static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels, unsigned int mode, unsigned int max); -static int ath5k_getchannels(struct ieee80211_hw *hw); +static int ath5k_setup_bands(struct ieee80211_hw *hw); static int ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan); static void ath5k_setcurmode(struct ath5k_softc *sc, unsigned int mode); static void ath5k_mode_setup(struct ath5k_softc *sc); -static void ath5k_set_total_hw_rates(struct ath5k_softc *sc); /* Descriptor setup */ static int ath5k_desc_alloc(struct ath5k_softc *sc, @@ -646,7 +684,6 @@ err_no_irq: #endif /* CONFIG_PM */ - /***********************\ * Driver Initialization * \***********************/ @@ -688,15 +725,12 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) * on settings like the phy mode and regulatory * domain restrictions. */ - ret = ath5k_getchannels(hw); + ret = ath5k_setup_bands(hw); if (ret) { ATH5K_ERR(sc, "can't get channels\n"); goto err; } - /* Set *_rates so we can map hw rate index */ - ath5k_set_total_hw_rates(sc); - /* NB: setup here so ath5k_rate_update is happy */ if (test_bit(AR5K_MODE_11A, ah->ah_modes)) ath5k_setcurmode(sc, AR5K_MODE_11A); @@ -812,27 +846,6 @@ ath5k_ieee2mhz(short chan) return 2212 + chan * 20; } -static unsigned int -ath5k_copy_rates(struct ieee80211_rate *rates, - const struct ath5k_rate_table *rt, - unsigned int max) -{ - unsigned int i, count; - - if (rt == NULL) - return 0; - - for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) { - rates[count].bitrate = rt->rates[i].rate_kbps / 100; - rates[count].hw_value = rt->rates[i].rate_code; - rates[count].flags = rt->rates[i].modulation; - count++; - max--; - } - - return count; -} - static unsigned int ath5k_copy_channels(struct ath5k_hw *ah, struct ieee80211_channel *channels, @@ -895,74 +908,97 @@ ath5k_copy_channels(struct ath5k_hw *ah, return count; } +static void +ath5k_setup_rate_idx(struct ath5k_softc *sc, struct ieee80211_supported_band *b) +{ + u8 i; + + for (i = 0; i < AR5K_MAX_RATES; i++) + sc->rate_idx[b->band][i] = -1; + + for (i = 0; i < b->n_bitrates; i++) { + sc->rate_idx[b->band][b->bitrates[i].hw_value] = i; + if (b->bitrates[i].hw_value_short) + sc->rate_idx[b->band][b->bitrates[i].hw_value_short] = i; + } +} + static int -ath5k_getchannels(struct ieee80211_hw *hw) +ath5k_setup_bands(struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - struct ieee80211_supported_band *sbands = sc->sbands; - const struct ath5k_rate_table *hw_rates; - unsigned int max_r, max_c, count_r, count_c; - int mode2g = AR5K_MODE_11G; + struct ieee80211_supported_band *sband; + int max_c, count_c = 0; + int i; BUILD_BUG_ON(ARRAY_SIZE(sc->sbands) < IEEE80211_NUM_BANDS); - - max_r = ARRAY_SIZE(sc->rates); max_c = ARRAY_SIZE(sc->channels); - count_r = count_c = 0; /* 2GHz band */ - if (!test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) { - mode2g = AR5K_MODE_11B; - if (!test_bit(AR5K_MODE_11B, - sc->ah->ah_capabilities.cap_mode)) - mode2g = -1; - } + sband = &sc->sbands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_2GHZ][0]; - if (mode2g > 0) { - struct ieee80211_supported_band *sband = - &sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(AR5K_MODE_11G, sc->ah->ah_capabilities.cap_mode)) { + /* G mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 12); + sband->n_bitrates = 12; - sband->bitrates = sc->rates; sband->channels = sc->channels; - - sband->band = IEEE80211_BAND_2GHZ; sband->n_channels = ath5k_copy_channels(ah, sband->channels, - mode2g, max_c); - - hw_rates = ath5k_hw_get_rate_table(ah, mode2g); - sband->n_bitrates = ath5k_copy_rates(sband->bitrates, - hw_rates, max_r); + AR5K_MODE_11G, max_c); + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; count_c = sband->n_channels; - count_r = sband->n_bitrates; + max_c -= count_c; + } else if (test_bit(AR5K_MODE_11B, sc->ah->ah_capabilities.cap_mode)) { + /* B mode */ + memcpy(sband->bitrates, &ath5k_rates[0], + sizeof(struct ieee80211_rate) * 4); + sband->n_bitrates = 4; + + /* 5211 only supports B rates and uses 4bit rate codes + * (e.g normally we have 0x1B for 1M, but on 5211 we have 0x0B) + * fix them up here: + */ + if (ah->ah_version == AR5K_AR5211) { + for (i = 0; i < 4; i++) { + sband->bitrates[i].hw_value = + sband->bitrates[i].hw_value & 0xF; + sband->bitrates[i].hw_value_short = + sband->bitrates[i].hw_value_short & 0xF; + } + } - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + sband->channels = sc->channels; + sband->n_channels = ath5k_copy_channels(ah, sband->channels, + AR5K_MODE_11B, max_c); - max_r -= count_r; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = sband; + count_c = sband->n_channels; max_c -= count_c; - } + ath5k_setup_rate_idx(sc, sband); - /* 5GHz band */ - + /* 5GHz band, A mode */ if (test_bit(AR5K_MODE_11A, sc->ah->ah_capabilities.cap_mode)) { - struct ieee80211_supported_band *sband = - &sbands[IEEE80211_BAND_5GHZ]; + sband = &sc->sbands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &sc->rates[IEEE80211_BAND_5GHZ][0]; - sband->bitrates = &sc->rates[count_r]; - sband->channels = &sc->channels[count_c]; + memcpy(sband->bitrates, &ath5k_rates[4], + sizeof(struct ieee80211_rate) * 8); + sband->n_bitrates = 8; - sband->band = IEEE80211_BAND_5GHZ; + sband->channels = &sc->channels[count_c]; sband->n_channels = ath5k_copy_channels(ah, sband->channels, AR5K_MODE_11A, max_c); - hw_rates = ath5k_hw_get_rate_table(ah, AR5K_MODE_11A); - sband->n_bitrates = ath5k_copy_rates(sband->bitrates, - hw_rates, max_r); - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = sband; } + ath5k_setup_rate_idx(sc, sband); ath5k_debug_dump_bands(sc); @@ -1031,75 +1067,13 @@ ath5k_mode_setup(struct ath5k_softc *sc) ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "RX filter 0x%x\n", rfilt); } -/* - * Match the hw provided rate index (through descriptors) - * to an index for sc->curband->bitrates, so it can be used - * by the stack. - * - * This one is a little bit tricky but i think i'm right - * about this... - * - * We have 4 rate tables in the following order: - * XR (4 rates) - * 802.11a (8 rates) - * 802.11b (4 rates) - * 802.11g (12 rates) - * that make the hw rate table. - * - * Lets take a 5211 for example that supports a and b modes only. - * First comes the 802.11a table and then 802.11b (total 12 rates). - * When hw returns eg. 11 it points to the last 802.11b rate (11Mbit), - * if it returns 2 it points to the second 802.11a rate etc. - * - * Same goes for 5212 who has xr/a/b/g support (total 28 rates). - * First comes the XR table, then 802.11a, 802.11b and 802.11g. - * When hw returns eg. 27 it points to the last 802.11g rate (54Mbits) etc - */ -static void -ath5k_set_total_hw_rates(struct ath5k_softc *sc) { - - struct ath5k_hw *ah = sc->ah; - - if (test_bit(AR5K_MODE_11A, ah->ah_modes)) - sc->a_rates = 8; - - if (test_bit(AR5K_MODE_11B, ah->ah_modes)) - sc->b_rates = 4; - - if (test_bit(AR5K_MODE_11G, ah->ah_modes)) - sc->g_rates = 12; - - /* XXX: Need to see what what happens when - xr disable bits in eeprom are set */ - if (ah->ah_version >= AR5K_AR5212) - sc->xr_rates = 4; - -} - static inline int -ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) { - - int mac80211_rix; - - if(sc->curband->band == IEEE80211_BAND_2GHZ) { - /* We setup a g ratetable for both b/g modes */ - mac80211_rix = - hw_rix - sc->b_rates - sc->a_rates - sc->xr_rates; - } else { - mac80211_rix = hw_rix - sc->xr_rates; - } - - /* Something went wrong, fallback to basic rate for this band */ - if ((mac80211_rix >= sc->curband->n_bitrates) || - (mac80211_rix <= 0 )) - mac80211_rix = 1; - - return mac80211_rix; +ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix) +{ + WARN_ON(hw_rix < 0 || hw_rix > AR5K_MAX_RATES); + return sc->rate_idx[sc->curband->band][hw_rix]; } - - - /***************\ * Buffers setup * \***************/ @@ -1788,6 +1762,12 @@ accept: rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); +#if 0 /* add rxs.flag SHORTPRE once it is in mac80211 */ + if (rs.rs_rate >= ATH5K_RATE_CODE_2M && + rs.rs_rate <= ATH5K_RATE_CODE_11M && + rs.rs_rate & AR5K_SET_SHORT_PREAMBLE) + rxs.flag |= RX_FLAG_SHORTPRE; +#endif ath5k_debug_dump_skb(sc, skb, "RX ", 0); /* check beacons in IBSS mode */ diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index d7e03e6b827..248e32eb6cb 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -111,17 +111,13 @@ struct ath5k_softc { struct ieee80211_hw *hw; /* IEEE 802.11 common */ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels[ATH_CHAN_MAX]; - struct ieee80211_rate rates[AR5K_MAX_RATES * IEEE80211_NUM_BANDS]; + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; + u8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES]; enum ieee80211_if_types opmode; struct ath5k_hw *ah; /* Atheros HW */ struct ieee80211_supported_band *curband; - u8 a_rates; - u8 b_rates; - u8 g_rates; - u8 xr_rates; - #ifdef CONFIG_ATH5K_DEBUG struct ath5k_dbg_info debug; /* debug info */ #endif /* CONFIG_ATH5K_DEBUG */ diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index b335d332305..b987aa1e0f7 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -31,13 +31,6 @@ #include "base.h" #include "debug.h" -/* Rate tables */ -static const struct ath5k_rate_table ath5k_rt_11a = AR5K_RATES_11A; -static const struct ath5k_rate_table ath5k_rt_11b = AR5K_RATES_11B; -static const struct ath5k_rate_table ath5k_rt_11g = AR5K_RATES_11G; -static const struct ath5k_rate_table ath5k_rt_turbo = AR5K_RATES_TURBO; -static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR; - /* Prototypes */ static int ath5k_hw_nic_reset(struct ath5k_hw *, u32); static int ath5k_hw_nic_wakeup(struct ath5k_hw *, int, bool); @@ -520,34 +513,6 @@ static int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) return 0; } -/* - * Get the rate table for a specific operation mode - */ -const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath5k_hw *ah, - unsigned int mode) -{ - ATH5K_TRACE(ah->ah_sc); - - if (!test_bit(mode, ah->ah_capabilities.cap_mode)) - return NULL; - - /* Get rate tables */ - switch (mode) { - case AR5K_MODE_11A: - return &ath5k_rt_11a; - case AR5K_MODE_11A_TURBO: - return &ath5k_rt_turbo; - case AR5K_MODE_11B: - return &ath5k_rt_11b; - case AR5K_MODE_11G: - return &ath5k_rt_11g; - case AR5K_MODE_11G_TURBO: - return &ath5k_rt_xr; - } - - return NULL; -} - /* * Free the ath5k_hw struct */ @@ -618,45 +583,42 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, return 0; } + +/* + * index into rates for control rates, we can set it up like this because + * this is only used for AR5212 and we know it supports G mode + */ +static int control_rates[] = + { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 }; + /** * ath5k_hw_write_rate_duration - set rate duration during hw resets * * @ah: the &struct ath5k_hw * @mode: one of enum ath5k_driver_mode * - * Write the rate duration table for the current mode upon hw reset. This - * is a helper for ath5k_hw_reset(). It seems all this is doing is setting - * an ACK timeout for the hardware for the current mode for each rate. The - * rates which are capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, - * and 11Mbps) have another register for the short preamble ACK timeout - * calculation. - * + * Write the rate duration table upon hw reset. This is a helper for + * ath5k_hw_reset(). It seems all this is doing is setting an ACK timeout for + * the hardware for the current mode for each rate. The rates which are capable + * of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have another + * register for the short preamble ACK timeout calculation. */ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah, unsigned int mode) { struct ath5k_softc *sc = ah->ah_sc; - const struct ath5k_rate_table *rt; - struct ieee80211_rate srate = {}; + struct ieee80211_rate *rate; unsigned int i; - /* Get rate table for the current operating mode */ - rt = ath5k_hw_get_rate_table(ah, mode); - /* Write rate duration table */ - for (i = 0; i < rt->rate_count; i++) { - const struct ath5k_rate *rate, *control_rate; - + for (i = 0; i < sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates; i++) { u32 reg; u16 tx_time; - rate = &rt->rates[i]; - control_rate = &rt->rates[rate->control_rate]; + rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[control_rates[i]]; /* Set ACK timeout */ - reg = AR5K_RATE_DUR(rate->rate_code); - - srate.bitrate = control_rate->rate_kbps/100; + reg = AR5K_RATE_DUR(rate->hw_value); /* An ACK frame consists of 10 bytes. If you add the FCS, * which ieee80211_generic_frame_duration() adds, @@ -665,11 +627,11 @@ static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah, * ieee80211_duration() for a brief description of * what rate we should choose to TX ACKs. */ tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw, - sc->vif, 10, &srate)); + sc->vif, 10, rate)); ath5k_hw_reg_write(ah, tx_time, reg); - if (!HAS_SHPREAMBLE(i)) + if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)) continue; /* -- cgit v1.2.3 From 6f0a2c4dfc96d268233a6a882f3805975f91216c Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Wed, 30 Jul 2008 17:20:14 +0200 Subject: iwlwifi: use generic mac80211 radiotap headers remove drivers own implementation of radiotap in favor of the generic one provided by mac80211. also remove priv->add_radiotap because it is not used any more. Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 104 ++--------------------- drivers/net/wireless/iwlwifi/iwl-3945.h | 1 - drivers/net/wireless/iwlwifi/iwl-agn.c | 2 - drivers/net/wireless/iwlwifi/iwl-dev.h | 1 - drivers/net/wireless/iwlwifi/iwl-rx.c | 125 +++++----------------------- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 - 6 files changed, 27 insertions(+), 208 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 3f51f363534..8dc26adc197 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -531,99 +531,6 @@ static int iwl3945_is_network_packet(struct iwl3945_priv *priv, } } -static void iwl3945_add_radiotap(struct iwl3945_priv *priv, - struct sk_buff *skb, - struct iwl3945_rx_frame_hdr *rx_hdr, - struct ieee80211_rx_status *stats) -{ - /* First cache any information we need before we overwrite - * the information provided in the skb from the hardware */ - s8 signal = stats->signal; - s8 noise = 0; - int rate = stats->rate_idx; - u64 tsf = stats->mactime; - __le16 phy_flags_hw = rx_hdr->phy_flags, antenna; - - struct iwl3945_rt_rx_hdr { - struct ieee80211_radiotap_header rt_hdr; - __le64 rt_tsf; /* TSF */ - u8 rt_flags; /* radiotap packet flags */ - u8 rt_rate; /* rate in 500kb/s */ - __le16 rt_channelMHz; /* channel in MHz */ - __le16 rt_chbitmask; /* channel bitfield */ - s8 rt_dbmsignal; /* signal in dBm, kluged to signed */ - s8 rt_dbmnoise; - u8 rt_antenna; /* antenna number */ - } __attribute__ ((packed)) *iwl3945_rt; - - if (skb_headroom(skb) < sizeof(*iwl3945_rt)) { - if (net_ratelimit()) - printk(KERN_ERR "not enough headroom [%d] for " - "radiotap head [%zd]\n", - skb_headroom(skb), sizeof(*iwl3945_rt)); - return; - } - - /* put radiotap header in front of 802.11 header and data */ - iwl3945_rt = (void *)skb_push(skb, sizeof(*iwl3945_rt)); - - /* initialise radiotap header */ - iwl3945_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; - iwl3945_rt->rt_hdr.it_pad = 0; - - /* total header + data */ - put_unaligned_le16(sizeof(*iwl3945_rt), &iwl3945_rt->rt_hdr.it_len); - - /* Indicate all the fields we add to the radiotap header */ - put_unaligned_le32((1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_CHANNEL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | - (1 << IEEE80211_RADIOTAP_ANTENNA), - &iwl3945_rt->rt_hdr.it_present); - - /* Zero the flags, we'll add to them as we go */ - iwl3945_rt->rt_flags = 0; - - put_unaligned_le64(tsf, &iwl3945_rt->rt_tsf); - - iwl3945_rt->rt_dbmsignal = signal; - iwl3945_rt->rt_dbmnoise = noise; - - /* Convert the channel frequency and set the flags */ - put_unaligned_le16(stats->freq, &iwl3945_rt->rt_channelMHz); - if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK)) - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, - &iwl3945_rt->rt_chbitmask); - else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK) - put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, - &iwl3945_rt->rt_chbitmask); - else /* 802.11g */ - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, - &iwl3945_rt->rt_chbitmask); - - if (rate == -1) - iwl3945_rt->rt_rate = 0; - else { - if (stats->band == IEEE80211_BAND_5GHZ) - rate += IWL_FIRST_OFDM_RATE; - - iwl3945_rt->rt_rate = iwl3945_rates[rate].ieee; - } - - /* antenna number */ - antenna = phy_flags_hw & RX_RES_PHY_FLAGS_ANTENNA_MSK; - iwl3945_rt->rt_antenna = le16_to_cpu(antenna) >> 4; - - /* set the preamble flag if we have it */ - if (phy_flags_hw & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - iwl3945_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - - stats->flag |= RX_FLAG_RADIOTAP; -} - static void iwl3945_pass_packet_to_mac80211(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb, struct ieee80211_rx_status *stats) @@ -657,9 +564,6 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl3945_priv *priv, iwl3945_set_decrypted_flag(priv, rxb->skb, le32_to_cpu(rx_end->status), stats); - if (priv->add_radiotap) - iwl3945_add_radiotap(priv, rxb->skb, rx_hdr, stats); - #ifdef CONFIG_IWL3945_LEDS if (ieee80211_is_data(hdr->frame_control)) priv->rxtxpackets += len; @@ -684,7 +588,6 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, u16 rx_stats_noise_diff = le16_to_cpu(rx_stats->noise_diff); u8 network_packet; - rx_status.antenna = 0; rx_status.flag = 0; rx_status.mactime = le64_to_cpu(rx_end->timestamp); rx_status.freq = @@ -696,6 +599,13 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IWL_FIRST_OFDM_RATE; + rx_status.antenna = le16_to_cpu(rx_hdr->phy_flags & + RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; + + /* set the preamble flag if appropriate */ + if (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + if ((unlikely(rx_stats->phy_count > 20))) { IWL_DEBUG_DROP ("dsp size out of range [0,20]: " diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index fa81ba1af3d..4dd3f0dbe07 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -707,7 +707,6 @@ struct iwl3945_priv { enum ieee80211_band band; int alloc_rxb_skb; - bool add_radiotap; void (*rx_handlers[REPLY_MAX])(struct iwl3945_priv *priv, struct iwl3945_rx_mem_buffer *rxb); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 061ffba9c88..676fc0acede 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2790,8 +2790,6 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co mutex_lock(&priv->mutex); IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel->hw_value); - priv->add_radiotap = !!(conf->flags & IEEE80211_CONF_RADIOTAP); - if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) { IWL_DEBUG_MAC80211("leave - RF-KILL - waiting for uCode\n"); goto out; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index c19db438306..79203081950 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -829,7 +829,6 @@ struct iwl_priv { enum ieee80211_band band; int alloc_rxb_skb; - bool add_radiotap; void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index f3f6ea49fdd..b92a580ed2f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -789,107 +789,6 @@ static inline void iwl_dbg_report_frame(struct iwl_priv *priv, } #endif -static void iwl_add_radiotap(struct iwl_priv *priv, - struct sk_buff *skb, - struct iwl_rx_phy_res *rx_start, - struct ieee80211_rx_status *stats, - u32 ampdu_status) -{ - s8 signal = stats->signal; - s8 noise = 0; - int rate = stats->rate_idx; - u64 tsf = stats->mactime; - __le16 antenna; - __le16 phy_flags_hw = rx_start->phy_flags; - struct iwl4965_rt_rx_hdr { - struct ieee80211_radiotap_header rt_hdr; - __le64 rt_tsf; /* TSF */ - u8 rt_flags; /* radiotap packet flags */ - u8 rt_rate; /* rate in 500kb/s */ - __le16 rt_channelMHz; /* channel in MHz */ - __le16 rt_chbitmask; /* channel bitfield */ - s8 rt_dbmsignal; /* signal in dBm, kluged to signed */ - s8 rt_dbmnoise; - u8 rt_antenna; /* antenna number */ - } __attribute__ ((packed)) *iwl4965_rt; - - /* TODO: We won't have enough headroom for HT frames. Fix it later. */ - if (skb_headroom(skb) < sizeof(*iwl4965_rt)) { - if (net_ratelimit()) - printk(KERN_ERR "not enough headroom [%d] for " - "radiotap head [%zd]\n", - skb_headroom(skb), sizeof(*iwl4965_rt)); - return; - } - - /* put radiotap header in front of 802.11 header and data */ - iwl4965_rt = (void *)skb_push(skb, sizeof(*iwl4965_rt)); - - /* initialise radiotap header */ - iwl4965_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; - iwl4965_rt->rt_hdr.it_pad = 0; - - /* total header + data */ - put_unaligned_le16(sizeof(*iwl4965_rt), &iwl4965_rt->rt_hdr.it_len); - - /* Indicate all the fields we add to the radiotap header */ - put_unaligned_le32((1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_CHANNEL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | - (1 << IEEE80211_RADIOTAP_ANTENNA), - &(iwl4965_rt->rt_hdr.it_present)); - - /* Zero the flags, we'll add to them as we go */ - iwl4965_rt->rt_flags = 0; - - put_unaligned_le64(tsf, &iwl4965_rt->rt_tsf); - - iwl4965_rt->rt_dbmsignal = signal; - iwl4965_rt->rt_dbmnoise = noise; - - /* Convert the channel frequency and set the flags */ - put_unaligned(cpu_to_le16(stats->freq), &iwl4965_rt->rt_channelMHz); - if (!(phy_flags_hw & RX_RES_PHY_FLAGS_BAND_24_MSK)) - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, - &iwl4965_rt->rt_chbitmask); - else if (phy_flags_hw & RX_RES_PHY_FLAGS_MOD_CCK_MSK) - put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, - &iwl4965_rt->rt_chbitmask); - else /* 802.11g */ - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, - &iwl4965_rt->rt_chbitmask); - - if (rate == -1) - iwl4965_rt->rt_rate = 0; - else - iwl4965_rt->rt_rate = iwl_rates[rate].ieee; - - /* - * "antenna number" - * - * It seems that the antenna field in the phy flags value - * is actually a bitfield. This is undefined by radiotap, - * it wants an actual antenna number but I always get "7" - * for most legacy frames I receive indicating that the - * same frame was received on all three RX chains. - * - * I think this field should be removed in favour of a - * new 802.11n radiotap field "RX chains" that is defined - * as a bitmask. - */ - antenna = phy_flags_hw & RX_RES_PHY_FLAGS_ANTENNA_MSK; - iwl4965_rt->rt_antenna = le16_to_cpu(antenna) >> 4; - - /* set the preamble flag if appropriate */ - if (phy_flags_hw & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - iwl4965_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - - stats->flag |= RX_FLAG_RADIOTAP; -} - static void iwl_update_rx_stats(struct iwl_priv *priv, u16 fc, u16 len) { /* 0 - mgmt, 1 - cnt, 2 - data */ @@ -1074,9 +973,6 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - if (priv->add_radiotap) - iwl_add_radiotap(priv, rxb->skb, rx_start, stats, ampdu_status); - iwl_update_rx_stats(priv, le16_to_cpu(hdr->frame_control), len); ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); priv->alloc_rxb_skb--; @@ -1171,7 +1067,6 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IWL_FIRST_OFDM_RATE; - rx_status.antenna = 0; rx_status.flag = 0; rx_status.flag |= RX_FLAG_TSFT; @@ -1250,6 +1145,26 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, rx_status.signal, rx_status.noise, rx_status.signal, (unsigned long long)rx_status.mactime); + /* + * "antenna number" + * + * It seems that the antenna field in the phy flags value + * is actually a bitfield. This is undefined by radiotap, + * it wants an actual antenna number but I always get "7" + * for most legacy frames I receive indicating that the + * same frame was received on all three RX chains. + * + * I think this field should be removed in favour of a + * new 802.11n radiotap field "RX chains" that is defined + * as a bitmask. + */ + rx_status.antenna = le16_to_cpu(rx_start->phy_flags & + RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; + + /* set the preamble flag if appropriate */ + if (rx_start->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + /* Take shortcut when only in monitor mode */ if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { iwl_pass_packet_to_mac80211(priv, include_phy, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index b775d5bab66..5096abc66c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -6650,8 +6650,6 @@ static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co mutex_lock(&priv->mutex); IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel->hw_value); - priv->add_radiotap = !!(conf->flags & IEEE80211_CONF_RADIOTAP); - if (!iwl3945_is_ready(priv)) { IWL_DEBUG_MAC80211("leave - not ready\n"); ret = -EIO; -- cgit v1.2.3 From 73ae98a1334c7ab7c4f01e8a1c0c773470979688 Mon Sep 17 00:00:00 2001 From: roel kluin Date: Mon, 21 Jul 2008 20:29:52 -0400 Subject: iwl-4965: test below 0 on unsigned num_tbs num_tbs is unsigned so the test doesn't work. Signed-off-by: Roel Kluin Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index d82823b5c8a..dac34aa8358 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -126,7 +126,7 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr, u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs); /* Each TFD can point to a maximum 20 Tx buffers */ - if ((num_tbs >= MAX_NUM_OF_TBS) || (num_tbs < 0)) { + if (num_tbs >= MAX_NUM_OF_TBS) { IWL_ERROR("Error can not send more than %d chunks\n", MAX_NUM_OF_TBS); return -EINVAL; -- cgit v1.2.3 From 39351fa343289070f6f30bd0652e10c8d97f0a3b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:41 +0800 Subject: iwlwifi: kill struct iwl4965_lq_mngr This patch removes struct iwl4965_lq_mngr it is not used. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 1 - drivers/net/wireless/iwlwifi/iwl-dev.h | 15 --------------- 2 files changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index c72f72579be..fbf75a62958 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -876,7 +876,6 @@ int iwl_init_drv(struct iwl_priv *priv) spin_lock_init(&priv->power_data.lock); spin_lock_init(&priv->sta_lock); spin_lock_init(&priv->hcmd_lock); - spin_lock_init(&priv->lq_mngr.lock); INIT_LIST_HEAD(&priv->free_frames); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 79203081950..f46e9cd1ca1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -672,18 +672,6 @@ struct iwl_kw { #define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 -struct iwl4965_lq_mngr { - spinlock_t lock; - s32 max_window_size; - s32 *expected_tpt; - u8 *next_higher_rate; - u8 *next_lower_rate; - unsigned long stamp; - unsigned long stamp_last; - u32 flush_time; - u32 tx_packets; -}; - /* Sensitivity and chain noise calibration */ #define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1) #define INITIALIZATION_VALUE 0xFFFF @@ -938,9 +926,6 @@ struct iwl_priv { struct iwl_ht_info current_ht_config; u8 last_phy_res[100]; - /* Rate scaling data */ - struct iwl4965_lq_mngr lq_mngr; - /* Rate scaling data */ s8 data_retry_limit; u8 retry_rate; -- cgit v1.2.3 From ded2ae7c1fbff22516fd55ff0edcf6cd86f76186 Mon Sep 17 00:00:00 2001 From: Esti Kummer Date: Mon, 4 Aug 2008 16:00:45 +0800 Subject: iwlwifi: add level for debugging host command This patch adds another level for debugging host command. This adds an option to suppress the debug prints for sensitivity and link quality commands. Signed-off-by: Esti Kummer Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-debug.h | 12 +++++++----- drivers/net/wireless/iwlwifi/iwl-hcmd.c | 14 ++++++++++++-- drivers/net/wireless/iwlwifi/iwl-tx.c | 26 ++++++++++++++++++++------ 3 files changed, 39 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index d2daa174df2..e548d67f87f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -110,11 +110,12 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) * */ -#define IWL_DL_INFO (1 << 0) -#define IWL_DL_MAC80211 (1 << 1) -#define IWL_DL_HOST_COMMAND (1 << 2) -#define IWL_DL_STATE (1 << 3) +#define IWL_DL_INFO (1 << 0) +#define IWL_DL_MAC80211 (1 << 1) +#define IWL_DL_HCMD (1 << 2) +#define IWL_DL_STATE (1 << 3) #define IWL_DL_MACDUMP (1 << 4) +#define IWL_DL_HCMD_DUMP (1 << 5) #define IWL_DL_RADIO (1 << 7) #define IWL_DL_POWER (1 << 8) #define IWL_DL_TEMP (1 << 9) @@ -162,7 +163,8 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) #define IWL_DEBUG_ISR(f, a...) IWL_DEBUG(IWL_DL_ISR, f, ## a) #define IWL_DEBUG_LED(f, a...) IWL_DEBUG(IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(f, a...) IWL_DEBUG(IWL_DL_WEP, f, ## a) -#define IWL_DEBUG_HC(f, a...) IWL_DEBUG(IWL_DL_HOST_COMMAND, f, ## a) +#define IWL_DEBUG_HC(f, a...) IWL_DEBUG(IWL_DL_HCMD, f, ## a) +#define IWL_DEBUG_HC_DUMP(f, a...) IWL_DEBUG(IWL_DL_HCMD_DUMP, f, ## a) #define IWL_DEBUG_CALIB(f, a...) IWL_DEBUG(IWL_DL_CALIB, f, ## a) #define IWL_DEBUG_FW(f, a...) IWL_DEBUG(IWL_DL_FW, f, ## a) #define IWL_DEBUG_RF_KILL(f, a...) IWL_DEBUG(IWL_DL_RF_KILL, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 2eb03eea190..8300f3d00a0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -120,8 +120,18 @@ static int iwl_generic_cmd_callback(struct iwl_priv *priv, return 1; } - IWL_DEBUG_HC("back from %s (0x%08X)\n", - get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); +#ifdef CONFIG_IWLWIFI_DEBUG + switch (cmd->hdr.cmd) { + case REPLY_TX_LINK_QUALITY_CMD: + case SENSITIVITY_CMD: + IWL_DEBUG_HC_DUMP("back from %s (0x%08X)\n", + get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + break; + default: + IWL_DEBUG_HC("back from %s (0x%08X)\n", + get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + } +#endif /* Let iwl_tx_complete free the response skb */ return 1; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index dac34aa8358..addf899df17 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -1065,12 +1065,26 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) phys_addr += offsetof(struct iwl_cmd, hdr); iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size); - IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, " - "%d bytes at %d[%d]:%d\n", - get_cmd_string(out_cmd->hdr.cmd), - out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), - fix_size, q->write_ptr, idx, IWL_CMD_QUEUE_NUM); - +#ifdef CONFIG_IWLWIFI_DEBUG + switch (out_cmd->hdr.cmd) { + case REPLY_TX_LINK_QUALITY_CMD: + case SENSITIVITY_CMD: + IWL_DEBUG_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + get_cmd_string(out_cmd->hdr.cmd), + out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, + q->write_ptr, idx, IWL_CMD_QUEUE_NUM); + break; + default: + IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + get_cmd_string(out_cmd->hdr.cmd), + out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, + q->write_ptr, idx, IWL_CMD_QUEUE_NUM); + } +#endif txq->need_update = 1; /* Set up entry in queue's byte count circular buffer */ -- cgit v1.2.3 From ba5b6efe5d945f7a4c5b656051cc16698afdda24 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 15 Jul 2008 18:43:56 -0700 Subject: b43legacy: use le16 frame control directly, avoid byteswapping Acked-by: Michael Buesch Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/b43legacy/xmit.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 68e1f8c7872..c5ca72aa59e 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -193,7 +193,6 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, { const struct ieee80211_hdr *wlhdr; int use_encryption = !!info->control.hw_key; - u16 fctl; u8 rate; struct ieee80211_rate *rate_fb; int rate_ofdm; @@ -204,7 +203,6 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, struct ieee80211_rate *tx_rate; wlhdr = (const struct ieee80211_hdr *)fragment_data; - fctl = le16_to_cpu(wlhdr->frame_control); memset(txhdr, 0, sizeof(*txhdr)); @@ -253,7 +251,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, mac_ctl |= (key->algorithm << B43legacy_TX4_MAC_KEYALG_SHIFT) & B43legacy_TX4_MAC_KEYALG; - wlhdr_len = ieee80211_get_hdrlen(fctl); + wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); iv_len = min((size_t)info->control.iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); -- cgit v1.2.3 From 7294ec955cb0c3eeefca2f4dd271c8068ab4edc5 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 15 Jul 2008 18:43:59 -0700 Subject: iwlwifi: use le16 frame control directly, use QOS symbolic constant mask Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index addf899df17..6cba5e9c54e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -824,7 +824,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); + hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ sta_id = iwl_get_sta_id(priv, hdr); @@ -842,7 +842,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) txq_id = swq_id; if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; seq_number = priv->stations[sta_id].tid[tid].seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl = hdr->seq_ctrl & diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 5096abc66c0..a622fc33590 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2574,7 +2574,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); + hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ sta_id = iwl3945_get_sta_id(priv, hdr); @@ -2590,7 +2590,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; hdr->seq_ctrl = cpu_to_le16(seq_number) | @@ -2709,7 +2709,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) sizeof(out_cmd->cmd.tx)); iwl3945_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr, - ieee80211_get_hdrlen(le16_to_cpu(fc))); + ieee80211_hdrlen(fc)); /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); -- cgit v1.2.3 From 798ee9850e9bf94b4436f9c7238823322e326885 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 15 Jul 2008 18:44:02 -0700 Subject: ath5k: explicitly check skb->len ieee80211_get_hdrlen_from_skb internally checks that the skb is long enough to hold the full header, or it returns 0 if not. The check in ath5k does not check this case and assumes it always got the actual header length which it then checks against the skb->len plus some headroom. Change to ieee80211_hdrlen which always returns the hdrlen and keep the existing headroom check. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 114520258b7..c1de1ef09e5 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1540,7 +1540,7 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, struct sk_buff *skb, struct ath5k_rx_status *rs) { struct ieee80211_hdr *hdr = (void *)skb->data; - unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb); + unsigned int keyix, hlen; if (!(rs->rs_status & AR5K_RXERR_DECRYPT) && rs->rs_keyix != AR5K_RXKEYIX_INVALID) @@ -1549,6 +1549,7 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds, /* Apparently when a default key is used to decrypt the packet the hw does not set the index used to decrypt. In such cases get the index from the packet. */ + hlen = ieee80211_hdrlen(hdr->frame_control); if (ieee80211_has_protected(hdr->frame_control) && !(rs->rs_status & AR5K_RXERR_DECRYPT) && skb->len >= hlen + 4) { -- cgit v1.2.3 From 7670e62c7ed6d1a70a98c3047898712be6aa9ff8 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Thu, 14 Aug 2008 10:40:52 -0700 Subject: libertas_tf: header file Signed-off-by: Luis Carlos Cobo Signed-off-by: John W. Linville --- drivers/net/wireless/libertas_tf/libertas_tf.h | 514 +++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 drivers/net/wireless/libertas_tf/libertas_tf.h (limited to 'drivers') diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/libertas_tf/libertas_tf.h new file mode 100644 index 00000000000..8995cd7c29b --- /dev/null +++ b/drivers/net/wireless/libertas_tf/libertas_tf.h @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2007, Red Hat, Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include +#include +#include +#include + +#ifndef DRV_NAME +#define DRV_NAME "libertas_tf" +#endif + +#define MRVL_DEFAULT_RETRIES 9 +#define MRVL_PER_PACKET_RATE 0x10 +#define MRVL_MAX_BCN_SIZE 440 +#define CMD_OPTION_WAITFORRSP 0x0002 + +/* Return command are almost always the same as the host command, but with + * bit 15 set high. There are a few exceptions, though... + */ +#define CMD_RET(cmd) (0x8000 | cmd) + +/* Command codes */ +#define CMD_GET_HW_SPEC 0x0003 +#define CMD_802_11_RESET 0x0005 +#define CMD_MAC_MULTICAST_ADR 0x0010 +#define CMD_802_11_RADIO_CONTROL 0x001c +#define CMD_802_11_RF_CHANNEL 0x001d +#define CMD_802_11_RF_TX_POWER 0x001e +#define CMD_MAC_CONTROL 0x0028 +#define CMD_802_11_MAC_ADDRESS 0x004d +#define CMD_SET_BOOT2_VER 0x00a5 +#define CMD_802_11_BEACON_CTRL 0x00b0 +#define CMD_802_11_BEACON_SET 0x00cb +#define CMD_802_11_SET_MODE 0x00cc +#define CMD_802_11_SET_BSSID 0x00cd + +#define CMD_ACT_GET 0x0000 +#define CMD_ACT_SET 0x0001 + +/* Define action or option for CMD_802_11_RESET */ +#define CMD_ACT_HALT 0x0003 + +/* Define action or option for CMD_MAC_CONTROL */ +#define CMD_ACT_MAC_RX_ON 0x0001 +#define CMD_ACT_MAC_TX_ON 0x0002 +#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 +#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 +#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 + +/* Define action or option for CMD_802_11_RADIO_CONTROL */ +#define CMD_TYPE_AUTO_PREAMBLE 0x0001 +#define CMD_TYPE_SHORT_PREAMBLE 0x0002 +#define CMD_TYPE_LONG_PREAMBLE 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 +#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 + +/* Codes for CMD_802_11_SET_MODE */ +enum lbtf_mode { + LBTF_PASSIVE_MODE, + LBTF_STA_MODE, + LBTF_AP_MODE, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_FIRMWARE_READY 48 +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define LBS_NUM_CMD_BUFFERS 10 +#define LBS_CMD_BUFFER_SIZE (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define LBS_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_REGION_CODE 6 +/** + * the table to keep region code + */ +#define LBTF_REGDOMAIN_US 0x10 +#define LBTF_REGDOMAIN_CA 0x20 +#define LBTF_REGDOMAIN_EU 0x30 +#define LBTF_REGDOMAIN_SP 0x31 +#define LBTF_REGDOMAIN_FR 0x32 +#define LBTF_REGDOMAIN_JP 0x40 + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define LBTF_EVENT_BCN_SENT 55 + +/** Global Variable Declaration */ +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +extern struct workqueue_struct *lbtf_wq; + +struct lbtf_private; + +struct lbtf_offset_value { + u32 offset; + u32 value; +}; + +struct channel_range { + u8 regdomain; + u8 start; + u8 end; /* exclusive (channel must be less than end) */ +}; + +struct if_usb_card; + +/** Private structure for the MV device */ +struct lbtf_private { + void *card; + struct ieee80211_hw *hw; + + /* Command response buffer */ + u8 cmd_resp_buff[LBS_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + struct ieee80211_vif *vif; + + struct work_struct cmd_work; + struct work_struct tx_work; + /** Hardware access */ + int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb); + int (*hw_prog_firmware) (struct if_usb_card *cardp); + int (*hw_reset_device) (struct if_usb_card *cardp); + + + /** Wlan adapter data structure*/ + /** STATUS variables */ + u32 fwrelease; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + int nr_retries; + int cmd_timed_out; + + u8 cmd_response_rxed; + + /** capability Info used in Association, start, join */ + u16 capability; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + int cur_freq; + + struct sk_buff *skb_to_tx; + struct sk_buff *tx_skb; + + /** NIC Operation characteristics */ + u16 mac_control; + u16 regioncode; + struct channel_range range; + + u8 radioon; + u32 preamble; + + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct lbtf_offset_value offsetvalue; + + u8 fw_ready; + u8 surpriseremoved; + struct sk_buff_head bc_ps_buf; +}; + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + __le32 tx_status; + /* Tx control */ + __le32 tx_control; + __le32 tx_packet_location; + /* Tx packet length */ + __le16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + __le16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + __le16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + __le32 pkt_ptr; + + /* Next Rx RxPD addr */ + __le32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_header { + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; +} __attribute__ ((packed)); + +struct cmd_ctrl_node { + struct list_head list; + int result; + /* command response */ + int (*callback)(struct lbtf_private *, + unsigned long, struct cmd_header *); + unsigned long callback_arg; + /* command data */ + struct cmd_header *cmdbuf; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* + * Define data structure for CMD_GET_HW_SPEC + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + struct cmd_header hdr; + + /* HW Interface version number */ + __le16 hwifversion; + /* HW version number */ + __le16 version; + /* Max number of TxPD FW can handle */ + __le16 nr_txpd; + /* Max no of Multicast address */ + __le16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + __le16 regioncode; + + /* Number of antenna used */ + __le16 nr_antenna; + + /* FW release number, example 0x01030304 = 2.3.4p1 */ + __le32 fwrelease; + + /* Base Address of TxPD queue */ + __le32 wcb_base; + /* Read Pointer of RxPd queue */ + __le32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + __le32 rxpd_wrptr; + + /*FW/HW capability */ + __le32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_mac_control { + struct cmd_header hdr; + __le16 action; + u16 reserved; +}; + +struct cmd_ds_802_11_mac_address { + struct cmd_header hdr; + + __le16 action; + uint8_t macadd[ETH_ALEN]; +}; + +struct cmd_ds_mac_multicast_addr { + struct cmd_header hdr; + + __le16 action; + __le16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_set_mode { + struct cmd_header hdr; + + __le16 mode; +}; + +struct cmd_ds_set_bssid { + struct cmd_header hdr; + + u8 bssid[6]; + u8 activate; +}; + +struct cmd_ds_802_11_radio_control { + struct cmd_header hdr; + + __le16 action; + __le16 control; +}; + + +struct cmd_ds_802_11_rf_channel { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 rftype; /* unused */ + __le16 reserved; /* unused */ + u8 channellist[32]; /* unused */ +}; + +struct cmd_ds_set_boot2_ver { + struct cmd_header hdr; + + __le16 action; + __le16 version; +}; + +struct cmd_ds_802_11_reset { + struct cmd_header hdr; + + __le16 action; +}; + +struct cmd_ds_802_11_beacon_control { + struct cmd_header hdr; + + __le16 action; + __le16 beacon_enable; + __le16 beacon_period; +}; + +struct cmd_ds_802_11_beacon_set { + struct cmd_header hdr; + + __le16 len; + u8 beacon[MRVL_MAX_BCN_SIZE]; +}; + +struct lbtf_private; +struct cmd_ctrl_node; + +/** Function Prototype Declaration */ +void lbtf_set_mac_control(struct lbtf_private *priv); + +int lbtf_free_cmd_buffer(struct lbtf_private *priv); + +int lbtf_allocate_cmd_buffer(struct lbtf_private *priv); +int lbtf_execute_next_command(struct lbtf_private *priv); +int lbtf_set_radio_control(struct lbtf_private *priv); +int lbtf_update_hw_spec(struct lbtf_private *priv); +int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); +void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid); +int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); + +int lbtf_set_channel(struct lbtf_private *priv, u8 channel); + +int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon); +int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, + int beacon_int); + + +int lbtf_process_rx_command(struct lbtf_private *priv); +void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, + int result); +void lbtf_cmd_response_rx(struct lbtf_private *priv); + +/* main.c */ +struct chan_freq_power *lbtf_get_region_cfp_table(u8 region, + int *cfp_no); +struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev); +int lbtf_remove_card(struct lbtf_private *priv); +int lbtf_start_card(struct lbtf_private *priv); +int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb); +void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail); +void lbtf_bcn_sent(struct lbtf_private *priv); + +/* support functions for cmd.c */ +/* lbtf_cmd() infers the size of the buffer to copy data back into, from + the size of the target of the pointer. Since the command to be sent + may often be smaller, that size is set in cmd->size by the caller.*/ +#define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ + uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ + (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ + __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ +}) + +#define lbtf_cmd_with_response(priv, cmdnr, cmd) \ + lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd)) + +void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size); + +int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, unsigned long, + struct cmd_header *), + unsigned long callback_arg); + +int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, + struct cmd_header *resp); -- cgit v1.2.3 From 06b16ae5319251c26377afcb401e46056d5673f4 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Thu, 14 Aug 2008 10:40:57 -0700 Subject: libertas_tf: main.c, data paths and mac80211 handlers This patch contains most of the libertastf driver, just lacking command helper functions and usb specific functions. Currently, monitor, managed, ap and mesh interfaces are supported. Even though this driver supports the same hardware as the "libertas" driver, it uses a different (thin) firmware, that makes it suitable for a mac80211 driver. Signed-off-by: Luis Carlos Cobo Signed-off-by: John W. Linville --- drivers/net/wireless/libertas_tf/main.c | 666 ++++++++++++++++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 drivers/net/wireless/libertas_tf/main.c (limited to 'drivers') diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c new file mode 100644 index 00000000000..2c1d680d2c5 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/main.c @@ -0,0 +1,666 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include "libertas_tf.h" +#include "linux/etherdevice.h" + +#define DRIVER_RELEASE_VERSION "004.p0" +/* thinfirm version: 5.132.X.pX */ +#define LBTF_FW_VER_MIN 0x05840300 +#define LBTF_FW_VER_MAX 0x0584ffff +#define QOS_CONTROL_LEN 2 + +static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION; +struct workqueue_struct *lbtf_wq; + +static const struct ieee80211_channel lbtf_channels[] = { + { .center_freq = 2412, .hw_value = 1 }, + { .center_freq = 2417, .hw_value = 2 }, + { .center_freq = 2422, .hw_value = 3 }, + { .center_freq = 2427, .hw_value = 4 }, + { .center_freq = 2432, .hw_value = 5 }, + { .center_freq = 2437, .hw_value = 6 }, + { .center_freq = 2442, .hw_value = 7 }, + { .center_freq = 2447, .hw_value = 8 }, + { .center_freq = 2452, .hw_value = 9 }, + { .center_freq = 2457, .hw_value = 10 }, + { .center_freq = 2462, .hw_value = 11 }, + { .center_freq = 2467, .hw_value = 12 }, + { .center_freq = 2472, .hw_value = 13 }, + { .center_freq = 2484, .hw_value = 14 }, +}; + +/* This table contains the hardware specific values for the modulation rates. */ +static const struct ieee80211_rate lbtf_rates[] = { + { .bitrate = 10, + .hw_value = 0, }, + { .bitrate = 20, + .hw_value = 1, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = 2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = 3, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = 5, + .flags = 0 }, + { .bitrate = 90, + .hw_value = 6, + .flags = 0 }, + { .bitrate = 120, + .hw_value = 7, + .flags = 0 }, + { .bitrate = 180, + .hw_value = 8, + .flags = 0 }, + { .bitrate = 240, + .hw_value = 9, + .flags = 0 }, + { .bitrate = 360, + .hw_value = 10, + .flags = 0 }, + { .bitrate = 480, + .hw_value = 11, + .flags = 0 }, + { .bitrate = 540, + .hw_value = 12, + .flags = 0 }, +}; + +static void lbtf_cmd_work(struct work_struct *work) +{ + struct lbtf_private *priv = container_of(work, struct lbtf_private, + cmd_work); + spin_lock_irq(&priv->driver_lock); + /* command response? */ + if (priv->cmd_response_rxed) { + priv->cmd_response_rxed = 0; + spin_unlock_irq(&priv->driver_lock); + lbtf_process_rx_command(priv); + spin_lock_irq(&priv->driver_lock); + } + + if (priv->cmd_timed_out && priv->cur_cmd) { + struct cmd_ctrl_node *cmdnode = priv->cur_cmd; + + if (++priv->nr_retries > 10) { + lbtf_complete_command(priv, cmdnode, + -ETIMEDOUT); + priv->nr_retries = 0; + } else { + priv->cur_cmd = NULL; + + /* Stick it back at the _top_ of the pending + * queue for immediate resubmission */ + list_add(&cmdnode->list, &priv->cmdpendingq); + } + } + priv->cmd_timed_out = 0; + spin_unlock_irq(&priv->driver_lock); + + if (!priv->fw_ready) + return; + /* Execute the next command */ + if (!priv->cur_cmd) + lbtf_execute_next_command(priv); +} + +/** + * lbtf_setup_firmware: initialize firmware. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +static int lbtf_setup_firmware(struct lbtf_private *priv) +{ + int ret = -1; + + /* + * Read priv address from HW + */ + memset(priv->current_addr, 0xff, ETH_ALEN); + ret = lbtf_update_hw_spec(priv); + if (ret) { + ret = -1; + goto done; + } + + lbtf_set_mac_control(priv); + lbtf_set_radio_control(priv); + + ret = 0; +done: + return ret; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + struct lbtf_private *priv = (struct lbtf_private *)data; + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + printk(KERN_DEBUG "libertastf: command timer expired; " + "no pending command\n"); + goto out; + } + + printk(KERN_DEBUG "libertas: command %x timed out\n", + le16_to_cpu(priv->cur_cmd->cmdbuf->command)); + + priv->cmd_timed_out = 1; + queue_work(lbtf_wq, &priv->cmd_work); +out: + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +static int lbtf_init_adapter(struct lbtf_private *priv) +{ + memset(priv->current_addr, 0xff, ETH_ALEN); + mutex_init(&priv->lock); + + priv->vif = NULL; + setup_timer(&priv->command_timer, command_timer_fn, + (unsigned long)priv); + + INIT_LIST_HEAD(&priv->cmdfreeq); + INIT_LIST_HEAD(&priv->cmdpendingq); + + spin_lock_init(&priv->driver_lock); + + /* Allocate the command buffers */ + if (lbtf_allocate_cmd_buffer(priv)) + return -1; + + return 0; +} + +static void lbtf_free_adapter(struct lbtf_private *priv) +{ + lbtf_free_cmd_buffer(priv); + del_timer(&priv->command_timer); +} + +static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct lbtf_private *priv = hw->priv; + + priv->skb_to_tx = skb; + queue_work(lbtf_wq, &priv->tx_work); + /* + * queue will be restarted when we receive transmission feedback if + * there are no buffered multicast frames to send + */ + ieee80211_stop_queues(priv->hw); + return 0; +} + +static void lbtf_tx_work(struct work_struct *work) +{ + struct lbtf_private *priv = container_of(work, struct lbtf_private, + tx_work); + unsigned int len; + struct ieee80211_tx_info *info; + struct txpd *txpd; + struct sk_buff *skb = NULL; + int err; + + if ((priv->vif->type == IEEE80211_IF_TYPE_AP) && + (!skb_queue_empty(&priv->bc_ps_buf))) + skb = skb_dequeue(&priv->bc_ps_buf); + else if (priv->skb_to_tx) { + skb = priv->skb_to_tx; + priv->skb_to_tx = NULL; + } else + return; + + len = skb->len; + info = IEEE80211_SKB_CB(skb); + txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd)); + + if (priv->surpriseremoved) { + dev_kfree_skb_any(skb); + return; + } + + memset(txpd, 0, sizeof(struct txpd)); + /* Activate per-packet rate selection */ + txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | + ieee80211_get_tx_rate(priv->hw, info)->hw_value); + + /* copy destination address from 802.11 header */ + memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, + ETH_ALEN); + txpd->tx_packet_length = cpu_to_le16(len); + txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); + BUG_ON(priv->tx_skb); + spin_lock_irq(&priv->driver_lock); + priv->tx_skb = skb; + err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); + spin_unlock_irq(&priv->driver_lock); + if (err) { + dev_kfree_skb_any(skb); + priv->tx_skb = NULL; + } +} + +static int lbtf_op_start(struct ieee80211_hw *hw) +{ + struct lbtf_private *priv = hw->priv; + void *card = priv->card; + int ret = -1; + + if (!priv->fw_ready) + /* Upload firmware */ + if (priv->hw_prog_firmware(card)) + goto err_prog_firmware; + + /* poke the firmware */ + priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; + priv->radioon = RADIO_ON; + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; + ret = lbtf_setup_firmware(priv); + if (ret) + goto err_prog_firmware; + + if ((priv->fwrelease < LBTF_FW_VER_MIN) || + (priv->fwrelease > LBTF_FW_VER_MAX)) { + ret = -1; + goto err_prog_firmware; + } + + printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); + return 0; + +err_prog_firmware: + priv->hw_reset_device(card); + return ret; +} + +static void lbtf_op_stop(struct ieee80211_hw *hw) +{ + struct lbtf_private *priv = hw->priv; + unsigned long flags; + struct sk_buff *skb; + + struct cmd_ctrl_node *cmdnode; + /* Flush pending command nodes */ + spin_lock_irqsave(&priv->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { + cmdnode->result = -ENOENT; + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + cancel_work_sync(&priv->cmd_work); + cancel_work_sync(&priv->tx_work); + while ((skb = skb_dequeue(&priv->bc_ps_buf))) + dev_kfree_skb_any(skb); + priv->radioon = RADIO_OFF; + lbtf_set_radio_control(priv); + + return; +} + +static int lbtf_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct lbtf_private *priv = hw->priv; + if (priv->vif != NULL) + return -EOPNOTSUPP; + + priv->vif = conf->vif; + switch (conf->type) { + case IEEE80211_IF_TYPE_MESH_POINT: + case IEEE80211_IF_TYPE_AP: + lbtf_set_mode(priv, LBTF_AP_MODE); + break; + case IEEE80211_IF_TYPE_STA: + lbtf_set_mode(priv, LBTF_STA_MODE); + break; + default: + priv->vif = NULL; + return -EOPNOTSUPP; + } + lbtf_set_mac_address(priv, (u8 *) conf->mac_addr); + return 0; +} + +static void lbtf_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct lbtf_private *priv = hw->priv; + + if (priv->vif->type == IEEE80211_IF_TYPE_AP || + priv->vif->type == IEEE80211_IF_TYPE_MESH_POINT) + lbtf_beacon_ctrl(priv, 0, 0); + lbtf_set_mode(priv, LBTF_PASSIVE_MODE); + lbtf_set_bssid(priv, 0, NULL); + priv->vif = NULL; +} + +static int lbtf_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct lbtf_private *priv = hw->priv; + if (conf->channel->center_freq != priv->cur_freq) { + priv->cur_freq = conf->channel->center_freq; + lbtf_set_channel(priv, conf->channel->hw_value); + } + return 0; +} + +static int lbtf_op_config_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct lbtf_private *priv = hw->priv; + struct sk_buff *beacon; + + switch (priv->vif->type) { + case IEEE80211_IF_TYPE_AP: + case IEEE80211_IF_TYPE_MESH_POINT: + beacon = ieee80211_beacon_get(hw, vif); + if (beacon) { + lbtf_beacon_set(priv, beacon); + kfree_skb(beacon); + lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int); + } + break; + default: + break; + } + + if (conf->bssid) { + u8 null_bssid[ETH_ALEN] = {0}; + bool activate = compare_ether_addr(conf->bssid, null_bssid); + lbtf_set_bssid(priv, activate, conf->bssid); + } + + return 0; +} + +#define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) +static void lbtf_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct lbtf_private *priv = hw->priv; + int old_mac_control = priv->mac_control; + int i; + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + if (!changed_flags) + return; + + if (*new_flags & (FIF_PROMISC_IN_BSS)) + priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; + else + priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; + if (*new_flags & (FIF_ALLMULTI) || + mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; + } else if (mc_count) { + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; + priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + priv->nr_of_multicastmacaddr = mc_count; + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + memcpy(&priv->multicastlist[i], mclist->da_addr, + ETH_ALEN); + mclist = mclist->next; + } + lbtf_cmd_set_mac_multicast_addr(priv); + } else { + priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | + CMD_ACT_MAC_ALL_MULTICAST_ENABLE); + if (priv->nr_of_multicastmacaddr) { + priv->nr_of_multicastmacaddr = 0; + lbtf_cmd_set_mac_multicast_addr(priv); + } + } + + + if (priv->mac_control != old_mac_control) + lbtf_set_mac_control(priv); +} + +static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct lbtf_private *priv = hw->priv; + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + if (bss_conf->use_short_preamble) + priv->preamble = CMD_TYPE_SHORT_PREAMBLE; + else + priv->preamble = CMD_TYPE_LONG_PREAMBLE; + lbtf_set_radio_control(priv); + } + + return; +} + +static const struct ieee80211_ops lbtf_ops = { + .tx = lbtf_op_tx, + .start = lbtf_op_start, + .stop = lbtf_op_stop, + .add_interface = lbtf_op_add_interface, + .remove_interface = lbtf_op_remove_interface, + .config = lbtf_op_config, + .config_interface = lbtf_op_config_interface, + .configure_filter = lbtf_op_configure_filter, + .bss_info_changed = lbtf_op_bss_info_changed, +}; + +int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) +{ + struct ieee80211_rx_status stats; + struct rxpd *prxpd; + bool is_qos, is_4addr, is_amsdu, need_padding; + unsigned int flags; + u16 fc, fc_le; + + prxpd = (struct rxpd *) skb->data; + + stats.flag = 0; + if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) + stats.flag |= RX_FLAG_FAILED_FCS_CRC; + stats.freq = priv->cur_freq; + stats.band = IEEE80211_BAND_2GHZ; + stats.signal = prxpd->snr; + stats.noise = prxpd->nf; + stats.qual = prxpd->snr - prxpd->nf; + /* Marvell rate index has a hole at value 4 */ + if (prxpd->rx_rate > 4) + --prxpd->rx_rate; + stats.rate_idx = prxpd->rx_rate; + skb_pull(skb, sizeof(struct rxpd)); + + fc_le = *((__le16 *) skb->data); + fc = le16_to_cpu(fc_le); + flags = le32_to_cpu(*(__le32 *)(skb->data + 4)); + + is_qos = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && + (fc & IEEE80211_STYPE_QOS_DATA); + is_4addr = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + is_amsdu = ((fc & 0x8C) == 0x88) && + (*(skb->data + ieee80211_hdrlen(fc_le) - QOS_CONTROL_LEN) + & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT); + + need_padding = is_qos ^ is_4addr ^ is_amsdu; + if (need_padding) { + memmove(skb->data + 2, skb->data, skb->len); + skb_reserve(skb, 2); + } + + ieee80211_rx_irqsafe(priv->hw, skb, &stats); + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_rx); + +/** + * lbtf_add_card: Add and initialize the card, no fw upload yet. + * + * @card A pointer to card + * + * Returns: pointer to struct lbtf_priv. + */ +struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) +{ + struct ieee80211_hw *hw; + struct lbtf_private *priv = NULL; + + hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); + if (!hw) + goto done; + + priv = hw->priv; + if (lbtf_init_adapter(priv)) + goto err_init_adapter; + + priv->hw = hw; + priv->card = card; + priv->tx_skb = NULL; + + hw->queues = 1; + hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; + hw->extra_tx_headroom = sizeof(struct txpd); + memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); + memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); + priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); + priv->band.bitrates = priv->rates; + priv->band.n_channels = ARRAY_SIZE(lbtf_channels); + priv->band.channels = priv->channels; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + skb_queue_head_init(&priv->bc_ps_buf); + + SET_IEEE80211_DEV(hw, dmdev); + + INIT_WORK(&priv->cmd_work, lbtf_cmd_work); + INIT_WORK(&priv->tx_work, lbtf_tx_work); + if (ieee80211_register_hw(hw)) + goto err_init_adapter; + + goto done; + +err_init_adapter: + lbtf_free_adapter(priv); + ieee80211_free_hw(hw); + priv = NULL; + +done: + return priv; +} +EXPORT_SYMBOL_GPL(lbtf_add_card); + + +int lbtf_remove_card(struct lbtf_private *priv) +{ + struct ieee80211_hw *hw = priv->hw; + + priv->surpriseremoved = 1; + del_timer(&priv->command_timer); + lbtf_free_adapter(priv); + priv->hw = NULL; + ieee80211_unregister_hw(hw); + ieee80211_free_hw(hw); + + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_remove_card); + +void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); + memset(&info->status, 0, sizeof(info->status)); + /* + * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 + * default pid rc algorithm. + * + * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; + */ + info->status.excessive_retries = fail ? 1 : 0; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) + info->flags |= IEEE80211_TX_STAT_ACK; + skb_pull(priv->tx_skb, sizeof(struct txpd)); + ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); + priv->tx_skb = NULL; + if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) + ieee80211_wake_queues(priv->hw); + else + queue_work(lbtf_wq, &priv->tx_work); +} +EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); + +void lbtf_bcn_sent(struct lbtf_private *priv) +{ + struct sk_buff *skb = NULL; + + if (priv->vif->type != IEEE80211_IF_TYPE_AP) + return; + + if (skb_queue_empty(&priv->bc_ps_buf)) { + bool tx_buff_bc = 0; + + while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { + skb_queue_tail(&priv->bc_ps_buf, skb); + tx_buff_bc = 1; + } + if (tx_buff_bc) { + ieee80211_stop_queues(priv->hw); + queue_work(lbtf_wq, &priv->tx_work); + } + } + + skb = ieee80211_beacon_get(priv->hw, priv->vif); + + if (skb) { + lbtf_beacon_set(priv, skb); + kfree_skb(skb); + } +} +EXPORT_SYMBOL_GPL(lbtf_bcn_sent); + +static int __init lbtf_init_module(void) +{ + lbtf_wq = create_workqueue("libertastf"); + if (lbtf_wq == NULL) { + printk(KERN_ERR "libertastf: couldn't create workqueue\n"); + return -ENOMEM; + } + return 0; +} + +static void __exit lbtf_exit_module(void) +{ + destroy_workqueue(lbtf_wq); +} + +module_init(lbtf_init_module); +module_exit(lbtf_exit_module); + +MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); +MODULE_AUTHOR("Cozybit Inc."); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 691cdb49388b808bfbfacaea93afb5c2807db45e Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Thu, 14 Aug 2008 10:41:01 -0700 Subject: libertas_tf: command helper functions for libertas_tf Signed-off-by: Luis Carlos Cobo Signed-off-by: John W. Linville --- drivers/net/wireless/libertas_tf/cmd.c | 669 +++++++++++++++++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 drivers/net/wireless/libertas_tf/cmd.c (limited to 'drivers') diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c new file mode 100644 index 00000000000..fdbcf8ba3e8 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/cmd.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include "libertas_tf.h" + +static const struct channel_range channel_ranges[] = { + { LBTF_REGDOMAIN_US, 1, 12 }, + { LBTF_REGDOMAIN_CA, 1, 12 }, + { LBTF_REGDOMAIN_EU, 1, 14 }, + { LBTF_REGDOMAIN_JP, 1, 14 }, + { LBTF_REGDOMAIN_SP, 1, 14 }, + { LBTF_REGDOMAIN_FR, 1, 14 }, +}; + +static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = +{ + LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, + LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, +}; + +static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); + + +/** + * lbtf_cmd_copyback - Simple callback that copies response back into command + * + * @priv A pointer to struct lbtf_private structure + * @extra A pointer to the original command structure for which + * 'resp' is a response + * @resp A pointer to the command response + * + * Returns: 0 on success, error on failure + */ +int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + struct cmd_header *buf = (void *)extra; + uint16_t copy_len; + + copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); + memcpy(buf, resp, copy_len); + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); + +#define CHAN_TO_IDX(chan) ((chan) - 1) + +static void lbtf_geo_init(struct lbtf_private *priv) +{ + const struct channel_range *range = channel_ranges; + u8 ch; + int i; + + for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) + if (channel_ranges[i].regdomain == priv->regioncode) { + range = &channel_ranges[i]; + break; + } + + for (ch = priv->range.start; ch < priv->range.end; ch++) + priv->channels[CHAN_TO_IDX(ch)].flags = 0; +} + +/** + * lbtf_update_hw_spec: Updates the hardware details. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success, error on failure + */ +int lbtf_update_hw_spec(struct lbtf_private *priv) +{ + struct cmd_ds_get_hw_spec cmd; + int ret = -1; + u32 i; + DECLARE_MAC_BUF(mac); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); + ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); + if (ret) + goto out; + + priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); + + /* The firmware release is in an interesting format: the patch + * level is in the most significant nibble ... so fix that: */ + priv->fwrelease = le32_to_cpu(cmd.fwrelease); + priv->fwrelease = (priv->fwrelease << 8) | + (priv->fwrelease >> 24 & 0xff); + + printk(KERN_INFO "libertastf: %s, fw %u.%u.%up%u, cap 0x%08x\n", + print_mac(mac, cmd.permanentaddr), + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff, + priv->fwcapinfo); + + /* Clamp region code to 8-bit since FW spec indicates that it should + * only ever be 8-bit, even though the field size is 16-bit. Some + * firmware returns non-zero high 8 bits here. + */ + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (priv->regioncode == lbtf_region_code_to_index[i]) + break; + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) + priv->regioncode = 0x10; + + if (priv->current_addr[0] == 0xff) + memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); + + SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); + + lbtf_geo_init(priv); +out: + return ret; +} + +/** + * lbtf_set_channel: Set the radio channel + * + * @priv A pointer to struct lbtf_private structure + * @channel The desired channel, or 0 to clear a locked channel + * + * Returns: 0 on success, error on failure + */ +int lbtf_set_channel(struct lbtf_private *priv, u8 channel) +{ + struct cmd_ds_802_11_rf_channel cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); + cmd.channel = cpu_to_le16(channel); + + return lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); +} + +int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) +{ + struct cmd_ds_802_11_beacon_set cmd; + int size; + + if (beacon->len > MRVL_MAX_BCN_SIZE) + return -1; + size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; + cmd.hdr.size = cpu_to_le16(size); + cmd.len = cpu_to_le16(beacon->len); + memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); + + lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); + return 0; +} + +int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, + int beacon_int) { + struct cmd_ds_802_11_beacon_control cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.beacon_enable = cpu_to_le16(beacon_enable); + cmd.beacon_period = cpu_to_le16(beacon_int); + + lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); + return 0; +} + +static void lbtf_queue_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + + if (!cmdnode) + return; + + if (!cmdnode->cmdbuf->size) + return; + + cmdnode->result = 0; + spin_lock_irqsave(&priv->driver_lock, flags); + list_add_tail(&cmdnode->list, &priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +static void lbtf_submit_command(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_header *cmd; + uint16_t cmdsize; + uint16_t command; + int timeo = 5 * HZ; + int ret; + + cmd = cmdnode->cmdbuf; + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->cur_cmd = cmdnode; + cmdsize = le16_to_cpu(cmd->size); + command = le16_to_cpu(cmd->command); + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (ret) + /* Let the timer kick in and retry, and potentially reset + the whole thing if the condition persists */ + timeo = HZ; + + /* Setup the timer after transmit command */ + mod_timer(&priv->command_timer, jiffies + timeo); +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires priv->driver_lock held. + */ +static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + if (!cmdnode) + return; + + cmdnode->callback = NULL; + cmdnode->callback_arg = 0; + + memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); + + list_add_tail(&cmdnode->list, &priv->cmdfreeq); +} + +static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + cmd->result = result; + cmd->cmdwaitqwoken = 1; + wake_up_interruptible(&cmd->cmdwait_q); + + if (!cmd->callback) + __lbtf_cleanup_and_insert_cmd(priv, cmd); + priv->cur_cmd = NULL; +} + +int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) +{ + struct cmd_ds_mac_multicast_addr cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); + memcpy(cmd.maclist, priv->multicastlist, + priv->nr_of_multicastmacaddr * ETH_ALEN); + + lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); + return 0; +} + +void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) +{ + struct cmd_ds_set_mode cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.mode = cpu_to_le16(mode); + lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); +} + +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid) +{ + struct cmd_ds_set_bssid cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.activate = activate ? 1 : 0; + if (activate) + memcpy(cmd.bssid, bssid, ETH_ALEN); + + lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); +} + +int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) +{ + struct cmd_ds_802_11_mac_address cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + memcpy(cmd.macadd, mac_addr, ETH_ALEN); + + lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); + return 0; +} + +int lbtf_set_radio_control(struct lbtf_private *priv) +{ + int ret = 0; + struct cmd_ds_802_11_radio_control cmd; + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + switch (priv->preamble) { + case CMD_TYPE_SHORT_PREAMBLE: + cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case CMD_TYPE_LONG_PREAMBLE: + cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case CMD_TYPE_AUTO_PREAMBLE: + default: + cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (priv->radioon) + cmd.control |= cpu_to_le16(TURN_ON_RF); + else + cmd.control &= cpu_to_le16(~TURN_ON_RF); + + ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); + return ret; +} + +void lbtf_set_mac_control(struct lbtf_private *priv) +{ + struct cmd_ds_mac_control cmd; + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + + lbtf_cmd_async(priv, CMD_MAC_CONTROL, + &cmd.hdr, sizeof(cmd)); +} + +/** + * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) +{ + u32 bufsize; + u32 i; + struct cmd_ctrl_node *cmdarray; + + /* Allocate and initialize the command array */ + bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; + cmdarray = kzalloc(bufsize, GFP_KERNEL); + if (!cmdarray) + return -1; + priv->cmd_array = cmdarray; + + /* Allocate and initialize each command buffer in the command array */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); + if (!cmdarray[i].cmdbuf) + return -1; + } + + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + init_waitqueue_head(&cmdarray[i].cmdwait_q); + lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); + } + return 0; +} + +/** + * lbtf_free_cmd_buffer - Frees the cmd buffer. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 + */ +int lbtf_free_cmd_buffer(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *cmdarray; + unsigned int i; + + /* need to check if cmd array is allocated or not */ + if (priv->cmd_array == NULL) + return 0; + + cmdarray = priv->cmd_array; + + /* Release shared memory buffers */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + kfree(cmdarray[i].cmdbuf); + cmdarray[i].cmdbuf = NULL; + } + + /* Release cmd_ctrl_node */ + kfree(priv->cmd_array); + priv->cmd_array = NULL; + + return 0; +} + +/** + * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. + */ +static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *tempnode; + unsigned long flags; + + if (!priv) + return NULL; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!list_empty(&priv->cmdfreeq)) { + tempnode = list_first_entry(&priv->cmdfreeq, + struct cmd_ctrl_node, list); + list_del(&tempnode->list); + } else + tempnode = NULL; + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + return tempnode; +} + +/** + * lbtf_execute_next_command: execute next command in cmd pending queue. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +int lbtf_execute_next_command(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_header *cmd; + unsigned long flags; + + /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the + * only caller to us is lbtf_thread() and we get even when a + * data packet is received */ + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + return -1; + } + + if (!list_empty(&priv->cmdpendingq)) { + cmdnode = list_first_entry(&priv->cmdpendingq, + struct cmd_ctrl_node, list); + } + + if (cmdnode) { + cmd = cmdnode->cmdbuf; + + list_del(&cmdnode->list); + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbtf_submit_command(priv, cmdnode); + } else + spin_unlock_irqrestore(&priv->driver_lock, flags); + return 0; +} + +static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, unsigned long, + struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + + if (priv->surpriseremoved) + return ERR_PTR(-ENOENT); + + cmdnode = lbtf_get_cmd_ctrl_node(priv); + if (cmdnode == NULL) { + /* Wake up main thread to execute next command */ + queue_work(lbtf_wq, &priv->cmd_work); + return ERR_PTR(-ENOBUFS); + } + + cmdnode->callback = callback; + cmdnode->callback_arg = callback_arg; + + /* Copy the incoming command to the buffer */ + memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); + + /* Set sequence number, clean result, move to buffer */ + priv->seqnum++; + cmdnode->cmdbuf->command = cpu_to_le16(command); + cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); + cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); + cmdnode->cmdbuf->result = 0; + cmdnode->cmdwaitqwoken = 0; + lbtf_queue_cmd(priv, cmdnode); + queue_work(lbtf_wq, &priv->cmd_work); + + return cmdnode; +} + +void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size) +{ + __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); +} + +int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, + unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + int ret = 0; + + cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, + callback, callback_arg); + if (IS_ERR(cmdnode)) + return PTR_ERR(cmdnode); + + might_sleep(); + ret = wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + if (ret) { + printk(KERN_DEBUG + "libertastf: command 0x%04x interrupted by signal", + command); + return ret; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + ret = cmdnode->result; + if (ret) + printk(KERN_DEBUG "libertastf: command 0x%04x failed: %d\n", + command, ret); + + __lbtf_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(__lbtf_cmd); + +/* Call holding driver_lock */ +void lbtf_cmd_response_rx(struct lbtf_private *priv) +{ + priv->cmd_response_rxed = 1; + queue_work(lbtf_wq, &priv->cmd_work); +} +EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); + +int lbtf_process_rx_command(struct lbtf_private *priv) +{ + uint16_t respcmd, curcmd; + struct cmd_header *resp; + int ret = 0; + unsigned long flags; + uint16_t result; + + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + ret = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + goto done; + } + + resp = (void *)priv->cmd_resp_buff; + curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); + respcmd = le16_to_cpu(resp->command); + result = le16_to_cpu(resp->result); + + if (net_ratelimit()) + printk(KERN_DEBUG "libertastf: cmd response 0x%04x, seq %d, size %d\n", + respcmd, le16_to_cpu(resp->seqnum), + le16_to_cpu(resp->size)); + + if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + if (respcmd != CMD_RET(curcmd)) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (resp->result == cpu_to_le16(0x0004)) { + /* 0x0004 means -EAGAIN. Drop the response, let it time out + and be resubmitted */ + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + /* Now we got response from FW, cancel the command timer */ + del_timer(&priv->command_timer); + priv->cmd_timed_out = 0; + if (priv->nr_retries) + priv->nr_retries = 0; + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + /* + * Handling errors here + */ + switch (respcmd) { + case CMD_RET(CMD_GET_HW_SPEC): + case CMD_RET(CMD_802_11_RESET): + printk(KERN_DEBUG "libertastf: reset failed\n"); + break; + + } + lbtf_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (priv->cur_cmd && priv->cur_cmd->callback) { + ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, + resp); + } + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + lbtf_complete_command(priv, priv->cur_cmd, result); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + mutex_unlock(&priv->lock); + return ret; +} -- cgit v1.2.3 From c305a19a0d0a47ac59a58865a4a63be65b1bf7c8 Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Thu, 14 Aug 2008 10:41:06 -0700 Subject: libertas_tf: usb specific functions The libertas thin firmware only supports usb devices, but the usb functions have been kept separate to ease future support for other devices. Signed-off-by: Luis Carlos Cobo Signed-off-by: John W. Linville --- drivers/net/wireless/libertas_tf/if_usb.c | 766 ++++++++++++++++++++++++++++++ drivers/net/wireless/libertas_tf/if_usb.h | 98 ++++ 2 files changed, 864 insertions(+) create mode 100644 drivers/net/wireless/libertas_tf/if_usb.c create mode 100644 drivers/net/wireless/libertas_tf/if_usb.h (limited to 'drivers') diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c new file mode 100644 index 00000000000..1cc03a8dd67 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -0,0 +1,766 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include +#include +#include +#include +#include + +#define DRV_NAME "lbtf_usb" + +#include "libertas_tf.h" +#include "if_usb.h" + +#define MESSAGE_HEADER_LEN 4 + +static char *lbtf_fw_name = "lbtf_usb.bin"; +module_param_named(fw_name, lbtf_fw_name, charp, 0644); + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { USB_DEVICE(0x1286, 0x2001) }, + { USB_DEVICE(0x05a3, 0x8388) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); +static int if_usb_prog_firmware(struct if_usb_card *cardp); +static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb); +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb, u8 data); +static void if_usb_free(struct if_usb_card *cardp); +static int if_usb_submit_rx_urb(struct if_usb_card *cardp); +static int if_usb_reset_device(struct if_usb_card *cardp); + +/** + * if_usb_wrike_bulk_callback - call back to handle URB status + * + * @param urb pointer to urb structure + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + if (urb->status != 0) + printk(KERN_INFO "libertastf: URB in failure status: %d\n", + urb->status); +} + +/** + * if_usb_free - free tx/rx urb, skb and rx buffer + * + * @param cardp pointer if_usb_card + */ +static void if_usb_free(struct if_usb_card *cardp) +{ + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->cmd_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + usb_free_urb(cardp->cmd_urb); + cardp->cmd_urb = NULL; + + kfree(cardp->ep_out_buf); + cardp->ep_out_buf = NULL; +} + +static void if_usb_setup_firmware(struct lbtf_private *priv) +{ + struct if_usb_card *cardp = priv->card; + struct cmd_ds_set_boot2_ver b2_cmd; + + if_usb_submit_rx_urb(cardp); + b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); + b2_cmd.action = 0; + b2_cmd.version = cardp->boot2_version; + + if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) + printk(KERN_INFO "libertastf: setting boot2 version failed\n"); +} + +static void if_usb_fw_timeo(unsigned long priv) +{ + struct if_usb_card *cardp = (void *)priv; + + if (!cardp->fwdnldover) + /* Download timed out */ + cardp->priv->surpriseremoved = 1; + wake_up(&cardp->fw_wq); +} + +/** + * if_usb_probe - sets the configuration values + * + * @ifnum interface number + * @id pointer to usb_device_id + * + * Returns: 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct lbtf_private *priv; + struct if_usb_card *cardp; + int i; + + udev = interface_to_usbdev(intf); + + cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); + if (!cardp) + goto error; + + setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); + init_waitqueue_head(&cardp->fw_wq); + + cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) { + cardp->ep_in_size = + le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_in = usb_endpoint_num(endpoint); + } else if (usb_endpoint_is_bulk_out(endpoint)) { + cardp->ep_out_size = + le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_out = usb_endpoint_num(endpoint); + } + } + if (!cardp->ep_out_size || !cardp->ep_in_size) + /* Endpoints not found */ + goto dealloc; + + cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->rx_urb) + goto dealloc; + + cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->tx_urb) + goto dealloc; + + cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->cmd_urb) + goto dealloc; + + cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + if (!cardp->ep_out_buf) + goto dealloc; + + priv = lbtf_add_card(cardp, &udev->dev); + if (!priv) + goto dealloc; + + cardp->priv = priv; + + priv->hw_host_to_card = if_usb_host_to_card; + priv->hw_prog_firmware = if_usb_prog_firmware; + priv->hw_reset_device = if_usb_reset_device; + cardp->boot2_version = udev->descriptor.bcdDevice; + + usb_get_dev(udev); + usb_set_intfdata(intf, cardp); + + return 0; + +dealloc: + if_usb_free(cardp); +error: + return -ENOMEM; +} + +/** + * if_usb_disconnect - free resource and cleanup + * + * @intf USB interface structure + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbtf_private *priv = (struct lbtf_private *) cardp->priv; + + if_usb_reset_device(cardp); + + if (priv) + lbtf_remove_card(priv); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); +} + +/** + * if_usb_send_fw_pkt - This function downloads the FW + * + * @priv pointer to struct lbtf_private + * + * Returns: 0 + */ +static int if_usb_send_fw_pkt(struct if_usb_card *cardp) +{ + struct fwdata *fwdata = cardp->ep_out_buf; + u8 *firmware = (u8 *) cardp->fw->data; + + /* If we got a CRC failure on the last block, back + up and retry it */ + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum--; + } + + /* struct fwdata (which we sent to the card) has an + extra __le32 field in between the header and the data, + which is not in the struct fwheader in the actual + firmware binary. Insert the seqnum in the middle... */ + memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + memcpy(fwdata->data, &firmware[cardp->totalbytes], + le32_to_cpu(fwdata->hdr.datalength)); + + fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); + cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); + + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + + le32_to_cpu(fwdata->hdr.datalength), 0); + + if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) + /* Host has finished FW downloading + * Donwloading FW JUMP BLOCK + */ + cardp->fwfinalblk = 1; + + return 0; +} + +static int if_usb_reset_device(struct if_usb_card *cardp) +{ + struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4; + int ret; + + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + + cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset)); + cmd->hdr.result = cpu_to_le16(0); + cmd->hdr.seqnum = cpu_to_le16(0x5a5a); + cmd->action = cpu_to_le16(CMD_ACT_HALT); + usb_tx_block(cardp, cardp->ep_out_buf, + 4 + sizeof(struct cmd_ds_802_11_reset), 0); + + msleep(100); + ret = usb_reset_device(cardp->udev); + msleep(100); + + return ret; +} +EXPORT_SYMBOL_GPL(if_usb_reset_device); + +/** + * usb_tx_block - transfer data to the device + * + * @priv pointer to struct lbtf_private + * @payload pointer to payload data + * @nb data length + * @data non-zero for data, zero for commands + * + * Returns: 0 on success, nonzero otherwise. + */ +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb, u8 data) +{ + struct urb *urb; + + /* check if device is removed */ + if (cardp->priv->surpriseremoved) + return -1; + + if (data) + urb = cardp->tx_urb; + else + urb = cardp->cmd_urb; + + usb_fill_bulk_urb(urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->ep_out), + payload, nb, if_usb_write_bulk_callback, cardp); + + urb->transfer_flags |= URB_ZERO_PACKET; + + if (usb_submit_urb(urb, GFP_ATOMIC)) + return -1; + return 0; +} + +static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, + void (*callbackfn)(struct urb *urb)) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + if (!skb) + return -1; + + cardp->rx_skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, cardp->ep_in), + (void *) (skb->tail), + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + if (usb_submit_urb(cardp->rx_urb, GFP_ATOMIC)) { + kfree_skb(skb); + cardp->rx_skb = NULL; + return -1; + } else + return 0; +} + +static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); +} + +static int if_usb_submit_rx_urb(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct fwsyncheader *syncfwheader; + struct bootcmdresp bcmdresp; + + if (urb->status) { + kfree_skb(skb); + return; + } + + if (cardp->fwdnldover) { + __le32 *tmp = (__le32 *)(skb->data); + + if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && + tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) + /* Firmware ready event received */ + wake_up(&cardp->fw_wq); + else + if_usb_submit_rx_urb_fwload(cardp); + kfree_skb(skb); + return; + } + if (cardp->bootcmdresp <= 0) { + memcpy(&bcmdresp, skb->data, sizeof(bcmdresp)); + + if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + cardp->bootcmdresp = 1; + /* Received valid boot command response */ + return; + } + if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { + if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || + bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || + bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) + cardp->bootcmdresp = -1; + } else if (bcmdresp.cmd == BOOT_CMD_FW_BY_USB && + bcmdresp.result == BOOT_CMD_RESP_OK) + cardp->bootcmdresp = 1; + + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + return; + } + + syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + kfree_skb(skb); + return; + } + + memcpy(syncfwheader, skb->data, sizeof(struct fwsyncheader)); + + if (!syncfwheader->cmd) + cardp->CRC_OK = 1; + else + cardp->CRC_OK = 0; + kfree_skb(skb); + + /* reschedule timer for 200ms hence */ + mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_usb_send_fw_pkt(cardp); + + exit: + if_usb_submit_rx_urb_fwload(cardp); + + kfree(syncfwheader); + + return; +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbtf_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN + || recvlength < MRVDRV_MIN_PKT_LEN) { + kfree_skb(skb); + return; + } + + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + lbtf_rx(priv, skb); +} + +static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, + struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbtf_private *priv) +{ + if (recvlength > LBS_CMD_BUFFER_SIZE) { + kfree_skb(skb); + return; + } + + if (!in_interrupt()) + BUG(); + + spin_lock(&priv->driver_lock); + memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN, + recvlength - MESSAGE_HEADER_LEN); + kfree_skb(skb); + lbtf_cmd_response_rx(priv); + spin_unlock(&priv->driver_lock); +} + +/** + * if_usb_receive - read data received from the device. + * + * @urb pointer to struct urb + */ +static void if_usb_receive(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct lbtf_private *priv = cardp->priv; + int recvlength = urb->actual_length; + uint8_t *recvbuff = NULL; + uint32_t recvtype = 0; + __le32 *pkt = (__le32 *) skb->data; + + if (recvlength) { + if (urb->status) { + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data; + recvtype = le32_to_cpu(pkt[0]); + } else if (urb->status) { + kfree_skb(skb); + return; + } + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + { + /* Event cause handling */ + u32 event_cause = le32_to_cpu(pkt[1]); + + /* Icky undocumented magic special case */ + if (event_cause & 0xffff0000) { + u16 tmp; + u8 retrycnt; + u8 failure; + + tmp = event_cause >> 16; + retrycnt = tmp & 0x00ff; + failure = (tmp & 0xff00) >> 8; + lbtf_send_tx_feedback(priv, retrycnt, failure); + } else if (event_cause == LBTF_EVENT_BCN_SENT) + lbtf_bcn_sent(priv); + else + printk(KERN_DEBUG + "Unsupported notification %d received\n", + event_cause); + kfree_skb(skb); + break; + } + default: + printk(KERN_DEBUG "libertastf: unknown command type 0x%X\n", + recvtype); + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(cardp); +} + +/** + * if_usb_host_to_card - Download data to the device + * + * @priv pointer to struct lbtf_private structure + * @type type of data + * @buf pointer to data buffer + * @len number of bytes + * + * Returns: 0 on success, nonzero otherwise + */ +static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb) +{ + struct if_usb_card *cardp = priv->card; + u8 data = 0; + + if (type == MVMS_CMD) { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + } else { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); + data = 1; + } + + memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); + + return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN, + data); +} + +/** + * if_usb_issue_boot_command - Issue boot command to Boot2. + * + * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM. + * + * Returns: 0 + */ +static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) +{ + struct bootcmd *bootcmd = cardp->ep_out_buf; + + /* Prepare command */ + bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); + bootcmd->cmd = ivalue; + memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); + + /* Issue command */ + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0); + + return 0; +} + + +/** + * check_fwfile_format - Check the validity of Boot2/FW image. + * + * @data pointer to image + * @totlen image length + * + * Returns: 0 if the image is valid, nonzero otherwise. + */ +static int check_fwfile_format(const u8 *data, u32 totlen) +{ + u32 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + struct fwheader *fwh = (void *) data; + + bincmd = le32_to_cpu(fwh->dnldcmd); + blksize = le32_to_cpu(fwh->datalength); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + printk(KERN_INFO + "libertastf: firmware file format check failed\n"); + return ret; +} + + +static int if_usb_prog_firmware(struct if_usb_card *cardp) +{ + int i = 0; + static int reset_count = 10; + int ret = 0; + + ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); + if (ret < 0) { + printk(KERN_INFO "libertastf: firmware %s not found\n", + lbtf_fw_name); + goto done; + } + + if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) + goto release_fw; + +restart: + if (if_usb_submit_rx_urb_fwload(cardp) < 0) { + ret = -1; + goto release_fw; + } + + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp <= 0) { + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + return -1; + } + + i = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + /* Send the first firmware packet... */ + if_usb_send_fw_pkt(cardp); + + /* ... and wait for the process to complete */ + wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || + cardp->fwdnldover); + + del_timer_sync(&cardp->fw_timeout); + usb_kill_urb(cardp->rx_urb); + + if (!cardp->fwdnldover) { + printk(KERN_INFO "libertastf: failed to load fw," + " resetting device!\n"); + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + + printk(KERN_INFO "libertastf: fw download failure\n"); + ret = -1; + goto release_fw; + } + + cardp->priv->fw_ready = 1; + + release_fw: + release_firmware(cardp->fw); + cardp->fw = NULL; + + if_usb_setup_firmware(cardp->priv); + + done: + return ret; +} +EXPORT_SYMBOL_GPL(if_usb_prog_firmware); + + +#define if_usb_suspend NULL +#define if_usb_resume NULL + +static struct usb_driver if_usb_driver = { + .name = DRV_NAME, + .probe = if_usb_probe, + .disconnect = if_usb_disconnect, + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, +}; + +static int __init if_usb_init_module(void) +{ + int ret = 0; + + ret = usb_register(&if_usb_driver); + return ret; +} + +static void __exit if_usb_exit_module(void) +{ + usb_deregister(&if_usb_driver); +} + +module_init(if_usb_init_module); +module_exit(if_usb_exit_module); + +MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver"); +MODULE_AUTHOR("Cozybit Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas_tf/if_usb.h b/drivers/net/wireless/libertas_tf/if_usb.h new file mode 100644 index 00000000000..6fa5b3f59ef --- /dev/null +++ b/drivers/net/wireless/libertas_tf/if_usb.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include +#include + +struct lbtf_private; + +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ + +struct bootcmd { + __le32 magic; + uint8_t cmd; + uint8_t pad[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdresp { + __le32 magic; + uint8_t cmd; + uint8_t result; + uint8_t pad[2]; +}; + +/** USB card description structure*/ +struct if_usb_card { + struct usb_device *udev; + struct urb *rx_urb, *tx_urb, *cmd_urb; + struct lbtf_private *priv; + + struct sk_buff *rx_skb; + + uint8_t ep_in; + uint8_t ep_out; + + int8_t bootcmdresp; + + int ep_in_size; + + void *ep_out_buf; + int ep_out_size; + + const struct firmware *fw; + struct timer_list fw_timeout; + wait_queue_head_t fw_wq; + uint32_t fwseqnum; + uint32_t totalbytes; + uint32_t fwlastblksent; + uint8_t CRC_OK; + uint8_t fwdnldover; + uint8_t fwfinalblk; + + __le16 boot2_version; +}; + +/** fwheader */ +struct fwheader { + __le32 dnldcmd; + __le32 baseaddr; + __le32 datalength; + __le32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct fwdata { + struct fwheader hdr; + __le32 seqnum; + uint8_t data[0]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + __le32 cmd; + __le32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 -- cgit v1.2.3 From d9c6350431d0c8cac000c6a230d9a20dad52e26d Mon Sep 17 00:00:00 2001 From: Luis Carlos Cobo Date: Thu, 14 Aug 2008 10:41:10 -0700 Subject: libertas_tf: config and makefile Signed-off-by: Luis Carlos Cobo Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 13 +++++++++++++ drivers/net/wireless/Makefile | 2 ++ drivers/net/wireless/libertas_tf/Makefile | 6 ++++++ 3 files changed, 21 insertions(+) create mode 100644 drivers/net/wireless/libertas_tf/Makefile (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 9931b5ab59c..1ac46ad48c3 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -300,6 +300,19 @@ config LIBERTAS_DEBUG ---help--- Debugging support. +config LIBERTAS_THINFIRM + tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware" + depends on WLAN_80211 && MAC80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices using thinfirm. + +config LIBERTAS_THINFIRM_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware" + depends on LIBERTAS_THINFIRM && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices using thinfirm. + config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 59aa89ec6e8..c05fd7627f3 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o obj-$(CONFIG_LIBERTAS) += libertas/ +obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ + rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o diff --git a/drivers/net/wireless/libertas_tf/Makefile b/drivers/net/wireless/libertas_tf/Makefile new file mode 100644 index 00000000000..ff5544d6ac9 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/Makefile @@ -0,0 +1,6 @@ +libertas_tf-objs := main.o cmd.o + +libertas_tf_usb-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o +obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o -- cgit v1.2.3 From c94c93da90a9e46a73a5733ff8454fb4b14733fb Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Mon, 28 Jul 2008 23:01:34 -0700 Subject: wireless: replace __FUNCTION__ with __func__ __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/airo.c | 16 ++++++++-------- drivers/net/wireless/hermes.c | 2 +- drivers/net/wireless/ipw2100.c | 2 +- drivers/net/wireless/ipw2200.h | 4 ++-- drivers/net/wireless/iwlwifi/iwl-3945-debug.h | 4 ++-- drivers/net/wireless/libertas/if_cs.c | 2 +- drivers/net/wireless/prism54/isl_ioctl.c | 8 ++++---- drivers/net/wireless/rt2x00/rt2x00.h | 4 ++-- drivers/net/wireless/wl3501_cs.c | 8 ++++---- 9 files changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index b5cd850a4a5..ae58a12befd 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1915,7 +1915,7 @@ static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct airo_info *ai = dev->priv; if (!skb) { - airo_print_err(dev->name, "%s: skb == NULL!",__FUNCTION__); + airo_print_err(dev->name, "%s: skb == NULL!",__func__); return 0; } npacks = skb_queue_len (&ai->txq); @@ -1964,7 +1964,7 @@ static int mpi_send_packet (struct net_device *dev) if ((skb = skb_dequeue(&ai->txq)) == NULL) { airo_print_err(dev->name, "%s: Dequeue'd zero in send_packet()", - __FUNCTION__); + __func__); return 0; } @@ -2115,7 +2115,7 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { u32 *fids = priv->fids; if ( skb == NULL ) { - airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__); + airo_print_err(dev->name, "%s: skb == NULL!", __func__); return 0; } @@ -2186,7 +2186,7 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { } if ( skb == NULL ) { - airo_print_err(dev->name, "%s: skb == NULL!", __FUNCTION__); + airo_print_err(dev->name, "%s: skb == NULL!", __func__); return 0; } @@ -4127,7 +4127,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) airo_print_err(ai->dev->name, "%s: MAC should be disabled (rid=%04x)", - __FUNCTION__, rid); + __func__, rid); memset(&cmd, 0, sizeof(cmd)); memset(&rsp, 0, sizeof(rsp)); @@ -4142,7 +4142,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, &ai->config_desc.rid_desc, sizeof(Rid)); if (len < 4 || len > 2047) { - airo_print_err(ai->dev->name, "%s: len=%d", __FUNCTION__, len); + airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); rc = -1; } else { memcpy((char *)ai->config_desc.virtual_host_addr, @@ -4151,9 +4151,9 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, rc = issuecommand(ai, &cmd, &rsp); if ((rc & 0xff00) != 0) { airo_print_err(ai->dev->name, "%s: Write rid Error %d", - __FUNCTION__, rc); + __func__, rc); airo_print_err(ai->dev->name, "%s: Cmd=%04x", - __FUNCTION__, cmd.cmd); + __func__, cmd.cmd); } if ((rsp.status & 0x7f00)) diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index 29d39105f5b..2cd86af1390 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -463,7 +463,7 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, if (rtype != rid) printk(KERN_WARNING "hermes @ %p: %s(): " "rid (0x%04x) does not match type (0x%04x)\n", - hw->iobase, __FUNCTION__, rid, rtype); + hw->iobase, __func__, rid, rtype); if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) printk(KERN_WARNING "hermes @ %p: " "Truncating LTV record from %d to %d bytes. " diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index 19a401c4a0d..bca74811bc7 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -211,7 +211,7 @@ static u32 ipw2100_debug_level = IPW_DL_NONE; do { \ if (ipw2100_debug_level & (level)) { \ printk(KERN_DEBUG "ipw2100: %c %s ", \ - in_interrupt() ? 'I' : 'U', __FUNCTION__); \ + in_interrupt() ? 'I' : 'U', __func__); \ printk(message); \ } \ } while (0) diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h index d4ab28b73b3..0bad1ec3e7e 100644 --- a/drivers/net/wireless/ipw2200.h +++ b/drivers/net/wireless/ipw2200.h @@ -1394,13 +1394,13 @@ BIT_ARG16(x) #define IPW_DEBUG(level, fmt, args...) \ do { if (ipw_debug_level & (level)) \ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #ifdef CONFIG_IPW2200_DEBUG #define IPW_LL_DEBUG(level, fmt, args...) \ do { if (ipw_debug_level & (level)) \ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IPW_LL_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IPW2200_DEBUG */ diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debug.h b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h index f1d002f7b79..33016fb5e9b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h @@ -34,12 +34,12 @@ extern u32 iwl3945_debug_level; #define IWL_DEBUG(level, fmt, args...) \ do { if (iwl3945_debug_level & (level)) \ printk(KERN_ERR DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #define IWL_DEBUG_LIMIT(level, fmt, args...) \ do { if ((iwl3945_debug_level & (level)) && net_ratelimit()) \ printk(KERN_ERR DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) static inline void iwl3945_print_hex_dump(int level, void *p, u32 len) { diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 04d7a251e3f..92837a2dd6d 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -720,7 +720,7 @@ static int if_cs_host_to_card(struct lbs_private *priv, ret = if_cs_send_cmd(priv, buf, nb); break; default: - lbs_pr_err("%s: unsupported type %d\n", __FUNCTION__, type); + lbs_pr_err("%s: unsupported type %d\n", __func__, type); } lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 3d75a7137d3..16e68f4b654 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -71,7 +71,7 @@ prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode) if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) { printk(KERN_DEBUG "%s(): Sorry, Repeater mode and Secondary mode " - "are not yet supported by this driver.\n", __FUNCTION__); + "are not yet supported by this driver.\n", __func__); return -EINVAL; } @@ -333,7 +333,7 @@ prism54_set_mode(struct net_device *ndev, struct iw_request_info *info, if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) { printk(KERN_DEBUG "%s: %s() You passed a non-valid init_mode.\n", - priv->ndev->name, __FUNCTION__); + priv->ndev->name, __func__); return -EINVAL; } @@ -1234,7 +1234,7 @@ prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info, /* don't know how to disable radio */ printk(KERN_DEBUG "%s: %s() disabling radio is not yet supported.\n", - priv->ndev->name, __FUNCTION__); + priv->ndev->name, __func__); return -ENOTSUPP; } else if (vwrq->fixed) /* currently only fixed value is supported */ @@ -1242,7 +1242,7 @@ prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info, else { printk(KERN_DEBUG "%s: %s() auto power will be implemented later.\n", - priv->ndev->name, __FUNCTION__); + priv->ndev->name, __func__); return -ENOTSUPP; } } diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 8b10ea41b20..82c28dcc471 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -53,11 +53,11 @@ */ #define DEBUG_PRINTK_MSG(__dev, __kernlvl, __lvl, __msg, __args...) \ printk(__kernlvl "%s -> %s: %s - " __msg, \ - wiphy_name((__dev)->hw->wiphy), __FUNCTION__, __lvl, ##__args) + wiphy_name((__dev)->hw->wiphy), __func__, __lvl, ##__args) #define DEBUG_PRINTK_PROBE(__kernlvl, __lvl, __msg, __args...) \ printk(__kernlvl "%s -> %s: %s - " __msg, \ - KBUILD_MODNAME, __FUNCTION__, __lvl, ##__args) + KBUILD_MODNAME, __func__, __lvl, ##__args) #ifdef CONFIG_RT2X00_DEBUG #define DEBUG_PRINTK(__dev, __kernlvl, __lvl, __msg, __args...) \ diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 377141995e3..b6d4e04b8ab 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -79,7 +79,7 @@ static int pc_debug = PCMCIA_DEBUG; module_param(pc_debug, int, 0); #define dprintk(n, format, args...) \ { if (pc_debug > (n)) \ - printk(KERN_INFO "%s: " format "\n", __FUNCTION__ , ##args); } + printk(KERN_INFO "%s: " format "\n", __func__ , ##args); } #else #define dprintk(n, format, args...) #endif @@ -470,7 +470,7 @@ static int wl3501_pwr_mgmt(struct wl3501_card *this, int suspend) spin_unlock_irqrestore(&this->lock, flags); rc = wait_event_interruptible(this->wait, this->sig_pwr_mgmt_confirm.status != 255); - printk(KERN_INFO "%s: %s status=%d\n", __FUNCTION__, + printk(KERN_INFO "%s: %s status=%d\n", __func__, suspend ? "suspend" : "resume", this->sig_pwr_mgmt_confirm.status); goto out; @@ -1199,7 +1199,7 @@ static int wl3501_reset_board(struct wl3501_card *this) } WL3501_NOPLOOP(10); } - printk(KERN_WARNING "%s: failed to reset the board!\n", __FUNCTION__); + printk(KERN_WARNING "%s: failed to reset the board!\n", __func__); rc = -ENODEV; out: return rc; @@ -1250,7 +1250,7 @@ static int wl3501_init_firmware(struct wl3501_card *this) out: return rc; fail: - printk(KERN_WARNING "%s: failed!\n", __FUNCTION__); + printk(KERN_WARNING "%s: failed!\n", __func__); goto out; } -- cgit v1.2.3 From 2bb057d07a0bc17475a7bf897fc41667ab08b73f Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:37:44 +0200 Subject: rt2x00: Implement HW encryption Various rt2x00 devices support hardware encryption. Most of them require the IV/EIV to be generated by mac80211, but require it to be provided seperately instead of within the frame itself. This means that rt2x00lib should extract the data from the frame and place it in the frame descriptor. During RX the IV/EIV is provided in the descriptor by the hardware which means that it should be inserted into the frame by rt2x00lib. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/Kconfig | 4 + drivers/net/wireless/rt2x00/Makefile | 1 + drivers/net/wireless/rt2x00/rt2x00.h | 39 +++++- drivers/net/wireless/rt2x00/rt2x00crypto.c | 215 +++++++++++++++++++++++++++++ drivers/net/wireless/rt2x00/rt2x00debug.c | 97 +++++++++++++ drivers/net/wireless/rt2x00/rt2x00dev.c | 30 +++- drivers/net/wireless/rt2x00/rt2x00lib.h | 47 +++++++ drivers/net/wireless/rt2x00/rt2x00mac.c | 110 +++++++++++++-- drivers/net/wireless/rt2x00/rt2x00queue.c | 81 +++++++++-- drivers/net/wireless/rt2x00/rt2x00queue.h | 73 +++++++++- drivers/net/wireless/rt2x00/rt2x00reg.h | 19 ++- 11 files changed, 686 insertions(+), 30 deletions(-) create mode 100644 drivers/net/wireless/rt2x00/rt2x00crypto.c (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index d485a86bba7..2f60e175f03 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -33,6 +33,10 @@ config RT2X00_LIB_FIRMWARE depends on RT2X00_LIB select FW_LOADER +config RT2X00_LIB_CRYPTO + boolean + depends on RT2X00_LIB + config RT2X00_LIB_RFKILL boolean depends on RT2X00_LIB diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index 1087dbcf1a0..917cb4f3b03 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -3,6 +3,7 @@ rt2x00lib-y += rt2x00mac.o rt2x00lib-y += rt2x00config.o rt2x00lib-y += rt2x00queue.o rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o +rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o rt2x00lib-$(CONFIG_RT2X00_LIB_RFKILL) += rt2x00rfkill.o rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 82c28dcc471..0ffd972bb85 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -451,6 +451,23 @@ struct rt2x00lib_erp { int ack_consume_time; }; +/* + * Configuration structure for hardware encryption. + */ +struct rt2x00lib_crypto { + enum cipher cipher; + + enum set_key_cmd cmd; + const u8 *address; + + u32 bssidx; + u32 aid; + + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +}; + /* * Configuration structure wrapper around the * rt2x00 interface configuration handler. @@ -547,6 +564,12 @@ struct rt2x00lib_ops { /* * Configuration handlers. */ + int (*config_shared_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + int (*config_pairwise_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); void (*config_filter) (struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags); void (*config_intf) (struct rt2x00_dev *rt2x00dev, @@ -609,7 +632,7 @@ enum rt2x00_flags { DEVICE_DIRTY_CONFIG, /* - * Driver features + * Driver requirements */ DRIVER_REQUIRE_FIRMWARE, DRIVER_REQUIRE_BEACON_GUARD, @@ -618,9 +641,14 @@ enum rt2x00_flags { DRIVER_REQUIRE_DMA, /* - * Driver configuration + * Driver features */ CONFIG_SUPPORT_HW_BUTTON, + CONFIG_SUPPORT_HW_CRYPTO, + + /* + * Driver configuration + */ CONFIG_FRAME_TYPE, CONFIG_RF_SEQUENCE, CONFIG_EXTERNAL_LNA_A, @@ -966,6 +994,13 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, int mc_count, struct dev_addr_list *mc_list); +#ifdef CONFIG_RT2X00_LIB_CRYPTO +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_address, const u8 *address, + struct ieee80211_key_conf *key); +#else +#define rt2x00mac_set_key NULL +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c new file mode 100644 index 00000000000..e1448cfa944 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -0,0 +1,215 @@ +/* + Copyright (C) 2004 - 2008 rt2x00 SourceForge Project + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the + Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 crypto specific routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + switch (key->alg) { + case ALG_WEP: + if (key->keylen == LEN_WEP40) + return CIPHER_WEP64; + else + return CIPHER_WEP128; + case ALG_TKIP: + return CIPHER_TKIP; + case ALG_CCMP: + return CIPHER_AES; + default: + return CIPHER_NONE; + } +} + +unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info) +{ + struct ieee80211_key_conf *key = tx_info->control.hw_key; + unsigned int overhead = 0; + + /* + * Extend frame length to include IV/EIV/ICV/MMIC, + * note that these lengths should only be added when + * mac80211 does not generate it. + */ + overhead += tx_info->control.icv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + overhead += tx_info->control.iv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { + if (key->alg == ALG_TKIP) + overhead += 8; + } + + return overhead; +} + +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb); + + if (unlikely(!iv_len)) + return; + + /* Copy IV/EIV data */ + if (iv_len >= 4) + memcpy(&skbdesc->iv, skb->data + header_length, 4); + if (iv_len >= 8) + memcpy(&skbdesc->eiv, skb->data + header_length + 4, 4); + + /* Move ieee80211 header */ + memmove(skb->data + iv_len, skb->data, header_length); + + /* Pull buffer to correct size */ + skb_pull(skb, iv_len); + + /* IV/EIV data has officially be stripped */ + skbdesc->flags |= FRAME_DESC_IV_STRIPPED; +} + +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb); + const unsigned int iv_len = + ((!!(skbdesc->iv)) * 4) + ((!!(skbdesc->eiv)) * 4); + + if (!(skbdesc->flags & FRAME_DESC_IV_STRIPPED)) + return; + + skb_push(skb, iv_len); + + /* Move ieee80211 header */ + memmove(skb->data, skb->data + iv_len, header_length); + + /* Copy IV/EIV data */ + if (iv_len >= 4) + memcpy(skb->data + header_length, &skbdesc->iv, 4); + if (iv_len >= 8) + memcpy(skb->data + header_length + 4, &skbdesc->eiv, 4); + + /* IV/EIV data has returned into the frame */ + skbdesc->flags &= ~FRAME_DESC_IV_STRIPPED; +} + +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ + unsigned int payload_len = rxdesc->size - header_length; + unsigned int iv_len; + unsigned int icv_len; + unsigned int transfer = 0; + + /* + * WEP64/WEP128: Provides IV & ICV + * TKIP: Provides IV/EIV & ICV + * AES: Provies IV/EIV & ICV + */ + switch (rxdesc->cipher) { + case CIPHER_WEP64: + case CIPHER_WEP128: + iv_len = 4; + icv_len = 4; + break; + case CIPHER_TKIP: + iv_len = 8; + icv_len = 4; + break; + case CIPHER_AES: + iv_len = 8; + icv_len = 8; + break; + default: + /* Unsupport type */ + return; + } + + /* + * Make room for new data, note that we increase both + * headsize and tailsize when required. The tailsize is + * only needed when ICV data needs to be inserted and + * the padding is smaller then the ICV data. + * When alignment requirements is greater then the + * ICV data we must trim the skb to the correct size + * because we need to remove the extra bytes. + */ + skb_push(skb, iv_len + align); + if (align < icv_len) + skb_put(skb, icv_len - align); + else if (align > icv_len) + skb_trim(skb, rxdesc->size + iv_len + icv_len); + + /* Move ieee80211 header */ + memmove(skb->data + transfer, + skb->data + transfer + iv_len + align, + header_length); + transfer += header_length; + + /* Copy IV data */ + if (iv_len >= 4) { + memcpy(skb->data + transfer, &rxdesc->iv, 4); + transfer += 4; + } + + /* Copy EIV data */ + if (iv_len >= 8) { + memcpy(skb->data + transfer, &rxdesc->eiv, 4); + transfer += 4; + } + + /* Move payload */ + if (align) { + memmove(skb->data + transfer, + skb->data + transfer + align, + payload_len); + } + + /* + * NOTE: Always count the payload as transfered, + * even when alignment was set to zero. This is required + * for determining the correct offset for the ICV data. + */ + transfer += payload_len; + + /* Copy ICV data */ + if (icv_len >= 4) { + memcpy(skb->data + transfer, &rxdesc->icv, 4); + /* + * AES appends 8 bytes, we can't fill the upper + * 4 bytes, but mac80211 doesn't care about what + * we provide here anyway and strips it immediately. + */ + transfer += icv_len; + } + + /* IV/EIV/ICV has been inserted into frame */ + rxdesc->size = transfer; + rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; +} diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 6bee1d611bb..5cf4c859e39 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -35,6 +35,13 @@ #define MAX_LINE_LENGTH 64 +struct rt2x00debug_crypto { + unsigned long success; + unsigned long icv_error; + unsigned long mic_error; + unsigned long key_error; +}; + struct rt2x00debug_intf { /* * Pointer to driver structure where @@ -63,6 +70,7 @@ struct rt2x00debug_intf { * - queue folder * - frame dump file * - queue stats file + * - crypto stats file */ struct dentry *driver_folder; struct dentry *driver_entry; @@ -80,6 +88,7 @@ struct rt2x00debug_intf { struct dentry *queue_folder; struct dentry *queue_frame_dump_entry; struct dentry *queue_stats_entry; + struct dentry *crypto_stats_entry; /* * The frame dump file only allows a single reader, @@ -97,6 +106,12 @@ struct rt2x00debug_intf { struct sk_buff_head frame_dump_skbqueue; wait_queue_head_t frame_dump_waitqueue; + /* + * HW crypto statistics. + * All statistics are stored seperately per cipher type. + */ + struct rt2x00debug_crypto crypto_stats[CIPHER_MAX]; + /* * Driver and chipset files will use a data buffer * that has been created in advance. This will simplify @@ -114,6 +129,25 @@ struct rt2x00debug_intf { unsigned int offset_rf; }; +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + enum cipher cipher, enum rx_crypto status) +{ + struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; + + if (cipher == CIPHER_TKIP_NO_MIC) + cipher = CIPHER_TKIP; + if (cipher == CIPHER_NONE || cipher > CIPHER_MAX) + return; + + /* Remove CIPHER_NONE index */ + cipher--; + + intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS); + intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV); + intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC); + intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY); +} + void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, enum rt2x00_dump_type type, struct sk_buff *skb) { @@ -327,6 +361,59 @@ static const struct file_operations rt2x00debug_fop_queue_stats = { .release = rt2x00debug_file_release, }; +#ifdef CONFIG_RT2X00_LIB_CRYPTO +static ssize_t rt2x00debug_read_crypto_stats(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + char *name[] = { "WEP64", "WEP128", "TKIP", "AES" }; + char *data; + char *temp; + size_t size; + unsigned int i; + + if (*offset) + return 0; + + data = kzalloc((1 + CIPHER_MAX)* MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return -ENOMEM; + + temp = data; + temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n"); + + for (i = 0; i < CIPHER_MAX; i++) { + temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i], + intf->crypto_stats[i].success, + intf->crypto_stats[i].icv_error, + intf->crypto_stats[i].mic_error, + intf->crypto_stats[i].key_error); + } + + size = strlen(data); + size = min(size, length); + + if (copy_to_user(buf, data, size)) { + kfree(data); + return -EFAULT; + } + + kfree(data); + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_crypto_stats = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_crypto_stats, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, +}; +#endif + #define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ static ssize_t rt2x00debug_read_##__name(struct file *file, \ char __user *buf, \ @@ -569,6 +656,13 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) debugfs_create_file("queue", S_IRUSR, intf->queue_folder, intf, &rt2x00debug_fop_queue_stats); +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) + intf->crypto_stats_entry = + debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, + intf, &rt2x00debug_fop_crypto_stats); +#endif + return; exit: @@ -587,6 +681,9 @@ void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) skb_queue_purge(&intf->frame_dump_skbqueue); +#ifdef CONFIG_RT2X00_LIB_CRYPTO + debugfs_remove(intf->crypto_stats_entry); +#endif debugfs_remove(intf->queue_stats_entry); debugfs_remove(intf->queue_frame_dump_entry); debugfs_remove(intf->queue_folder); diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index f42283ad7b0..182952249a1 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -507,6 +507,15 @@ void rt2x00lib_txdone(struct queue_entry *entry, */ rt2x00queue_unmap_skb(rt2x00dev, entry->skb); + /* + * If the IV/EIV data was stripped from the frame before it was + * passed to the hardware, we should now reinsert it again because + * mac80211 will expect the the same data to be present it the + * frame as it was passed to us. + */ + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) + rt2x00crypto_tx_insert_iv(entry->skb); + /* * Send frame to debugfs immediately, after this call is completed * we are going to overwrite the skb->cb array. @@ -585,7 +594,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, struct ieee80211_supported_band *sband; struct ieee80211_hdr *hdr; const struct rt2x00_rate *rate; - unsigned int header_size; + unsigned int header_length; unsigned int align; unsigned int i; int idx = -1; @@ -613,10 +622,19 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, * The data behind the ieee80211 header must be * aligned on a 4 byte boundary. */ - header_size = ieee80211_get_hdrlen_from_skb(entry->skb); - align = ((unsigned long)(entry->skb->data + header_size)) & 3; + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); + align = ((unsigned long)(entry->skb->data + header_length)) & 3; - if (align) { + /* + * Hardware might have stripped the IV/EIV/ICV data, + * in that case it is possible that the data was + * provided seperately (through hardware descriptor) + * in which case we should reinsert the data into the frame. + */ + if ((rxdesc.flags & RX_FLAG_IV_STRIPPED)) { + rt2x00crypto_rx_insert_iv(entry->skb, align, + header_length, &rxdesc); + } else if (align) { skb_push(entry->skb, align); /* Move entire frame in 1 command */ memmove(entry->skb->data, entry->skb->data + align, @@ -657,6 +675,10 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, (rxdesc.dev_flags & RXDONE_MY_BSS)) rt2x00lib_update_link_stats(&rt2x00dev->link, rxdesc.rssi); + rt2x00debug_update_crypto(rt2x00dev, + rxdesc.cipher, + rxdesc.cipher_status); + rt2x00dev->link.qual.rx_success++; rx_status->mactime = rxdesc.timestamp; diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index c5fb3a72cf3..7bbc16b1b6c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -181,6 +181,8 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev); void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev); void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, enum rt2x00_dump_type type, struct sk_buff *skb); +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + enum cipher cipher, enum rx_crypto status); #else static inline void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) { @@ -195,8 +197,53 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) { } + +static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + enum cipher cipher, + enum rx_crypto status) +{ +} #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +/* + * Crypto handlers. + */ +#ifdef CONFIG_RT2X00_LIB_CRYPTO +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key); +unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info); +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len); +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb); +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc); +#else +static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + return CIPHER_NONE; +} + +static inline unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info) +{ + return 0; +} + +static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, + unsigned int iv_len) +{ +} + +static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb) +{ +} + +static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int align, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ +} +#endif + /* * RFkill handlers. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 4b9fde903cd..3af42733941 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -36,22 +36,22 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(frag_skb); struct ieee80211_tx_info *rts_info; struct sk_buff *skb; - int size; + unsigned int data_length; int retval = 0; if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) - size = sizeof(struct ieee80211_cts); + data_length = sizeof(struct ieee80211_cts); else - size = sizeof(struct ieee80211_rts); + data_length = sizeof(struct ieee80211_rts); - skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); + skb = dev_alloc_skb(data_length + rt2x00dev->hw->extra_tx_headroom); if (unlikely(!skb)) { WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); return -ENOMEM; } skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); - skb_put(skb, size); + skb_put(skb, data_length); /* * Copy TX information over from original frame to @@ -64,7 +64,6 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, */ memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); rts_info = IEEE80211_SKB_CB(skb); - rts_info->control.hw_key = NULL; rts_info->flags &= ~IEEE80211_TX_CTL_USE_RTS_CTS; rts_info->flags &= ~IEEE80211_TX_CTL_USE_CTS_PROTECT; rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS; @@ -74,13 +73,24 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, else rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + skb->do_not_encrypt = 1; + + /* + * RTS/CTS frame should use the length of the frame plus any + * encryption overhead that will be added by the hardware. + */ +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (!frag_skb->do_not_encrypt) + data_length += rt2x00crypto_tx_overhead(tx_info); +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif, - frag_skb->data, size, tx_info, + frag_skb->data, data_length, tx_info, (struct ieee80211_cts *)(skb->data)); else ieee80211_rts_get(rt2x00dev->hw, tx_info->control.vif, - frag_skb->data, size, tx_info, + frag_skb->data, data_length, tx_info, (struct ieee80211_rts *)(skb->data)); retval = rt2x00queue_write_tx_frame(queue, skb); @@ -464,6 +474,90 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); +#ifdef CONFIG_RT2X00_LIB_CRYPTO +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_address, const u8 *address, + struct ieee80211_key_conf *key) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + int (*set_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + struct rt2x00lib_crypto crypto; + + if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) + return -EOPNOTSUPP; + else if (key->keylen > 32) + return -ENOSPC; + + memset(&crypto, 0, sizeof(crypto)); + + /* + * When in STA mode, bssidx is always 0 otherwise local_address[5] + * contains the bss number, see BSS_ID_MASK comments for details. + */ + if (rt2x00dev->intf_sta_count) + crypto.bssidx = 0; + else + crypto.bssidx = + local_address[5] & (rt2x00dev->ops->max_ap_intf - 1); + + crypto.cipher = rt2x00crypto_key_to_cipher(key); + if (crypto.cipher == CIPHER_NONE) + return -EOPNOTSUPP; + + crypto.cmd = cmd; + crypto.address = address; + + if (crypto.cipher == CIPHER_TKIP) { + if (key->keylen > NL80211_TKIP_DATA_OFFSET_ENCR_KEY) + memcpy(&crypto.key, + &key->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY], + sizeof(crypto.key)); + + if (key->keylen > NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) + memcpy(&crypto.tx_mic, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + sizeof(crypto.tx_mic)); + + if (key->keylen > NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY) + memcpy(&crypto.rx_mic, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + sizeof(crypto.rx_mic)); + } else + memcpy(&crypto.key, &key->key[0], key->keylen); + + /* + * Each BSS has a maximum of 4 shared keys. + * Shared key index values: + * 0) BSS0 key0 + * 1) BSS0 key1 + * ... + * 4) BSS1 key0 + * ... + * 8) BSS2 key0 + * ... + * Both pairwise as shared key indeces are determined by + * driver. This is required because the hardware requires + * keys to be assigned in correct order (When key 1 is + * provided but key 0 is not, then the key is not found + * by the hardware during RX). + */ + key->hw_key_idx = 0; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + set_key = rt2x00dev->ops->lib->config_pairwise_key; + else + set_key = rt2x00dev->ops->lib->config_shared_key; + + if (!set_key) + return -EOPNOTSUPP; + + return set_key(rt2x00dev, &crypto, key); +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_key); +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 898cdd7f57d..c0f97c53e5c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -33,10 +33,11 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct rt2x00_dev *rt2x00dev, struct queue_entry *entry) { - unsigned int frame_size; - unsigned int reserved_size; struct sk_buff *skb; struct skb_frame_desc *skbdesc; + unsigned int frame_size; + unsigned int head_size = 0; + unsigned int tail_size = 0; /* * The frame size includes descriptor size, because the @@ -49,16 +50,32 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct rt2x00_dev *rt2x00dev, * this means we need at least 3 bytes for moving the frame * into the correct offset. */ - reserved_size = 4; + head_size = 4; + + /* + * For IV/EIV/ICV assembly we must make sure there is + * at least 8 bytes bytes available in headroom for IV/EIV + * and 4 bytes for ICV data as tailroon. + */ +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { + head_size += 8; + tail_size += 4; + } +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ /* * Allocate skbuffer. */ - skb = dev_alloc_skb(frame_size + reserved_size); + skb = dev_alloc_skb(frame_size + head_size + tail_size); if (!skb) return NULL; - skb_reserve(skb, reserved_size); + /* + * Make sure we not have a frame with the requested bytes + * available in the head and tail. + */ + skb_reserve(skb, head_size); skb_put(skb, frame_size); /* @@ -140,7 +157,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, txdesc->cw_max = entry->queue->cw_max; txdesc->aifs = entry->queue->aifs; - /* Data length should be extended with 4 bytes for CRC */ + /* Data length + CRC + IV/EIV/ICV/MMIC (when using encryption) */ data_length = entry->skb->len + 4; /* @@ -149,6 +166,35 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) __set_bit(ENTRY_TXD_ACK, &txdesc->flags); +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) && + !entry->skb->do_not_encrypt) { + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + + __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); + + txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); + + if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); + + txdesc->key_idx = hw_key->hw_key_idx; + txdesc->iv_offset = ieee80211_get_hdrlen_from_skb(entry->skb); + + /* + * Extend frame length to include all encryption overhead + * that will be added by the hardware. + */ + data_length += rt2x00crypto_tx_overhead(tx_info); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) + __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); + } +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + /* * Check if this is a RTS/CTS frame */ @@ -305,6 +351,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); struct txentry_desc txdesc; struct skb_frame_desc *skbdesc; + unsigned int iv_len = IEEE80211_SKB_CB(skb)->control.iv_len; if (unlikely(rt2x00queue_full(queue))) return -EINVAL; @@ -326,15 +373,33 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) rt2x00queue_create_tx_descriptor(entry, &txdesc); /* - * skb->cb array is now ours and we are free to use it. + * All information is retreived from the skb->cb array, + * now we should claim ownership of the driver part of that + * array. */ skbdesc = get_skb_frame_desc(entry->skb); memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = entry; + /* + * When hardware encryption is supported, and this frame + * is to be encrypted, we should strip the IV/EIV data from + * the frame so we can provide it to the driver seperately. + */ + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) + rt2x00crypto_tx_remove_iv(skb, iv_len); + + /* + * It could be possible that the queue was corrupted and this + * call failed. Just drop the frame, we cannot rollback and pass + * the frame to mac80211 because the skb->cb has now been tainted. + */ if (unlikely(queue->rt2x00dev->ops->lib->write_tx_data(entry))) { __clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - return -EIO; + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + return 0; } if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags)) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index ff78e52ce43..37f3f98d58a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -87,10 +87,13 @@ enum data_queue_qid { * * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX + * @FRAME_DESC_IV_STRIPPED: Frame contained a IV/EIV provided by + * mac80211 but was stripped for processing by the driver. */ enum skb_frame_desc_flags { - SKBDESC_DMA_MAPPED_RX = (1 << 0), - SKBDESC_DMA_MAPPED_TX = (1 << 1), + SKBDESC_DMA_MAPPED_RX = 1 << 0, + SKBDESC_DMA_MAPPED_TX = 1 << 1, + FRAME_DESC_IV_STRIPPED = 1 << 2, }; /** @@ -104,6 +107,8 @@ enum skb_frame_desc_flags { * @desc: Pointer to descriptor part of the frame. * Note that this pointer could point to something outside * of the scope of the skb->data pointer. + * @iv: IV data used during encryption/decryption. + * @eiv: EIV data used during encryption/decryption. * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. * @entry: The entry to which this sk buffer belongs. */ @@ -113,6 +118,9 @@ struct skb_frame_desc { unsigned int desc_len; void *desc; + __le32 iv; + __le32 eiv; + dma_addr_t skb_dma; struct queue_entry *entry; @@ -152,7 +160,11 @@ enum rxdone_entry_desc_flags { * @size: Data size of the received frame. * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). - + * @cipher: Cipher type used during decryption. + * @cipher_status: Decryption status. + * @iv: IV data used during decryption. + * @eiv: EIV data used during decryption. + * @icv: ICV data used during decryption. */ struct rxdone_entry_desc { u64 timestamp; @@ -161,6 +173,12 @@ struct rxdone_entry_desc { int size; int flags; int dev_flags; + u8 cipher; + u8 cipher_status; + + __le32 iv; + __le32 eiv; + __le32 icv; }; /** @@ -206,6 +224,10 @@ struct txdone_entry_desc { * @ENTRY_TXD_BURST: This frame belongs to the same burst event. * @ENTRY_TXD_ACK: An ACK is required for this frame. * @ENTRY_TXD_RETRY_MODE: When set, the long retry count is used. + * @ENTRY_TXD_ENCRYPT: This frame should be encrypted. + * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). + * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. + * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. */ enum txentry_desc_flags { ENTRY_TXD_RTS_FRAME, @@ -218,6 +240,10 @@ enum txentry_desc_flags { ENTRY_TXD_BURST, ENTRY_TXD_ACK, ENTRY_TXD_RETRY_MODE, + ENTRY_TXD_ENCRYPT, + ENTRY_TXD_ENCRYPT_PAIRWISE, + ENTRY_TXD_ENCRYPT_IV, + ENTRY_TXD_ENCRYPT_MMIC, }; /** @@ -236,6 +262,9 @@ enum txentry_desc_flags { * @ifs: IFS value. * @cw_min: cwmin value. * @cw_max: cwmax value. + * @cipher: Cipher type used for encryption. + * @key_idx: Key index used for encryption. + * @iv_offset: Position where IV should be inserted by hardware. */ struct txentry_desc { unsigned long flags; @@ -252,6 +281,10 @@ struct txentry_desc { short ifs; short cw_min; short cw_max; + + enum cipher cipher; + u16 key_idx; + u16 iv_offset; }; /** @@ -484,25 +517,51 @@ static inline int rt2x00queue_threshold(struct data_queue *queue) } /** - * rt2x00_desc_read - Read a word from the hardware descriptor. + * _rt2x00_desc_read - Read a word from the hardware descriptor. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be read. + * @value: Address where the descriptor value should be written into. + */ +static inline void _rt2x00_desc_read(__le32 *desc, const u8 word, __le32 *value) +{ + *value = desc[word]; +} + +/** + * rt2x00_desc_read - Read a word from the hardware descriptor, this + * function will take care of the byte ordering. * @desc: Base descriptor address * @word: Word index from where the descriptor should be read. * @value: Address where the descriptor value should be written into. */ static inline void rt2x00_desc_read(__le32 *desc, const u8 word, u32 *value) { - *value = le32_to_cpu(desc[word]); + __le32 tmp; + _rt2x00_desc_read(desc, word, &tmp); + *value = le32_to_cpu(tmp); +} + +/** + * rt2x00_desc_write - write a word to the hardware descriptor, this + * function will take care of the byte ordering. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be written. + * @value: Value that should be written into the descriptor. + */ +static inline void _rt2x00_desc_write(__le32 *desc, const u8 word, __le32 value) +{ + desc[word] = value; } /** - * rt2x00_desc_write - wrote a word to the hardware descriptor. + * rt2x00_desc_write - write a word to the hardware descriptor. * @desc: Base descriptor address * @word: Word index from where the descriptor should be written. * @value: Value that should be written into the descriptor. */ static inline void rt2x00_desc_write(__le32 *desc, const u8 word, u32 value) { - desc[word] = cpu_to_le32(value); + _rt2x00_desc_write(desc, word, cpu_to_le32(value)); } #endif /* RT2X00QUEUE_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index 7e88ce5651b..6d5acf99a1c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -26,6 +26,16 @@ #ifndef RT2X00REG_H #define RT2X00REG_H +/* + * RX crypto status + */ +enum rx_crypto { + RX_CRYPTO_SUCCESS = 0, + RX_CRYPTO_FAIL_ICV = 1, + RX_CRYPTO_FAIL_MIC = 2, + RX_CRYPTO_FAIL_KEY = 3, +}; + /* * Antenna values */ @@ -104,7 +114,14 @@ enum cipher { */ CIPHER_CKIP64 = 5, CIPHER_CKIP128 = 6, - CIPHER_TKIP_NO_MIC = 7, + CIPHER_TKIP_NO_MIC = 7, /* Don't send to device */ + +/* + * Max cipher type. + * Note that CIPHER_NONE isn't counted, and CKIP64 and CKIP128 + * are excluded due to limitations in mac80211. + */ + CIPHER_MAX = 4, }; /* -- cgit v1.2.3 From 61e754f44b57060464b1fcf261a8428ecfa23bed Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:38:02 +0200 Subject: rt2x00: Implement HW encryption (rt61pci) rt61pci supports hardware encryption. rt61pci supports up to 4 shared keys and up to 64 pairwise keys. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/Kconfig | 1 + drivers/net/wireless/rt2x00/rt61pci.c | 256 +++++++++++++++++++++++++++++++++- drivers/net/wireless/rt2x00/rt61pci.h | 21 ++- 3 files changed, 270 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 2f60e175f03..f333f6100d0 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -107,6 +107,7 @@ config RT61PCI depends on PCI select RT2X00_LIB_PCI select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO select CRC_ITU_T select EEPROM_93CX6 ---help--- diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 087e90b328c..86e7a50374b 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -346,6 +346,204 @@ static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev, /* * Configuration handlers. */ +static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt2x00pci_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? (ffz(reg) - 1) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2x00pci_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00pci_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00pci_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00pci_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00pci_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided seperately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + mask = 1 << key->hw_key_idx; + + rt2x00pci_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00pci_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; +} + +static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple, read + * the first register, if that is full move to + * the next register. + * When both registers are full, we drop the key, + * otherwise we use the first invalid entry. + */ + rt2x00pci_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt2x00pci_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } + + key->hw_key_idx += reg ? (ffz(reg) - 1) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; + + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2x00pci_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt2x00pci_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); + + /* + * Enable pairwise lookup table for given BSS idx, + * without this received frames will not be decrypted + * by the hardware. + */ + rt2x00pci_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt2x00pci_register_write(rt2x00dev, SEC_CSR4, reg); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided seperately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; + + rt2x00pci_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00pci_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt2x00pci_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00pci_register_write(rt2x00dev, SEC_CSR3, reg); + } + + return 0; +} + static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { @@ -1533,8 +1731,8 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, * TX descriptor initialization */ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc) + struct sk_buff *skb, + struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); __le32 *txd = skbdesc->desc; @@ -1548,7 +1746,7 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs); rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); @@ -1561,6 +1759,11 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->length_high); rt2x00_desc_write(txd, 2, word); + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv); + _rt2x00_desc_write(txd, 4, skbdesc->eiv); + } + rt2x00_desc_read(txd, 5, &word); rt2x00_set_field32(&word, TXD_W5_PID_TYPE, skbdesc->entry->queue->qid); rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, @@ -1595,11 +1798,15 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len); rt2x00_set_field32(&word, TXD_W0_BURST, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); } @@ -1718,6 +1925,7 @@ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) static void rt61pci_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct queue_entry_priv_pci *entry_priv = entry->priv_data; u32 word0; u32 word1; @@ -1728,6 +1936,38 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry, if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { + rxdesc->cipher = + rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = + rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + } + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv); + _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->eiv); + _rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv); + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data seperately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * FIXME: Legacy driver indicates that the frame does + * contain the Michael Mic. Unfortunately, in rt2x00 + * the MIC seems to be missing completely... + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, @@ -1735,7 +1975,7 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry, * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - rxdesc->rssi = rt61pci_agc_to_rssi(entry->queue->rt2x00dev, word1); + rxdesc->rssi = rt61pci_agc_to_rssi(rt2x00dev, word1); rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) @@ -2355,6 +2595,7 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); + __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); /* * Set the rssi offset. @@ -2404,6 +2645,7 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { .config = rt2x00mac_config, .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .set_retry_limit = rt61pci_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, @@ -2432,6 +2674,8 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .write_beacon = rt61pci_write_beacon, .kick_tx_queue = rt61pci_kick_tx_queue, .fill_rxdone = rt61pci_fill_rxdone, + .config_shared_key = rt61pci_config_shared_key, + .config_pairwise_key = rt61pci_config_pairwise_key, .config_filter = rt61pci_config_filter, .config_intf = rt61pci_config_intf, .config_erp = rt61pci_config_erp, diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 1004d5b899e..6d591cef3e5 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -134,6 +134,16 @@ #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; @@ -142,7 +152,8 @@ struct hw_key_entry { struct hw_pairwise_ta_entry { u8 address[6]; - u8 reserved[2]; + u8 cipher; + u8 reserved; } __attribute__ ((packed)); /* @@ -662,6 +673,10 @@ struct hw_pairwise_ta_entry { * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. @@ -1428,8 +1443,10 @@ struct hw_pairwise_ta_entry { /* * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" */ -#define RXD_W4_RESERVED FIELD32(0xffffffff) +#define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block -- cgit v1.2.3 From 906c110fcc24bdd5bf0fa22d89ac75d99c747e53 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:38:24 +0200 Subject: rt2x00: Implement HW encryption (rt73usb) rt73usb supports hardware encryption. rt73usb supports up to 4 shared keys and up to 64 pairwise keys. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/Kconfig | 1 + drivers/net/wireless/rt2x00/rt73usb.c | 271 +++++++++++++++++++++++++++++++++- drivers/net/wireless/rt2x00/rt73usb.h | 21 ++- 3 files changed, 285 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index f333f6100d0..11f590d63af 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -156,6 +156,7 @@ config RT73USB depends on USB select RT2X00_LIB_USB select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO select CRC_ITU_T ---help--- This adds support for rt2501 wireless chipset family. diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 9761eaaa08b..ddba747fed9 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -357,6 +357,219 @@ static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev, /* * Configuration handlers. */ +static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + int timeout; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt73usb_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? (ffz(reg) - 1) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + timeout = REGISTER_TIMEOUT32(sizeof(key_entry)); + rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, reg, + &key_entry, + sizeof(key_entry), + timeout); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt73usb_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt73usb_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt73usb_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt73usb_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided seperately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + mask = 1 << key->hw_key_idx; + + rt73usb_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt73usb_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; +} + +static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + int timeout; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple, read + * the first register, if that is full move to + * the next register. + * When both registers are full, we drop the key, + * otherwise we use the first invalid entry. + */ + rt73usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt73usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } + + key->hw_key_idx += reg ? (ffz(reg) - 1) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + timeout = REGISTER_TIMEOUT32(sizeof(key_entry)); + rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, reg, + &key_entry, + sizeof(key_entry), + timeout); + + /* + * Send the address and cipher type to the hardware register. + * This data fits within the CSR cache size, so we can use + * rt73usb_register_multiwrite() directly. + */ + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; + + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt73usb_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); + + /* + * Enable pairwise lookup table for given BSS idx, + * without this received frames will not be decrypted + * by the hardware. + */ + rt73usb_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt73usb_register_write(rt2x00dev, SEC_CSR4, reg); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided seperately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; + + rt73usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt73usb_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt73usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt73usb_register_write(rt2x00dev, SEC_CSR3, reg); + } + + return 0; +} + static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, const unsigned int filter_flags) { @@ -1265,8 +1478,8 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, * TX descriptor initialization */ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc) + struct sk_buff *skb, + struct txentry_desc *txdesc) { struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); __le32 *txd = skbdesc->desc; @@ -1280,7 +1493,7 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs); rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_desc_write(txd, 1, word); @@ -1292,6 +1505,11 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->length_high); rt2x00_desc_write(txd, 2, word); + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv); + _rt2x00_desc_write(txd, 4, skbdesc->eiv); + } + rt2x00_desc_read(txd, 5, &word); rt2x00_set_field32(&word, TXD_W5_TX_POWER, TXPOWER_TO_DEV(rt2x00dev->tx_power)); @@ -1313,12 +1531,16 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len - skbdesc->desc_len); rt2x00_set_field32(&word, TXD_W0_BURST2, test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); } @@ -1468,6 +1690,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) static void rt73usb_fill_rxdone(struct queue_entry *entry, struct rxdone_entry_desc *rxdesc) { + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *rxd = (__le32 *)entry->skb->data; u32 word0; @@ -1489,6 +1712,38 @@ static void rt73usb_fill_rxdone(struct queue_entry *entry, if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) { + rxdesc->cipher = + rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = + rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + } + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(rxd, 2, &rxdesc->iv); + _rt2x00_desc_read(rxd, 3, &rxdesc->eiv); + _rt2x00_desc_read(rxd, 4, &rxdesc->icv); + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data seperately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * FIXME: Legacy driver indicates that the frame does + * contain the Michael Mic. Unfortunately, in rt2x00 + * the MIC seems to be missing completely... + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + /* * Obtain the status about this packet. * When frame was received with an OFDM bitrate, @@ -1496,7 +1751,7 @@ static void rt73usb_fill_rxdone(struct queue_entry *entry, * a CCK bitrate the signal is the rate in 100kbit/s. */ rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - rxdesc->rssi = rt73usb_agc_to_rssi(entry->queue->rt2x00dev, word1); + rxdesc->rssi = rt73usb_agc_to_rssi(rt2x00dev, word1); rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); if (rt2x00_get_field32(word0, RXD_W0_OFDM)) @@ -1938,6 +2193,7 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags); + __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); /* * Set the rssi offset. @@ -1997,6 +2253,7 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { .config = rt2x00mac_config, .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .set_retry_limit = rt73usb_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, @@ -2024,6 +2281,8 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .get_tx_data_len = rt73usb_get_tx_data_len, .kick_tx_queue = rt73usb_kick_tx_queue, .fill_rxdone = rt73usb_fill_rxdone, + .config_shared_key = rt73usb_config_shared_key, + .config_pairwise_key = rt73usb_config_pairwise_key, .config_filter = rt73usb_config_filter, .config_intf = rt73usb_config_intf, .config_erp = rt73usb_config_erp, diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index 14849350101..91e04d319d7 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -92,6 +92,16 @@ #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; @@ -100,7 +110,8 @@ struct hw_key_entry { struct hw_pairwise_ta_entry { u8 address[6]; - u8 reserved[2]; + u8 cipher; + u8 reserved; } __attribute__ ((packed)); /* @@ -563,6 +574,10 @@ struct hw_pairwise_ta_entry { * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. @@ -1010,8 +1025,10 @@ struct hw_pairwise_ta_entry { /* * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" */ -#define RXD_W4_RESERVED FIELD32(0xffffffff) +#define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block -- cgit v1.2.3 From 8c5e7a5f59f9d11597bd47de28334da318ea0e80 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:38:47 +0200 Subject: rt2x00: Gather channel information in structure Channel information which is read from EEPROM should be read into an array containing per-channel information. This removes the requirement of multiple arrays and makes the channel handling a bit cleaner and easier to expand. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2400pci.c | 40 ++++++++++++++++----------- drivers/net/wireless/rt2x00/rt2400pci.h | 22 ++++++--------- drivers/net/wireless/rt2x00/rt2500pci.c | 39 ++++++++++++++++++--------- drivers/net/wireless/rt2x00/rt2500pci.h | 17 ++++-------- drivers/net/wireless/rt2x00/rt2500usb.c | 39 ++++++++++++++++++--------- drivers/net/wireless/rt2x00/rt2500usb.h | 17 ++++-------- drivers/net/wireless/rt2x00/rt2x00.h | 23 ++++++++++------ drivers/net/wireless/rt2x00/rt2x00config.c | 4 +++ drivers/net/wireless/rt2x00/rt2x00dev.c | 18 +++---------- drivers/net/wireless/rt2x00/rt61pci.c | 42 +++++++++++++++++------------ drivers/net/wireless/rt2x00/rt61pci.h | 17 ++++-------- drivers/net/wireless/rt2x00/rt73usb.c | 43 +++++++++++++++++------------- drivers/net/wireless/rt2x00/rt73usb.h | 17 ++++-------- 13 files changed, 176 insertions(+), 162 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 4c0538d6099..0107cec18b2 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1404,7 +1404,7 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * RF value list for RF2420 & RF2421 * Supports: 2.4 GHz */ -static const struct rf_channel rf_vals_bg[] = { +static const struct rf_channel rf_vals_b[] = { { 1, 0x00022058, 0x000c1fda, 0x00000101, 0 }, { 2, 0x00022058, 0x000c1fee, 0x00000101, 0 }, { 3, 0x00022058, 0x000c2002, 0x00000101, 0 }, @@ -1421,10 +1421,11 @@ static const struct rf_channel rf_vals_bg[] = { { 14, 0x00022058, 0x000c20fa, 0x00000101, 0 }, }; -static void rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* @@ -1439,24 +1440,29 @@ static void rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); - /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; - spec->num_channels = ARRAY_SIZE(rf_vals_bg); - spec->channels = rf_vals_bg; + spec->num_channels = ARRAY_SIZE(rf_vals_b); + spec->channels = rf_vals_b; + + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); + + return 0; } static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -1477,7 +1483,9 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize hw specifications. */ - rt2400pci_probe_hw_mode(rt2x00dev); + retval = rt2400pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* * This device requires the atim queue and DMA-mapped skbs. diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index bc556425822..bbff381ce39 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -938,19 +938,13 @@ #define MAX_TXPOWER 62 #define DEFAULT_TXPOWER 39 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ - ((__txpower) < MIN_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ - (((__txpower) - MAX_TXPOWER) + MIN_TXPOWER); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - (__txpower) += MIN_TXPOWER; \ - ((__txpower) <= MIN_TXPOWER) ? MAX_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MIN_TXPOWER : \ - (MAX_TXPOWER - ((__txpower) - MIN_TXPOWER))); \ -}) +#define __CLAMP_TX(__txpower) \ + clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) + +#define TXPOWER_FROM_DEV(__txpower) \ + ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) + +#define TXPOWER_TO_DEV(__txpower) \ + MAX_TXPOWER - (__CLAMP_TX(__txpower) - MIN_TXPOWER) #endif /* RT2400PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 181a146b476..e0ff76ff490 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1721,10 +1721,11 @@ static const struct rf_channel rf_vals_5222[] = { { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; -static void rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* @@ -1740,21 +1741,11 @@ static void rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); - /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); @@ -1776,6 +1767,26 @@ static void rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; } + + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) + info[i].tx_power1 = DEFAULT_TXPOWER; + } + + return 0; } static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -1796,7 +1807,9 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize hw specifications. */ - rt2500pci_probe_hw_mode(rt2x00dev); + retval = rt2500pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* * This device requires the atim queue and DMA-mapped skbs. diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index 42f376929ea..8c26bef6cf4 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -1223,17 +1223,10 @@ #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index cd5af656932..3b90ed62214 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1665,10 +1665,11 @@ static const struct rf_channel rf_vals_5222[] = { { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; -static void rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* @@ -1686,21 +1687,11 @@ static void rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); - /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); @@ -1722,6 +1713,26 @@ static void rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; } + + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) + info[i].tx_power1 = DEFAULT_TXPOWER; + } + + return 0; } static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -1742,7 +1753,9 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize hw specifications. */ - rt2500usb_probe_hw_mode(rt2x00dev); + retval = rt2500usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* * This device requires the atim queue diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h index 4769ffeb4cc..89e5ed24e4f 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/rt2x00/rt2500usb.h @@ -825,17 +825,10 @@ #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 0ffd972bb85..323bd54eb80 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -143,6 +143,17 @@ struct rf_channel { u32 rf4; }; +/* + * Channel information structure + */ +struct channel_info { + unsigned int flags; +#define GEOGRAPHY_ALLOWED 0x00000001 + + short tx_power1; + short tx_power2; +}; + /* * Antenna setup values. */ @@ -394,10 +405,7 @@ static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) * @num_channels: Number of supported channels. This is used as array size * for @tx_power_a, @tx_power_bg and @channels. * @channels: Device/chipset specific channel values (See &struct rf_channel). - * @tx_power_a: TX power values for all 5.2GHz channels (may be NULL). - * @tx_power_bg: TX power values for all 2.4GHz channels (may be NULL). - * @tx_power_default: Default TX power value to use when either - * @tx_power_a or @tx_power_bg is missing. + * @channels_info: Additional information for channels (See &struct channel_info). */ struct hw_mode_spec { unsigned int supported_bands; @@ -410,10 +418,7 @@ struct hw_mode_spec { unsigned int num_channels; const struct rf_channel *channels; - - const u8 *tx_power_a; - const u8 *tx_power_bg; - u8 tx_power_default; + const struct channel_info *channels_info; }; /* @@ -425,7 +430,9 @@ struct hw_mode_spec { */ struct rt2x00lib_conf { struct ieee80211_conf *conf; + struct rf_channel rf; + struct channel_info channel; struct antenna_setup ant; diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index d134c3be539..ea37c796204 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -245,6 +245,10 @@ config: memcpy(&libconf.rf, &rt2x00dev->spec.channels[conf->channel->hw_value], sizeof(libconf.rf)); + + memcpy(&libconf.channel, + &rt2x00dev->spec.channels_info[conf->channel->hw_value], + sizeof(libconf.channel)); } if (flags & CONFIG_UPDATE_ANTENNA) { diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 182952249a1..328ff8bc4c1 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -818,7 +818,6 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, struct ieee80211_rate *rates; unsigned int num_rates; unsigned int i; - unsigned char tx_power; num_rates = 0; if (spec->supported_rates & SUPPORT_RATE_CCK) @@ -844,20 +843,9 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, * Initialize Channel list. */ for (i = 0; i < spec->num_channels; i++) { - if (spec->channels[i].channel <= 14) { - if (spec->tx_power_bg) - tx_power = spec->tx_power_bg[i]; - else - tx_power = spec->tx_power_default; - } else { - if (spec->tx_power_a) - tx_power = spec->tx_power_a[i]; - else - tx_power = spec->tx_power_default; - } - rt2x00lib_channel(&channels[i], - spec->channels[i].channel, tx_power, i); + spec->channels[i].channel, + spec->channels_info[i].tx_power1, i); } /* @@ -909,6 +897,8 @@ static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; } + + kfree(rt2x00dev->spec.channels_info); } static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 86e7a50374b..87012f88461 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2514,10 +2514,11 @@ static const struct rf_channel rf_vals_seq[] = { { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000c0a23 }, }; -static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* @@ -2533,21 +2534,11 @@ static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); - /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; if (!test_bit(CONFIG_RF_SEQUENCE, &rt2x00dev->flags)) { spec->num_channels = 14; @@ -2561,13 +2552,28 @@ static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_rf(&rt2x00dev->chip, RF5325)) { spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_seq); + } + + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); - spec->tx_power_a = txpower; + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); } + + return 0; } static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -2588,7 +2594,9 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize hw specifications. */ - rt61pci_probe_hw_mode(rt2x00dev); + retval = rt61pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* * This device requires firmware and DMA mapped skbs. diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 6d591cef3e5..8ec1451308c 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -1482,17 +1482,10 @@ struct hw_pairwise_ta_entry { #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index ddba747fed9..1bd6b3ffca0 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2107,10 +2107,11 @@ static const struct rf_channel rf_vals_5225_2527[] = { }; -static void rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* @@ -2126,21 +2127,11 @@ static void rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); - /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; if (rt2x00_rf(&rt2x00dev->chip, RF2528)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); @@ -2158,14 +2149,26 @@ static void rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->channels = rf_vals_5225_2527; } - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5226)) { - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + /* + * Create channel information array + */ + info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - spec->tx_power_a = txpower; + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); + + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) + info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]); } + + return 0; } static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -2186,7 +2189,9 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize hw specifications. */ - rt73usb_probe_hw_mode(rt2x00dev); + retval = rt73usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* * This device requires firmware. diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index 91e04d319d7..868386c457f 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -1050,17 +1050,10 @@ struct hw_pairwise_ta_entry { #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT73USB_H */ -- cgit v1.2.3 From edcc25b22f7c328f56e8c129afa1e10c8bacb2ef Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 4 Aug 2008 16:39:05 +0200 Subject: rt2x00: Release rt2x00 2.2.0 Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 323bd54eb80..ced0a525176 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -44,7 +44,7 @@ /* * Module information. */ -#define DRV_VERSION "2.1.8" +#define DRV_VERSION "2.2.0" #define DRV_PROJECT "http://rt2x00.serialmonkey.com" /* -- cgit v1.2.3 From 06303352d3de02a371ccaec1366a92db5e1d7c30 Mon Sep 17 00:00:00 2001 From: Bruno Randolf Date: Tue, 5 Aug 2008 19:32:23 +0200 Subject: ath5k: set short preamble flag for rx set the short preamble flag in ieee80211_rx_status for frames received with a short preamble. drivers/net/wireless/ath5k/base.c: Changes-licensed-under: 3-Clause-BSD Signed-off-by: Bruno Randolf Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index c1de1ef09e5..cd598c0d8d6 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1763,12 +1763,10 @@ accept: rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); -#if 0 /* add rxs.flag SHORTPRE once it is in mac80211 */ - if (rs.rs_rate >= ATH5K_RATE_CODE_2M && - rs.rs_rate <= ATH5K_RATE_CODE_11M && - rs.rs_rate & AR5K_SET_SHORT_PREAMBLE) + if (rxs.rate_idx >= 0 && rs.rs_rate == + sc->curband->bitrates[rxs.rate_idx].hw_value_short) rxs.flag |= RX_FLAG_SHORTPRE; -#endif + ath5k_debug_dump_skb(sc, skb, "RX ", 0); /* check beacons in IBSS mode */ -- cgit v1.2.3 From ba2ab47129eee76f6f0ef52b4beae30a12cee7f6 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Wed, 6 Aug 2008 16:22:17 +0200 Subject: rt2x00: Move lna_gain calculation to config() callback We can optimize lna calculation in IRQ context by calculating most of the value during the config() callback when most of the value is actually influenced. This will be required later by rt2800pci and rt2800usb as well, since they need the lna_gain value during config(). Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 5 ++++ drivers/net/wireless/rt2x00/rt61pci.c | 48 ++++++++++++++++++++++------------- drivers/net/wireless/rt2x00/rt73usb.c | 41 ++++++++++++++++++++---------- 3 files changed, 63 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index ced0a525176..3fa3d5b006a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -803,6 +803,11 @@ struct rt2x00_dev { */ u32 *rf; + /* + * LNA gain + */ + short lna_gain; + /* * USB Max frame size (for rt2500usb & rt73usb). */ diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 87012f88461..0fcc4563683 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -638,6 +638,30 @@ static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev, rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); } + +static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain = 0; + + if (libconf->band == IEEE80211_BAND_2GHZ) { + if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } + + rt2x00dev->lna_gain = lna_gain; +} + static void rt61pci_config_phymode(struct rt2x00_dev *rt2x00dev, const int basic_rate_mask) { @@ -956,6 +980,9 @@ static void rt61pci_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { + /* Always recalculate LNA gain before changing configuration */ + rt61pci_config_lna_gain(rt2x00dev, libconf); + if (flags & CONFIG_UPDATE_PHYMODE) rt61pci_config_phymode(rt2x00dev, libconf->basic_rates); if (flags & CONFIG_UPDATE_CHANNEL) @@ -1883,40 +1910,27 @@ static void rt61pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, */ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { - u16 eeprom; - u8 offset; + u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: - offset = 90; + offset += 90; break; case 2: - offset = 74; + offset += 74; break; case 1: - offset = 64; + offset += 64; break; default: return 0; } if (rt2x00dev->rx_status.band == IEEE80211_BAND_5GHZ) { - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) - offset += 14; - if (lna == 3 || lna == 2) offset += 10; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } else { - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) - offset += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 1bd6b3ffca0..4f2eb90aaf0 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -664,6 +664,26 @@ static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev, rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg); } +static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain = 0; + + if (libconf->band == IEEE80211_BAND_2GHZ) { + if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } + + rt2x00dev->lna_gain = lna_gain; +} + static void rt73usb_config_phymode(struct rt2x00_dev *rt2x00dev, const int basic_rate_mask) { @@ -918,6 +938,9 @@ static void rt73usb_config(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf, const unsigned int flags) { + /* Always recalculate LNA gain before changing configuration */ + rt73usb_config_lna_gain(rt2x00dev, libconf); + if (flags & CONFIG_UPDATE_PHYMODE) rt73usb_config_phymode(rt2x00dev, libconf->basic_rates); if (flags & CONFIG_UPDATE_CHANNEL) @@ -1644,20 +1667,19 @@ static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, */ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { - u16 eeprom; - u8 offset; + u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: - offset = 90; + offset += 90; break; case 2: - offset = 74; + offset += 74; break; case 1: - offset = 64; + offset += 64; break; default: return 0; @@ -1673,15 +1695,6 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) else if (lna == 2) offset += 8; } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } else { - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) - offset += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; -- cgit v1.2.3 From 008c44825db74c6cdcea13dfb1598896e74fe908 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Wed, 6 Aug 2008 17:27:31 +0200 Subject: rt2x00: Add module parameter to disable HW crypto Add a module parameter to rt61 and rt73 to disable HW crypto. The option should only be checked when determining if the SUPPORT_HW_CRYPTO flag should be set or not. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt61pci.c | 10 +++++++++- drivers/net/wireless/rt2x00/rt73usb.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 0fcc4563683..23cf93dfda0 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -37,6 +37,13 @@ #include "rt2x00pci.h" #include "rt61pci.h" +/* + * Allow hardware encryption to be disabled. + */ +static int modparam_nohwcrypt = 0; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + /* * Register access. * BBP and RF register require indirect register access, @@ -2617,7 +2624,8 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); - __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); + if (!modparam_nohwcrypt) + __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); /* * Set the rssi offset. diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 4f2eb90aaf0..f58fd059c9a 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -36,6 +36,13 @@ #include "rt2x00usb.h" #include "rt73usb.h" +/* + * Allow hardware encryption to be disabled. + */ +static int modparam_nohwcrypt = 0; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + /* * Register access. * All access to the CSR registers will go through the methods @@ -2211,7 +2218,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags); - __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); + if (!modparam_nohwcrypt) + __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); /* * Set the rssi offset. -- cgit v1.2.3 From 32ddf0718590f410d5f18cb4fcda419f4aeefc57 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 8 Aug 2008 21:17:37 +0200 Subject: p54: rename prism54xyz -> p54xyz It's been a long time, but fullmac prism54 driver is still around... I think we should rename every prism54* in order to avoid some confusion about "what is actually what" in the future ;-). Thanks-to: Maxi Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54.h | 6 +++--- drivers/net/wireless/p54/p54common.h | 6 +++--- drivers/net/wireless/p54/p54pci.c | 34 +++++++++++++++++----------------- drivers/net/wireless/p54/p54pci.h | 10 +++++----- drivers/net/wireless/p54/p54usb.c | 32 ++++++++++++++++---------------- drivers/net/wireless/p54/p54usb.h | 6 +++--- 6 files changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 4801a363507..10df07de9e5 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -1,5 +1,5 @@ -#ifndef PRISM54_H -#define PRISM54_H +#ifndef P54_H +#define P54_H /* * Shared defines for all mac80211 Prism54 code @@ -76,4 +76,4 @@ void p54_fill_eeprom_readback(struct p54_control_hdr *hdr); struct ieee80211_hw *p54_init_common(size_t priv_data_len); void p54_free_common(struct ieee80211_hw *dev); -#endif /* PRISM54_H */ +#endif /* P54_H */ diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h index 8db6c0e8e54..9bc2a1cf4b5 100644 --- a/drivers/net/wireless/p54/p54common.h +++ b/drivers/net/wireless/p54/p54common.h @@ -1,5 +1,5 @@ -#ifndef PRISM54COMMON_H -#define PRISM54COMMON_H +#ifndef P54COMMON_H +#define P54COMMON_H /* * Common code specific definitions for mac80211 Prism54 drivers @@ -250,4 +250,4 @@ struct p54_tx_control_vdcf { __le16 frameburst; } __attribute__ ((packed)); -#endif /* PRISM54COMMON_H */ +#endif /* P54COMMON_H */ diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 7dd4add4bf4..ea2dc3d93c4 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -75,7 +75,7 @@ static int p54p_upload_firmware(struct ieee80211_hw *dev) err = request_firmware(&fw_entry, "isl3886", &priv->pdev->dev); if (err) { - printk(KERN_ERR "%s (prism54pci): cannot find firmware " + printk(KERN_ERR "%s (p54pci): cannot find firmware " "(isl3886)\n", pci_name(priv->pdev)); return err; } @@ -150,16 +150,16 @@ static int p54p_read_eeprom(struct ieee80211_hw *dev) init_completion(&priv->boot_comp); err = request_irq(priv->pdev->irq, &p54p_simple_interrupt, - IRQF_SHARED, "prism54pci", priv); + IRQF_SHARED, "p54pci", priv); if (err) { - printk(KERN_ERR "%s (prism54pci): failed to register IRQ handler\n", + printk(KERN_ERR "%s (p54pci): failed to register IRQ handler\n", pci_name(priv->pdev)); return err; } eeprom = kmalloc(0x2010 + EEPROM_READBACK_LEN, GFP_KERNEL); if (!eeprom) { - printk(KERN_ERR "%s (prism54pci): no memory for eeprom!\n", + printk(KERN_ERR "%s (p54pci): no memory for eeprom!\n", pci_name(priv->pdev)); err = -ENOMEM; goto out; @@ -177,7 +177,7 @@ static int p54p_read_eeprom(struct ieee80211_hw *dev) P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) { - printk(KERN_ERR "%s (prism54pci): Cannot boot firmware!\n", + printk(KERN_ERR "%s (p54pci): Cannot boot firmware!\n", pci_name(priv->pdev)); err = -EINVAL; goto out; @@ -219,7 +219,7 @@ static int p54p_read_eeprom(struct ieee80211_hw *dev) alen = le16_to_cpu(ring_control->rx_mgmt[0].len); if (le32_to_cpu(ring_control->device_idx[2]) != 1 || alen < 0x10) { - printk(KERN_ERR "%s (prism54pci): Cannot read eeprom!\n", + printk(KERN_ERR "%s (p54pci): Cannot read eeprom!\n", pci_name(priv->pdev)); err = -EINVAL; goto out; @@ -412,7 +412,7 @@ static int p54p_open(struct ieee80211_hw *dev) init_completion(&priv->boot_comp); err = request_irq(priv->pdev->irq, &p54p_interrupt, - IRQF_SHARED, "prism54pci", dev); + IRQF_SHARED, "p54pci", dev); if (err) { printk(KERN_ERR "%s: failed to register IRQ handler\n", wiphy_name(dev->wiphy)); @@ -506,7 +506,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) { - printk(KERN_ERR "%s (prism54pci): Cannot enable new PCI device\n", + printk(KERN_ERR "%s (p54pci): Cannot enable new PCI device\n", pci_name(pdev)); return err; } @@ -514,22 +514,22 @@ static int __devinit p54p_probe(struct pci_dev *pdev, mem_addr = pci_resource_start(pdev, 0); mem_len = pci_resource_len(pdev, 0); if (mem_len < sizeof(struct p54p_csr)) { - printk(KERN_ERR "%s (prism54pci): Too short PCI resources\n", + printk(KERN_ERR "%s (p54pci): Too short PCI resources\n", pci_name(pdev)); pci_disable_device(pdev); return err; } - err = pci_request_regions(pdev, "prism54pci"); + err = pci_request_regions(pdev, "p54pci"); if (err) { - printk(KERN_ERR "%s (prism54pci): Cannot obtain PCI resources\n", + printk(KERN_ERR "%s (p54pci): Cannot obtain PCI resources\n", pci_name(pdev)); return err; } if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) || pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { - printk(KERN_ERR "%s (prism54pci): No suitable DMA available\n", + printk(KERN_ERR "%s (p54pci): No suitable DMA available\n", pci_name(pdev)); goto err_free_reg; } @@ -542,7 +542,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, dev = p54_init_common(sizeof(*priv)); if (!dev) { - printk(KERN_ERR "%s (prism54pci): ieee80211 alloc failed\n", + printk(KERN_ERR "%s (p54pci): ieee80211 alloc failed\n", pci_name(pdev)); err = -ENOMEM; goto err_free_reg; @@ -556,7 +556,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, priv->map = ioremap(mem_addr, mem_len); if (!priv->map) { - printk(KERN_ERR "%s (prism54pci): Cannot map device memory\n", + printk(KERN_ERR "%s (p54pci): Cannot map device memory\n", pci_name(pdev)); err = -EINVAL; // TODO: use a better error code? goto err_free_dev; @@ -565,7 +565,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control), &priv->ring_control_dma); if (!priv->ring_control) { - printk(KERN_ERR "%s (prism54pci): Cannot allocate rings\n", + printk(KERN_ERR "%s (p54pci): Cannot allocate rings\n", pci_name(pdev)); err = -ENOMEM; goto err_iounmap; @@ -588,7 +588,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, err = ieee80211_register_hw(dev); if (err) { - printk(KERN_ERR "%s (prism54pci): Cannot register netdevice\n", + printk(KERN_ERR "%s (p54pci): Cannot register netdevice\n", pci_name(pdev)); goto err_free_common; } @@ -673,7 +673,7 @@ static int p54p_resume(struct pci_dev *pdev) #endif /* CONFIG_PM */ static struct pci_driver p54p_driver = { - .name = "prism54pci", + .name = "p54pci", .id_table = p54p_table, .probe = p54p_probe, .remove = __devexit_p(p54p_remove), diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h index 5bedd7af385..07678ef5ddc 100644 --- a/drivers/net/wireless/p54/p54pci.h +++ b/drivers/net/wireless/p54/p54pci.h @@ -1,5 +1,5 @@ -#ifndef PRISM54PCI_H -#define PRISM54PCI_H +#ifndef P54PCI_H +#define P54PCI_H /* * Defines for PCI based mac80211 Prism54 driver @@ -68,7 +68,7 @@ struct p54p_csr { } __attribute__ ((packed)); /* usb backend only needs the register defines above */ -#ifndef PRISM54USB_H +#ifndef P54USB_H struct p54p_desc { __le32 host_addr; __le32 device_addr; @@ -102,5 +102,5 @@ struct p54p_priv { struct completion boot_comp; }; -#endif /* PRISM54USB_H */ -#endif /* PRISM54PCI_H */ +#endif /* P54USB_H */ +#endif /* P54PCI_H */ diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index cbaca23a945..8a420df605a 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -322,7 +322,7 @@ static int p54u_read_eeprom(struct ieee80211_hw *dev) buf = kmalloc(0x2020, GFP_KERNEL); if (!buf) { - printk(KERN_ERR "prism54usb: cannot allocate memory for " + printk(KERN_ERR "p54usb: cannot allocate memory for " "eeprom readback!\n"); return -ENOMEM; } @@ -331,7 +331,7 @@ static int p54u_read_eeprom(struct ieee80211_hw *dev) *((u32 *) buf) = priv->common.rx_start; err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); if (err) { - printk(KERN_ERR "prism54usb: addr send failed\n"); + printk(KERN_ERR "p54usb: addr send failed\n"); goto fail; } } else { @@ -341,7 +341,7 @@ static int p54u_read_eeprom(struct ieee80211_hw *dev) reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); err = p54u_bulk_msg(priv, P54U_PIPE_DEV, buf, sizeof(*reg)); if (err) { - printk(KERN_ERR "prism54usb: dev_int send failed\n"); + printk(KERN_ERR "p54usb: dev_int send failed\n"); goto fail; } } @@ -359,7 +359,7 @@ static int p54u_read_eeprom(struct ieee80211_hw *dev) err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, EEPROM_READBACK_LEN + priv->common.tx_hdr_len); if (err) { - printk(KERN_ERR "prism54usb: eeprom req send failed\n"); + printk(KERN_ERR "p54usb: eeprom req send failed\n"); goto fail; } @@ -369,7 +369,7 @@ static int p54u_read_eeprom(struct ieee80211_hw *dev) if (!err && alen > offset) { p54_parse_eeprom(dev, (u8 *)buf + offset, alen - offset); } else { - printk(KERN_ERR "prism54usb: eeprom read failed!\n"); + printk(KERN_ERR "p54usb: eeprom read failed!\n"); err = -EINVAL; goto fail; } @@ -458,7 +458,7 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); if (err) { - printk(KERN_ERR "prism54usb: firmware upload failed!\n"); + printk(KERN_ERR "p54usb: firmware upload failed!\n"); goto err_upload_failed; } @@ -469,7 +469,7 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size)); err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); if (err) { - printk(KERN_ERR "prism54usb: firmware upload failed!\n"); + printk(KERN_ERR "p54usb: firmware upload failed!\n"); goto err_upload_failed; } @@ -480,13 +480,13 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) break; if (alen > 5 && !memcmp(buf, "ERROR", 5)) { - printk(KERN_INFO "prism54usb: firmware upload failed!\n"); + printk(KERN_INFO "p54usb: firmware upload failed!\n"); err = -EINVAL; break; } if (time_after(jiffies, timeout)) { - printk(KERN_ERR "prism54usb: firmware boot timed out!\n"); + printk(KERN_ERR "p54usb: firmware boot timed out!\n"); err = -ETIMEDOUT; break; } @@ -498,7 +498,7 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) buf[1] = '\r'; err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); if (err) { - printk(KERN_ERR "prism54usb: firmware boot failed!\n"); + printk(KERN_ERR "p54usb: firmware boot failed!\n"); goto err_upload_failed; } @@ -660,7 +660,7 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); if (err) { - printk(KERN_ERR "prism54usb: firmware block upload " + printk(KERN_ERR "p54usb: firmware block upload " "failed\n"); goto fail; } @@ -694,7 +694,7 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) 0x002C | (unsigned long)&devreg->direct_mem_win); if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { - printk(KERN_ERR "prism54usb: firmware DMA transfer " + printk(KERN_ERR "p54usb: firmware DMA transfer " "failed\n"); goto fail; } @@ -802,7 +802,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, dev = p54_init_common(sizeof(*priv)); if (!dev) { - printk(KERN_ERR "prism54usb: ieee80211 alloc failed\n"); + printk(KERN_ERR "p54usb: ieee80211 alloc failed\n"); return -ENOMEM; } @@ -858,7 +858,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { u8 perm_addr[ETH_ALEN]; - printk(KERN_WARNING "prism54usb: Invalid hwaddr! Using randomly generated MAC addr\n"); + printk(KERN_WARNING "p54usb: Invalid hwaddr! Using randomly generated MAC addr\n"); random_ether_addr(perm_addr); SET_IEEE80211_PERM_ADDR(dev, perm_addr); } @@ -867,7 +867,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, err = ieee80211_register_hw(dev); if (err) { - printk(KERN_ERR "prism54usb: Cannot register netdevice\n"); + printk(KERN_ERR "p54usb: Cannot register netdevice\n"); goto err_free_dev; } @@ -902,7 +902,7 @@ static void __devexit p54u_disconnect(struct usb_interface *intf) } static struct usb_driver p54u_driver = { - .name = "prism54usb", + .name = "p54usb", .id_table = p54u_table, .probe = p54u_probe, .disconnect = p54u_disconnect, diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h index d1896b396c1..1baaff058c5 100644 --- a/drivers/net/wireless/p54/p54usb.h +++ b/drivers/net/wireless/p54/p54usb.h @@ -1,5 +1,5 @@ -#ifndef PRISM54USB_H -#define PRISM54USB_H +#ifndef P54USB_H +#define P54USB_H /* * Defines for USB based mac80211 Prism54 driver @@ -130,4 +130,4 @@ struct p54u_priv { struct sk_buff_head rx_queue; }; -#endif /* PRISM54USB_H */ +#endif /* P54USB_H */ -- cgit v1.2.3 From 0753bba2d45762f83b9f4db94d15a3047f592964 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:46 +0100 Subject: orinoco: Add ESSID specific scanning for Agere fw Pass the ESSID to the card. This allows 'iwlist eth1 scan essid ' to work, and will help with routers setup not to broadcast the ESSID. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 1ebcafe7ca5..a60b459a9a9 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -2982,6 +2982,11 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, 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) */ @@ -3951,6 +3956,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev, { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; + struct iw_scan_req *si = (struct iw_scan_req *) extra; int err = 0; unsigned long flags; @@ -4012,7 +4018,19 @@ static int orinoco_ioctl_setscan(struct net_device *dev, } break; case FIRMWARE_TYPE_AGERE: - err = hermes_write_wordrec(hw, USER_BAP, + if (priv->scan_mode & IW_SCAN_THIS_ESSID) { + struct hermes_idstring idbuf; + size_t len = min(sizeof(idbuf.val), + (size_t) si->essid_len); + idbuf.len = cpu_to_le16(len); + memcpy(idbuf.val, si->essid, len); + + err = hermes_write_ltv(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + HERMES_BYTES_TO_RECLEN(len + 2), + &idbuf); + } else + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSCANSSID_AGERE, 0); /* Any ESSID */ if (err) -- cgit v1.2.3 From 17a1a887041ea39a19c5d469f1e31df940b00622 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:47 +0100 Subject: orinoco: Update scan translation Report channel, beacon interval and capabilities. Use WEXT defines instead of magic numbers. State quality stats in dB. Also a few changes to keep line length less than 80. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 71 +++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index a60b459a9a9..00b1d595fa3 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -3948,7 +3948,7 @@ static int orinoco_ioctl_getrid(struct net_device *dev, return err; } -/* Trigger a scan (look for other cells in the vicinity */ +/* Trigger a scan (look for other cells in the vicinity) */ static int orinoco_ioctl_setscan(struct net_device *dev, struct iw_request_info *info, struct iw_param *srq, @@ -3988,7 +3988,6 @@ static int orinoco_ioctl_setscan(struct net_device *dev, * we access scan variables in priv is critical. * o scan_inprogress : not touched by irq handler * o scan_mode : not touched by irq handler - * o scan_len : synchronised with scan_result * Before modifying anything on those variables, please think hard ! * Jean II */ @@ -4054,8 +4053,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev, #define MAX_CUSTOM_LEN 64 /* Translate scan data returned from the card to a card independant - * format that the Wireless Tools will understand - Jean II - * Return message length or -errno for fatal errors */ + * format that the Wireless Tools will understand - Jean II */ static inline char *orinoco_translate_scan(struct net_device *dev, struct iw_request_info *info, char *current_ev, @@ -4067,9 +4065,10 @@ static inline char *orinoco_translate_scan(struct net_device *dev, u16 capabilities; u16 channel; struct iw_event iwe; /* Temporary buffer */ - char *p; char custom[MAX_CUSTOM_LEN]; + memset(&iwe, 0, sizeof(iwe)); + /* First entry *MUST* be the AP MAC address */ iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; @@ -4091,8 +4090,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, /* Add mode */ iwe.cmd = SIOCGIWMODE; capabilities = le16_to_cpu(bss->a.capabilities); - if (capabilities & 0x3) { - if (capabilities & 0x1) + if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + if (capabilities & WLAN_CAPABILITY_ESS) iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; @@ -4102,17 +4101,22 @@ static inline char *orinoco_translate_scan(struct net_device *dev, channel = bss->s.channel; if ((channel >= 1) && (channel <= NUM_CHANNELS)) { - /* Add frequency */ + /* Add channel and frequency */ iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = channel; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + iwe.u.freq.m = channel_frequency[channel-1] * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); } - /* Add quality statistics */ + /* Add quality statistics. level and noise in dB. No link quality */ iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = 0x10; /* no link quality */ + iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; /* Wireless tools prior to 27.pre22 will show link quality @@ -4126,25 +4130,13 @@ static inline char *orinoco_translate_scan(struct net_device *dev, /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; - if (capabilities & 0x10) + if (capabilities & WLAN_CAPABILITY_PRIVACY) iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->a.essid); - - /* Add EXTRA: Age to display seconds since last beacon/probe response - * for given network. */ - iwe.cmd = IWEVCUSTOM; - p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), - " Last beacon: %dms ago", - jiffies_to_msecs(jiffies - last_scanned)); - iwe.u.data.length = p - custom; - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); + &iwe, NULL); /* Bit rate is not available in Lucent/Agere firmwares */ if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { @@ -4166,7 +4158,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, if (bss->p.rates[i] == 0x0) break; /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = ((bss->p.rates[i] & 0x7f) * 500000); + iwe.u.bitrate.value = + ((bss->p.rates[i] & 0x7f) * 500000); current_val = iwe_stream_add_value(info, current_ev, current_val, end_buf, &iwe, @@ -4177,6 +4170,34 @@ static inline char *orinoco_translate_scan(struct net_device *dev, current_ev = current_val; } + /* Beacon interval */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + "bcn_int=%d", + le16_to_cpu(bss->a.beacon_interv)); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + /* Capabilites */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + "capab=0x%04x", + capabilities); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + " Last beacon: %dms ago", + jiffies_to_msecs(jiffies - last_scanned)); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + return current_ev; } -- cgit v1.2.3 From a1d81f1523de0c588f1d7798a49d66559f538e4f Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:48 +0100 Subject: orinoco: Specify all three parameters to every Hermes command. hermes_issue_cmd now takes two more parameters. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index 2cd86af1390..3a6ce5a9647 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -87,7 +87,8 @@ MODULE_LICENSE("Dual MPL/GPL"); Callable from any context. */ -static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) +static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0, + u16 param1, u16 param2) { int k = CMD_BUSY_TIMEOUT; u16 reg; @@ -103,8 +104,8 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) return -EBUSY; } - hermes_write_regn(hw, PARAM2, 0); - hermes_write_regn(hw, PARAM1, 0); + hermes_write_regn(hw, PARAM2, param2); + hermes_write_regn(hw, PARAM1, param1); hermes_write_regn(hw, PARAM0, param0); hermes_write_regn(hw, CMD, cmd); @@ -162,7 +163,7 @@ int hermes_init(hermes_t *hw) /* We don't use hermes_docmd_wait here, because the reset wipes the magic constant in SWSUPPORT0 away, and it gets confused */ - err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0); + err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0, 0, 0); if (err) return err; @@ -216,7 +217,7 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 reg; u16 status; - err = hermes_issue_cmd(hw, cmd, parm0); + err = hermes_issue_cmd(hw, cmd, parm0, 0, 0); if (err) { if (! hermes_present(hw)) { if (net_ratelimit()) @@ -497,7 +498,7 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, hermes_write_bytes(hw, dreg, value, count << 1); - err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, + err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, rid, NULL); return err; -- cgit v1.2.3 From 82a06ee518f570eb8fbb6588e75a4a5d838ac901 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:49 +0100 Subject: orinoco: Move EXPORT_SYMBOL declarations next to exported function Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index 3a6ce5a9647..240118f3358 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -122,6 +122,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) hw->reg_spacing = reg_spacing; hw->inten = 0x0; } +EXPORT_SYMBOL(hermes_struct_init); int hermes_init(hermes_t *hw) { @@ -202,6 +203,7 @@ int hermes_init(hermes_t *hw) out: return err; } +EXPORT_SYMBOL(hermes_init); /* Issue a command to the chip, and (busy!) wait for it to * complete. @@ -272,6 +274,7 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, out: return err; } +EXPORT_SYMBOL(hermes_docmd_wait); int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) { @@ -314,7 +317,7 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) return 0; } - +EXPORT_SYMBOL(hermes_allocate); /* Set up a BAP to read a particular chunk of data from card's internal buffer. * @@ -398,6 +401,7 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, out: return err; } +EXPORT_SYMBOL(hermes_bap_pread); /* Write a block of data to the chip's buffer, via the * BAP. Synchronization/serialization is the caller's problem. @@ -423,6 +427,7 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, out: return err; } +EXPORT_SYMBOL(hermes_bap_pwrite); /* Read a Length-Type-Value record from the card. * @@ -476,6 +481,7 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, return 0; } +EXPORT_SYMBOL(hermes_read_ltv); int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, u16 length, const void *value) @@ -503,15 +509,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, return err; } - -EXPORT_SYMBOL(hermes_struct_init); -EXPORT_SYMBOL(hermes_init); -EXPORT_SYMBOL(hermes_docmd_wait); -EXPORT_SYMBOL(hermes_allocate); - -EXPORT_SYMBOL(hermes_bap_pread); -EXPORT_SYMBOL(hermes_bap_pwrite); -EXPORT_SYMBOL(hermes_read_ltv); EXPORT_SYMBOL(hermes_write_ltv); static int __init init_hermes(void) -- cgit v1.2.3 From fc5a62d8b7052ea588e4f7750cd78b0c4c47015a Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:50 +0100 Subject: orinoco: Add function to execute Hermes initialisation commands synchronously The current synchronous execution function doesn't work for certain Hermes commands which clear the MAGIC number from SWSUPPORT0. These commands seem to be related to initialisation or programming, for example HERMES_CMD_INIT. Replicate hermes_docmd_wait for commands which clear the MAGIC number from SWSUPPORT0. This version accepts two extra arguments which are passed straight to the firmware. Functionality copied out of hermes_init. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes.c | 94 ++++++++++++++++++++++++++----------------- drivers/net/wireless/hermes.h | 3 ++ 2 files changed, 60 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index 240118f3358..bfa375369df 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -116,6 +116,61 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0, * Function definitions */ +/* For doing cmds that wipe the magic constant in SWSUPPORT0 */ +int hermes_doicmd_wait(hermes_t *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp) +{ + int err = 0; + int k; + u16 status, reg; + + err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2); + if (err) + return err; + + reg = hermes_read_regn(hw, EVSTAT); + k = CMD_INIT_TIMEOUT; + while ((!(reg & HERMES_EV_CMD)) && k) { + k--; + udelay(10); + reg = hermes_read_regn(hw, EVSTAT); + } + + hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); + + if (!hermes_present(hw)) { + DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", + hw->iobase); + err = -ENODEV; + goto out; + } + + if (!(reg & HERMES_EV_CMD)) { + printk(KERN_ERR "hermes @ %p: " + "Timeout waiting for card to reset (reg=0x%04x)!\n", + hw->iobase, reg); + err = -ETIMEDOUT; + goto out; + } + + status = hermes_read_regn(hw, STATUS); + if (resp) { + resp->status = status; + resp->resp0 = hermes_read_regn(hw, RESP0); + resp->resp1 = hermes_read_regn(hw, RESP1); + resp->resp2 = hermes_read_regn(hw, RESP2); + } + + hermes_write_regn(hw, EVACK, HERMES_EV_CMD); + + if (status & HERMES_STATUS_RESULT) + err = -EIO; +out: + return err; +} +EXPORT_SYMBOL(hermes_doicmd_wait); + void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) { hw->iobase = address; @@ -126,7 +181,7 @@ EXPORT_SYMBOL(hermes_struct_init); int hermes_init(hermes_t *hw) { - u16 status, reg; + u16 reg; int err = 0; int k; @@ -164,43 +219,8 @@ int hermes_init(hermes_t *hw) /* We don't use hermes_docmd_wait here, because the reset wipes the magic constant in SWSUPPORT0 away, and it gets confused */ - err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0, 0, 0); - if (err) - return err; - - reg = hermes_read_regn(hw, EVSTAT); - k = CMD_INIT_TIMEOUT; - while ( (! (reg & HERMES_EV_CMD)) && k) { - k--; - udelay(10); - reg = hermes_read_regn(hw, EVSTAT); - } - - hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); - - if (! hermes_present(hw)) { - DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", - hw->iobase); - err = -ENODEV; - goto out; - } - - if (! (reg & HERMES_EV_CMD)) { - printk(KERN_ERR "hermes @ %p: " - "Timeout waiting for card to reset (reg=0x%04x)!\n", - hw->iobase, reg); - err = -ETIMEDOUT; - goto out; - } - - status = hermes_read_regn(hw, STATUS); - - hermes_write_regn(hw, EVACK, HERMES_EV_CMD); + err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL); - if (status & HERMES_STATUS_RESULT) - err = -EIO; - - out: return err; } EXPORT_SYMBOL(hermes_init); diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h index 8e3f0e3edb5..287f5363fda 100644 --- a/drivers/net/wireless/hermes.h +++ b/drivers/net/wireless/hermes.h @@ -353,6 +353,9 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing); int hermes_init(hermes_t *hw); int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, struct hermes_response *resp); +int hermes_doicmd_wait(hermes_t *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp); int hermes_allocate(hermes_t *hw, u16 size, u16 *fid); int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, -- cgit v1.2.3 From f482eb797a391a098046a934f55af8bd785a4494 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:51 +0100 Subject: orinoco: Move firmware download functionality into new module Move the functionality from spectrum_cs to hermes_dld in preparation for making it more generic and usable by other orinoco drivers. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/hermes_dld.c | 338 +++++++++++++++++++++++++++++++++++++ drivers/net/wireless/hermes_dld.h | 45 +++++ drivers/net/wireless/spectrum_cs.c | 274 +----------------------------- 4 files changed, 385 insertions(+), 274 deletions(-) create mode 100644 drivers/net/wireless/hermes_dld.c create mode 100644 drivers/net/wireless/hermes_dld.h (limited to 'drivers') diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index c05fd7627f3..59d2d805f60 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_WAVELAN) += wavelan.o obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o -obj-$(CONFIG_HERMES) += orinoco.o hermes.o +obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o obj-$(CONFIG_APPLE_AIRPORT) += airport.o obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c new file mode 100644 index 00000000000..9a8ef3040d5 --- /dev/null +++ b/drivers/net/wireless/hermes_dld.c @@ -0,0 +1,338 @@ +/* + * Hermes download helper driver. + * + * This could be entirely merged into hermes.c. + * + * I'm keeping it separate to minimise the amount of merging between + * kernel upgrades. It also means the memory overhead for drivers that + * don't need firmware download low. + * + * This driver: + * - is capable of writing to the volatile area of the hermes device + * - is currently not capable of writing to non-volatile areas + * - provide helpers to identify and update plugin data + * - is not capable of interpreting a fw image directly. That is up to + * the main card driver. + * - deals with Hermes I devices. It can probably be modified to deal + * with Hermes II devices + * + * Copyright (C) 2007, David Kilroy + * + * Plug data code slightly modified from spectrum_cs driver + * Copyright (C) 2002-2005 Pavel Roskin + * Portions based on information in wl_lkm_718 Agere driver + * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#include +#include +#include "hermes.h" +#include "hermes_dld.h" + +MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset"); +MODULE_AUTHOR("David Kilroy "); +MODULE_LICENSE("Dual MPL/GPL"); + +#define PFX "hermes_dld: " + +/* + * AUX port access. To unlock the AUX port write the access keys to the + * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL + * register. Then read it and make sure it's HERMES_AUX_ENABLED. + */ +#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ +#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ +#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ + +#define HERMES_AUX_PW0 0xFE01 +#define HERMES_AUX_PW1 0xDC23 +#define HERMES_AUX_PW2 0xBA45 + +/* End markers */ +#define PDI_END 0x00000000 /* End of PDA */ +#define BLOCK_END 0xFFFFFFFF /* Last image block */ + +/* + * The following structures have little-endian fields denoted by + * the leading underscore. Don't access them directly - use inline + * functions defined below. + */ + +/* + * The binary image to be downloaded consists of series of data blocks. + * Each block has the following structure. + */ +struct dblock { + __le32 addr; /* adapter address where to write the block */ + __le16 len; /* length of the data only, in bytes */ + char data[0]; /* data to be written */ +} __attribute__ ((packed)); + +/* + * Plug Data References are located in in the image after the last data + * block. They refer to areas in the adapter memory where the plug data + * items with matching ID should be written. + */ +struct pdr { + __le32 id; /* record ID */ + __le32 addr; /* adapter address where to write the data */ + __le32 len; /* expected length of the data, in bytes */ + char next[0]; /* next PDR starts here */ +} __attribute__ ((packed)); + +/* + * Plug Data Items are located in the EEPROM read from the adapter by + * primary firmware. They refer to the device-specific data that should + * be plugged into the secondary firmware. + */ +struct pdi { + __le16 len; /* length of ID and data, in words */ + __le16 id; /* record ID */ + char data[0]; /* plug data */ +} __attribute__ ((packed)); + +/* Functions for access to little-endian data */ +static inline u32 +dblock_addr(const struct dblock *blk) +{ + return le32_to_cpu(blk->addr); +} + +static inline u32 +dblock_len(const struct dblock *blk) +{ + return le16_to_cpu(blk->len); +} + +static inline u32 +pdr_id(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->id); +} + +static inline u32 +pdr_addr(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->addr); +} + +static inline u32 +pdr_len(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->len); +} + +static inline u32 +pdi_id(const struct pdi *pdi) +{ + return le16_to_cpu(pdi->id); +} + +/* Return length of the data only, in bytes */ +static inline u32 +pdi_len(const struct pdi *pdi) +{ + return 2 * (le16_to_cpu(pdi->len) - 1); +} + +/* Set address of the auxiliary port */ +static inline void +spectrum_aux_setaddr(hermes_t *hw, u32 addr) +{ + hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); + hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); +} + +/* Open access to the auxiliary port */ +static int +spectrum_aux_open(hermes_t *hw) +{ + int i; + + /* Already open? */ + if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) + return 0; + + hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); + hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); + hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); + hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); + + for (i = 0; i < 20; i++) { + udelay(10); + if (hermes_read_reg(hw, HERMES_CONTROL) == + HERMES_AUX_ENABLED) + return 0; + } + + return -EBUSY; +} + +/* + * Scan PDR for the record with the specified RECORD_ID. + * If it's not found, return NULL. + */ +static struct pdr * +spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) +{ + struct pdr *pdr = first_pdr; + + while (pdr_id(pdr) != PDI_END) { + /* + * PDR area is currently not terminated by PDI_END. + * It's followed by CRC records, which have the type + * field where PDR has length. The type can be 0 or 1. + */ + if (pdr_len(pdr) < 2) + return NULL; + + /* If the record ID matches, we are done */ + if (pdr_id(pdr) == record_id) + return pdr; + + pdr = (struct pdr *) pdr->next; + } + return NULL; +} + +/* Process one Plug Data Item - find corresponding PDR and plug it */ +static int +spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) +{ + struct pdr *pdr; + + /* Find the PDI corresponding to this PDR */ + pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); + + /* No match is found, safe to ignore */ + if (!pdr) + return 0; + + /* Lengths of the data in PDI and PDR must match */ + if (pdi_len(pdi) != pdr_len(pdr)) + return -EINVAL; + + /* do the actual plugging */ + spectrum_aux_setaddr(hw, pdr_addr(pdr)); + hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); + + return 0; +} + +/* Read PDA from the adapter */ +int +spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) +{ + int ret; + int pda_size; + + /* Issue command to read EEPROM */ + ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); + if (ret) + return ret; + + /* Open auxiliary port */ + ret = spectrum_aux_open(hw); + if (ret) + return ret; + + /* read PDA from EEPROM */ + spectrum_aux_setaddr(hw, PDA_ADDR); + hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); + + /* Check PDA length */ + pda_size = le16_to_cpu(pda[0]); + if (pda_size > pda_len) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(spectrum_read_pda); + +/* Parse PDA and write the records into the adapter */ +int +spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, + __le16 *pda) +{ + int ret; + struct pdi *pdi; + struct pdr *first_pdr; + const struct dblock *blk = first_block; + + /* Skip all blocks to locate Plug Data References */ + while (dblock_addr(blk) != BLOCK_END) + blk = (struct dblock *) &blk->data[dblock_len(blk)]; + + first_pdr = (struct pdr *) blk; + + /* Go through every PDI and plug them into the adapter */ + pdi = (struct pdi *) (pda + 2); + while (pdi_id(pdi) != PDI_END) { + ret = spectrum_plug_pdi(hw, first_pdr, pdi); + if (ret) + return ret; + + /* Increment to the next PDI */ + pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; + } + return 0; +} +EXPORT_SYMBOL(spectrum_apply_pda); + +/* Load firmware blocks into the adapter */ +int +spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) +{ + const struct dblock *blk; + u32 blkaddr; + u32 blklen; + + blk = first_block; + blkaddr = dblock_addr(blk); + blklen = dblock_len(blk); + + while (dblock_addr(blk) != BLOCK_END) { + spectrum_aux_setaddr(hw, blkaddr); + hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, + blklen); + + blk = (struct dblock *) &blk->data[blklen]; + blkaddr = dblock_addr(blk); + blklen = dblock_len(blk); + } + return 0; +} +EXPORT_SYMBOL(spectrum_load_blocks); + +static int __init init_hermes_dld(void) +{ + return 0; +} + +static void __exit exit_hermes_dld(void) +{ +} + +module_init(init_hermes_dld); +module_exit(exit_hermes_dld); diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h new file mode 100644 index 00000000000..2c8892ac635 --- /dev/null +++ b/drivers/net/wireless/hermes_dld.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007, David Kilroy + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ +#ifndef _HERMES_DLD_H +#define _HERMES_DLD_H + +#include "hermes.h" + +/* Position of PDA in the adapter memory */ +#define EEPROM_ADDR 0x3000 +#define EEPROM_LEN 0x200 +#define PDA_OFFSET 0x100 + +#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) +#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) + +struct dblock; + +int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len); +int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, + __le16 *pda); +int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block); + +#endif /* _HERMES_DLD_H */ diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 98df9bc7836..579873d0e8c 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -33,6 +33,7 @@ #include #include "orinoco.h" +#include "hermes_dld.h" static const char primary_fw_name[] = "symbol_sp24t_prim_fw"; static const char secondary_fw_name[] = "symbol_sp24t_sec_fw"; @@ -88,144 +89,9 @@ static void spectrum_cs_release(struct pcmcia_device *link); #define HCR_IDLE 0x0E /* don't run firmware after reset */ #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ -/* - * AUX port access. To unlock the AUX port write the access keys to the - * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL - * register. Then read it and make sure it's HERMES_AUX_ENABLED. - */ -#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ -#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ -#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ - -#define HERMES_AUX_PW0 0xFE01 -#define HERMES_AUX_PW1 0xDC23 -#define HERMES_AUX_PW2 0xBA45 - /* End markers */ -#define PDI_END 0x00000000 /* End of PDA */ -#define BLOCK_END 0xFFFFFFFF /* Last image block */ #define TEXT_END 0x1A /* End of text header */ -/* - * The following structures have little-endian fields denoted by - * the leading underscore. Don't access them directly - use inline - * functions defined below. - */ - -/* - * The binary image to be downloaded consists of series of data blocks. - * Each block has the following structure. - */ -struct dblock { - __le32 addr; /* adapter address where to write the block */ - __le16 len; /* length of the data only, in bytes */ - char data[0]; /* data to be written */ -} __attribute__ ((packed)); - -/* - * Plug Data References are located in in the image after the last data - * block. They refer to areas in the adapter memory where the plug data - * items with matching ID should be written. - */ -struct pdr { - __le32 id; /* record ID */ - __le32 addr; /* adapter address where to write the data */ - __le32 len; /* expected length of the data, in bytes */ - char next[0]; /* next PDR starts here */ -} __attribute__ ((packed)); - - -/* - * Plug Data Items are located in the EEPROM read from the adapter by - * primary firmware. They refer to the device-specific data that should - * be plugged into the secondary firmware. - */ -struct pdi { - __le16 len; /* length of ID and data, in words */ - __le16 id; /* record ID */ - char data[0]; /* plug data */ -} __attribute__ ((packed)); - - -/* Functions for access to little-endian data */ -static inline u32 -dblock_addr(const struct dblock *blk) -{ - return le32_to_cpu(blk->addr); -} - -static inline u32 -dblock_len(const struct dblock *blk) -{ - return le16_to_cpu(blk->len); -} - -static inline u32 -pdr_id(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->id); -} - -static inline u32 -pdr_addr(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->addr); -} - -static inline u32 -pdr_len(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->len); -} - -static inline u32 -pdi_id(const struct pdi *pdi) -{ - return le16_to_cpu(pdi->id); -} - -/* Return length of the data only, in bytes */ -static inline u32 -pdi_len(const struct pdi *pdi) -{ - return 2 * (le16_to_cpu(pdi->len) - 1); -} - - -/* Set address of the auxiliary port */ -static inline void -spectrum_aux_setaddr(hermes_t *hw, u32 addr) -{ - hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); - hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); -} - - -/* Open access to the auxiliary port */ -static int -spectrum_aux_open(hermes_t *hw) -{ - int i; - - /* Already open? */ - if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) - return 0; - - hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); - hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); - hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); - hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); - - for (i = 0; i < 20; i++) { - udelay(10); - if (hermes_read_reg(hw, HERMES_CONTROL) == - HERMES_AUX_ENABLED) - return 0; - } - - return -EBUSY; -} - #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) @@ -293,144 +159,6 @@ spectrum_reset(struct pcmcia_device *link, int idle) } -/* - * Scan PDR for the record with the specified RECORD_ID. - * If it's not found, return NULL. - */ -static struct pdr * -spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) -{ - struct pdr *pdr = first_pdr; - - while (pdr_id(pdr) != PDI_END) { - /* - * PDR area is currently not terminated by PDI_END. - * It's followed by CRC records, which have the type - * field where PDR has length. The type can be 0 or 1. - */ - if (pdr_len(pdr) < 2) - return NULL; - - /* If the record ID matches, we are done */ - if (pdr_id(pdr) == record_id) - return pdr; - - pdr = (struct pdr *) pdr->next; - } - return NULL; -} - - -/* Process one Plug Data Item - find corresponding PDR and plug it */ -static int -spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) -{ - struct pdr *pdr; - - /* Find the PDI corresponding to this PDR */ - pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); - - /* No match is found, safe to ignore */ - if (!pdr) - return 0; - - /* Lengths of the data in PDI and PDR must match */ - if (pdi_len(pdi) != pdr_len(pdr)) - return -EINVAL; - - /* do the actual plugging */ - spectrum_aux_setaddr(hw, pdr_addr(pdr)); - hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); - - return 0; -} - - -/* Read PDA from the adapter */ -static int -spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) -{ - int ret; - int pda_size; - - /* Issue command to read EEPROM */ - ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); - if (ret) - return ret; - - /* Open auxiliary port */ - ret = spectrum_aux_open(hw); - if (ret) - return ret; - - /* read PDA from EEPROM */ - spectrum_aux_setaddr(hw, PDA_ADDR); - hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); - - /* Check PDA length */ - pda_size = le16_to_cpu(pda[0]); - if (pda_size > pda_len) - return -EINVAL; - - return 0; -} - - -/* Parse PDA and write the records into the adapter */ -static int -spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, - __le16 *pda) -{ - int ret; - struct pdi *pdi; - struct pdr *first_pdr; - const struct dblock *blk = first_block; - - /* Skip all blocks to locate Plug Data References */ - while (dblock_addr(blk) != BLOCK_END) - blk = (struct dblock *) &blk->data[dblock_len(blk)]; - - first_pdr = (struct pdr *) blk; - - /* Go through every PDI and plug them into the adapter */ - pdi = (struct pdi *) (pda + 2); - while (pdi_id(pdi) != PDI_END) { - ret = spectrum_plug_pdi(hw, first_pdr, pdi); - if (ret) - return ret; - - /* Increment to the next PDI */ - pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; - } - return 0; -} - - -/* Load firmware blocks into the adapter */ -static int -spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) -{ - const struct dblock *blk; - u32 blkaddr; - u32 blklen; - - blk = first_block; - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - - while (dblock_addr(blk) != BLOCK_END) { - spectrum_aux_setaddr(hw, blkaddr); - hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, - blklen); - - blk = (struct dblock *) &blk->data[blklen]; - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - } - return 0; -} - - /* * Process a firmware image - stop the card, load the firmware, reset * the card and make sure it responds. For the secondary firmware take -- cgit v1.2.3 From e23341809b7b60981d14a368155cd1f0724fb8d5 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:52 +0100 Subject: orinoco: Make firmware download logic more generic Ensure PDA read is terminated. Prevent invalid programming blocks from causing reads outside the firmware image Turn off aux stuff when finished. Option to program in limited block sizes (controlled by macro). Option to read PDA from EEPROM. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes_dld.c | 209 ++++++++++++++++++++++++++++--------- drivers/net/wireless/hermes_dld.h | 22 ++-- drivers/net/wireless/spectrum_cs.c | 23 ++-- 3 files changed, 182 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c index 9a8ef3040d5..22ae79dae41 100644 --- a/drivers/net/wireless/hermes_dld.c +++ b/drivers/net/wireless/hermes_dld.c @@ -64,14 +64,34 @@ MODULE_LICENSE("Dual MPL/GPL"); #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ +#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ #define HERMES_AUX_PW0 0xFE01 #define HERMES_AUX_PW1 0xDC23 #define HERMES_AUX_PW2 0xBA45 -/* End markers */ +/* End markers used in dblocks */ #define PDI_END 0x00000000 /* End of PDA */ #define BLOCK_END 0xFFFFFFFF /* Last image block */ +#define TEXT_END 0x1A /* End of text header */ + +/* + * PDA == Production Data Area + * + * In principle, the max. size of the PDA is is 4096 words. Currently, + * however, only about 500 bytes of this area are used. + * + * Some USB implementations can't handle sizes in excess of 1016. Note + * that PDA is not actually used in those USB environments, but may be + * retrieved by common code. + */ +#define MAX_PDA_SIZE 1000 + +/* Limit the amout we try to download in a single shot. + * Size is in bytes. + */ +#define MAX_DL_SIZE 1024 +#define LIMIT_PROGRAM_SIZE 0 /* * The following structures have little-endian fields denoted by @@ -112,7 +132,8 @@ struct pdi { char data[0]; /* plug data */ } __attribute__ ((packed)); -/* Functions for access to little-endian data */ +/*** FW data block access functions ***/ + static inline u32 dblock_addr(const struct dblock *blk) { @@ -125,6 +146,8 @@ dblock_len(const struct dblock *blk) return le16_to_cpu(blk->len); } +/*** PDR Access functions ***/ + static inline u32 pdr_id(const struct pdr *pdr) { @@ -143,6 +166,8 @@ pdr_len(const struct pdr *pdr) return le32_to_cpu(pdr->len); } +/*** PDI Access functions ***/ + static inline u32 pdi_id(const struct pdi *pdi) { @@ -156,49 +181,55 @@ pdi_len(const struct pdi *pdi) return 2 * (le16_to_cpu(pdi->len) - 1); } -/* Set address of the auxiliary port */ +/*** Hermes AUX control ***/ + static inline void -spectrum_aux_setaddr(hermes_t *hw, u32 addr) +hermes_aux_setaddr(hermes_t *hw, u32 addr) { hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); } -/* Open access to the auxiliary port */ -static int -spectrum_aux_open(hermes_t *hw) +static inline int +hermes_aux_control(hermes_t *hw, int enabled) { + int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; + int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; int i; /* Already open? */ - if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED) + if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) return 0; hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); - hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE); + hermes_write_reg(hw, HERMES_CONTROL, action); for (i = 0; i < 20; i++) { udelay(10); if (hermes_read_reg(hw, HERMES_CONTROL) == - HERMES_AUX_ENABLED) + desired_state) return 0; } return -EBUSY; } +/*** Plug Data Functions ***/ + /* * Scan PDR for the record with the specified RECORD_ID. * If it's not found, return NULL. */ static struct pdr * -spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) +hermes_find_pdr(struct pdr *first_pdr, u32 record_id) { struct pdr *pdr = first_pdr; + void *end = (void *)first_pdr + MAX_PDA_SIZE; - while (pdr_id(pdr) != PDI_END) { + while (((void *)pdr < end) && + (pdr_id(pdr) != PDI_END)) { /* * PDR area is currently not terminated by PDI_END. * It's followed by CRC records, which have the type @@ -218,12 +249,12 @@ spectrum_find_pdr(struct pdr *first_pdr, u32 record_id) /* Process one Plug Data Item - find corresponding PDR and plug it */ static int -spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) +hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) { struct pdr *pdr; - /* Find the PDI corresponding to this PDR */ - pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi)); + /* Find the PDR corresponding to this PDI */ + pdr = hermes_find_pdr(first_pdr, pdi_id(pdi)); /* No match is found, safe to ignore */ if (!pdr) @@ -234,96 +265,172 @@ spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi) return -EINVAL; /* do the actual plugging */ - spectrum_aux_setaddr(hw, pdr_addr(pdr)); + hermes_aux_setaddr(hw, pdr_addr(pdr)); hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); return 0; } /* Read PDA from the adapter */ -int -spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len) +int hermes_read_pda(hermes_t *hw, + __le16 *pda, + u32 pda_addr, + u16 pda_len, + int use_eeprom) /* can we get this into hw? */ { int ret; - int pda_size; + u16 pda_size; + u16 data_len = pda_len; + __le16 *data = pda; - /* Issue command to read EEPROM */ - ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); - if (ret) - return ret; + if (use_eeprom) { + /* PDA of spectrum symbol is in eeprom */ + + /* Issue command to read EEPROM */ + ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); + if (ret) + return ret; + } /* Open auxiliary port */ - ret = spectrum_aux_open(hw); + ret = hermes_aux_control(hw, 1); + printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret); if (ret) return ret; /* read PDA from EEPROM */ - spectrum_aux_setaddr(hw, PDA_ADDR); - hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2); + hermes_aux_setaddr(hw, pda_addr); + hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); + + /* Close aux port */ + ret = hermes_aux_control(hw, 0); + printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret); /* Check PDA length */ pda_size = le16_to_cpu(pda[0]); + printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n", + pda_size, pda_len); if (pda_size > pda_len) return -EINVAL; return 0; } -EXPORT_SYMBOL(spectrum_read_pda); +EXPORT_SYMBOL(hermes_read_pda); -/* Parse PDA and write the records into the adapter */ -int -spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, - __le16 *pda) +/* Parse PDA and write the records into the adapter + * + * Attempt to write every records that is in the specified pda + * which also has a valid production data record for the firmware. + */ +int hermes_apply_pda(hermes_t *hw, + const char *first_pdr, + const __le16 *pda) { int ret; - struct pdi *pdi; - struct pdr *first_pdr; - const struct dblock *blk = first_block; - - /* Skip all blocks to locate Plug Data References */ - while (dblock_addr(blk) != BLOCK_END) - blk = (struct dblock *) &blk->data[dblock_len(blk)]; + const struct pdi *pdi; + struct pdr *pdr; - first_pdr = (struct pdr *) blk; + pdr = (struct pdr *) first_pdr; /* Go through every PDI and plug them into the adapter */ - pdi = (struct pdi *) (pda + 2); + pdi = (const struct pdi *) (pda + 2); while (pdi_id(pdi) != PDI_END) { - ret = spectrum_plug_pdi(hw, first_pdr, pdi); + ret = hermes_plug_pdi(hw, pdr, pdi); if (ret) return ret; /* Increment to the next PDI */ - pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; + pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; } return 0; } -EXPORT_SYMBOL(spectrum_apply_pda); +EXPORT_SYMBOL(hermes_apply_pda); + +/* Identify the total number of bytes in all blocks + * including the header data. + */ +size_t +hermes_blocks_length(const char *first_block) +{ + const struct dblock *blk = (const struct dblock *) first_block; + int total_len = 0; + int len; + + /* Skip all blocks to locate Plug Data References + * (Spectrum CS) */ + while (dblock_addr(blk) != BLOCK_END) { + len = dblock_len(blk); + total_len += sizeof(*blk) + len; + blk = (struct dblock *) &blk->data[len]; + } + + return total_len; +} +EXPORT_SYMBOL(hermes_blocks_length); + +/*** Hermes programming ***/ -/* Load firmware blocks into the adapter */ -int -spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) +/* Program the data blocks */ +int hermes_program(hermes_t *hw, const char *first_block, const char *end) { const struct dblock *blk; u32 blkaddr; u32 blklen; +#if LIMIT_PROGRAM_SIZE + u32 addr; + u32 len; +#endif + + blk = (const struct dblock *) first_block; + + if ((const char *) blk > (end - sizeof(*blk))) + return -EIO; - blk = first_block; blkaddr = dblock_addr(blk); blklen = dblock_len(blk); - while (dblock_addr(blk) != BLOCK_END) { - spectrum_aux_setaddr(hw, blkaddr); + while ((blkaddr != BLOCK_END) && + (((const char *) blk + blklen) <= end)) { + printk(KERN_DEBUG PFX + "Programming block of length %d to address 0x%08x\n", + blklen, blkaddr); + +#if !LIMIT_PROGRAM_SIZE + /* wl_lkm driver splits this into writes of 2000 bytes */ + hermes_aux_setaddr(hw, blkaddr); hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, blklen); +#else + len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE; + addr = blkaddr; + + while (addr < (blkaddr + blklen)) { + printk(KERN_DEBUG PFX + "Programming subblock of length %d " + "to address 0x%08x. Data @ %p\n", + len, addr, &blk->data[addr - blkaddr]); + + hermes_aux_setaddr(hw, addr); + hermes_write_bytes(hw, HERMES_AUXDATA, + &blk->data[addr - blkaddr], + len); + + addr += len; + len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ? + (blkaddr + blklen - addr) : MAX_DL_SIZE; + } +#endif + blk = (const struct dblock *) &blk->data[blklen]; + + if ((const char *) blk > (end - sizeof(*blk))) + return -EIO; - blk = (struct dblock *) &blk->data[blklen]; blkaddr = dblock_addr(blk); blklen = dblock_len(blk); } return 0; } -EXPORT_SYMBOL(spectrum_load_blocks); +EXPORT_SYMBOL(hermes_program); static int __init init_hermes_dld(void) { diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h index 2c8892ac635..af75c030b11 100644 --- a/drivers/net/wireless/hermes_dld.h +++ b/drivers/net/wireless/hermes_dld.h @@ -27,19 +27,17 @@ #include "hermes.h" -/* Position of PDA in the adapter memory */ -#define EEPROM_ADDR 0x3000 -#define EEPROM_LEN 0x200 -#define PDA_OFFSET 0x100 +int hermes_program(hermes_t *hw, const char *first_block, const char *end); -#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) -#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) +int hermes_read_pda(hermes_t *hw, + __le16 *pda, + u32 pda_addr, + u16 pda_len, + int use_eeprom); +int hermes_apply_pda(hermes_t *hw, + const char *first_pdr, + const __le16 *pda); -struct dblock; - -int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len); -int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block, - __le16 *pda); -int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block); +size_t hermes_blocks_length(const char *first_block); #endif /* _HERMES_DLD_H */ diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 579873d0e8c..2fb00183cd7 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -166,11 +166,12 @@ spectrum_reset(struct pcmcia_device *link, int idle) */ static int spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, - const unsigned char *image, int secondary) + const unsigned char *image, const unsigned char *end, + int secondary) { int ret; const unsigned char *ptr; - const struct dblock *first_block; + const unsigned char *first_block; /* Plug Data Area (PDA) */ __le16 pda[PDA_WORDS]; @@ -178,11 +179,11 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, /* Binary block begins after the 0x1A marker */ ptr = image; while (*ptr++ != TEXT_END); - first_block = (const struct dblock *) ptr; + first_block = ptr; - /* Read the PDA */ + /* Read the PDA from EEPROM */ if (secondary) { - ret = spectrum_read_pda(hw, pda, sizeof(pda)); + ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1); if (ret) return ret; } @@ -193,13 +194,15 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, return ret; /* Program the adapter with new firmware */ - ret = spectrum_load_blocks(hw, first_block); + ret = hermes_program(hw, first_block, end); if (ret) return ret; /* Write the PDA to the adapter */ if (secondary) { - ret = spectrum_apply_pda(hw, first_block, pda); + size_t len = hermes_blocks_length(first_block); + ptr = first_block + len; + ret = hermes_apply_pda(hw, ptr, pda); if (ret) return ret; } @@ -242,7 +245,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) } /* Load primary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, 0); + ret = spectrum_dl_image(hw, link, fw_entry->data, + fw_entry->data + fw_entry->size, 0); release_firmware(fw_entry); if (ret) { printk(KERN_ERR PFX "Primary firmware download failed\n"); @@ -257,7 +261,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) } /* Load secondary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, 1); + ret = spectrum_dl_image(hw, link, fw_entry->data, + fw_entry->data + fw_entry->size, 1); release_firmware(fw_entry); if (ret) { printk(KERN_ERR PFX "Secondary firmware download failed\n"); -- cgit v1.2.3 From 8f5ae73c5366128d3800cf9765507422bcf1ef96 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:53 +0100 Subject: orinoco: Extend hermes_dld routines for Agere firmware Add programming initialisation and termination functions. Add checks to avoid overrunning the firmware image or PDA areas. Extra algorithm to program PDA values using defaults where necessary. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes_dld.c | 285 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/hermes_dld.h | 5 + 2 files changed, 290 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c index 22ae79dae41..d8c626e61a3 100644 --- a/drivers/net/wireless/hermes_dld.c +++ b/drivers/net/wireless/hermes_dld.c @@ -70,6 +70,12 @@ MODULE_LICENSE("Dual MPL/GPL"); #define HERMES_AUX_PW1 0xDC23 #define HERMES_AUX_PW2 0xBA45 +/* HERMES_CMD_DOWNLD */ +#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) + /* End markers used in dblocks */ #define PDI_END 0x00000000 /* End of PDA */ #define BLOCK_END 0xFFFFFFFF /* Last image block */ @@ -247,6 +253,23 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id) return NULL; } +/* Scan production data items for a particular entry */ +static struct pdi * +hermes_find_pdi(struct pdi *first_pdi, u32 record_id) +{ + struct pdi *pdi = first_pdi; + + while (pdi_id(pdi) != PDI_END) { + + /* If the record ID matches, we are done */ + if (pdi_id(pdi) == record_id) + return pdi; + + pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; + } + return NULL; +} + /* Process one Plug Data Item - find corresponding PDR and plug it */ static int hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) @@ -290,6 +313,15 @@ int hermes_read_pda(hermes_t *hw, ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); if (ret) return ret; + } else { + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + data_len = pda_len - 4; + data = pda + 2; } /* Open auxiliary port */ @@ -370,6 +402,94 @@ EXPORT_SYMBOL(hermes_blocks_length); /*** Hermes programming ***/ +/* About to start programming data (Hermes I) + * offset is the entry point + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +int hermesi_program_init(hermes_t *hw, u32 offset) +{ + int err; + + /* Disable interrupts?*/ + /*hw->inten = 0x0;*/ + /*hermes_write_regn(hw, INTEN, 0);*/ + /*hermes_set_irqmask(hw, 0);*/ + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Using doicmd_wait rather than docmd_wait */ + err = hermes_doicmd_wait(hw, + 0x0100 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_doicmd_wait(hw, + 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_aux_control(hw, 1); + printk(KERN_DEBUG PFX "AUX enable returned %d\n", err); + + if (err) + return err; + + printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); + err = hermes_doicmd_wait(hw, + HERMES_PROGRAM_ENABLE_VOLATILE, + offset & 0xFFFFu, + offset >> 16, + 0, + NULL); + printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n", + err); + + return err; +} +EXPORT_SYMBOL(hermesi_program_init); + +/* Done programming data (Hermes I) + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +int hermesi_program_end(hermes_t *hw) +{ + struct hermes_response resp; + int rc = 0; + int err; + + rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); + + printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, " + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", + rc, resp.resp0, resp.resp1, resp.resp2); + + if ((rc == 0) && + ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) + rc = -EIO; + + err = hermes_aux_control(hw, 0); + printk(KERN_DEBUG PFX "AUX disable returned %d\n", err); + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Reinitialise, ignoring return */ + (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + + return rc ? rc : err; +} +EXPORT_SYMBOL(hermesi_program_end); + /* Program the data blocks */ int hermes_program(hermes_t *hw, const char *first_block, const char *end) { @@ -443,3 +563,168 @@ static void __exit exit_hermes_dld(void) module_init(init_hermes_dld); module_exit(exit_hermes_dld); + +/*** Default plugging data for Hermes I ***/ +/* Values from wl_lkm_718/hcf/dhf.c */ + +#define DEFINE_DEFAULT_PDR(pid, length, data) \ +static const struct { \ + __le16 len; \ + __le16 id; \ + u8 val[length]; \ +} __attribute__ ((packed)) default_pdr_data_##pid = { \ + __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ + sizeof(__le16)) - 1), \ + __constant_cpu_to_le16(pid), \ + data \ +} + +#define DEFAULT_PDR(pid) default_pdr_data_##pid + +/* HWIF Compatiblity */ +DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); + +/* PPPPSign */ +DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); + +/* PPPPProf */ +DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); + +/* Antenna diversity */ +DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); + +/* Modem VCO band Set-up */ +DEFINE_DEFAULT_PDR(0x0160, 28, + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00"); + +/* Modem Rx Gain Table Values */ +DEFINE_DEFAULT_PDR(0x0161, 256, + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3E\01\x3E\x01\x3D\x01" + "\x3D\x01\x3C\01\x3C\x01\x3B\x01" + "\x3B\x01\x3A\01\x3A\x01\x39\x01" + "\x39\x01\x38\01\x38\x01\x37\x01" + "\x37\x01\x36\01\x36\x01\x35\x01" + "\x35\x01\x34\01\x34\x01\x33\x01" + "\x33\x01\x32\x01\x32\x01\x31\x01" + "\x31\x01\x30\x01\x30\x01\x7B\x01" + "\x7B\x01\x7A\x01\x7A\x01\x79\x01" + "\x79\x01\x78\x01\x78\x01\x77\x01" + "\x77\x01\x76\x01\x76\x01\x75\x01" + "\x75\x01\x74\x01\x74\x01\x73\x01" + "\x73\x01\x72\x01\x72\x01\x71\x01" + "\x71\x01\x70\x01\x70\x01\x68\x01" + "\x68\x01\x67\x01\x67\x01\x66\x01" + "\x66\x01\x65\x01\x65\x01\x57\x01" + "\x57\x01\x56\x01\x56\x01\x55\x01" + "\x55\x01\x54\x01\x54\x01\x53\x01" + "\x53\x01\x52\x01\x52\x01\x51\x01" + "\x51\x01\x50\x01\x50\x01\x48\x01" + "\x48\x01\x47\x01\x47\x01\x46\x01" + "\x46\x01\x45\x01\x45\x01\x44\x01" + "\x44\x01\x43\x01\x43\x01\x42\x01" + "\x42\x01\x41\x01\x41\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01"); + +/* Write PDA according to certain rules. + * + * For every production data record, look for a previous setting in + * the pda, and use that. + * + * For certain records, use defaults if they are not found in pda. + */ +int hermes_apply_pda_with_defaults(hermes_t *hw, + const char *first_pdr, + const __le16 *pda) +{ + const struct pdr *pdr = (const struct pdr *) first_pdr; + struct pdi *first_pdi = (struct pdi *) &pda[2]; + struct pdi *pdi; + struct pdi *default_pdi = NULL; + struct pdi *outdoor_pdi; + void *end = (void *)first_pdr + MAX_PDA_SIZE; + int record_id; + + while (((void *)pdr < end) && + (pdr_id(pdr) != PDI_END)) { + /* + * For spectrum_cs firmwares, + * PDR area is currently not terminated by PDI_END. + * It's followed by CRC records, which have the type + * field where PDR has length. The type can be 0 or 1. + */ + if (pdr_len(pdr) < 2) + break; + record_id = pdr_id(pdr); + + pdi = hermes_find_pdi(first_pdi, record_id); + if (pdi) + printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n", + record_id, pdi); + + switch (record_id) { + case 0x110: /* Modem REFDAC values */ + case 0x120: /* Modem VGDAC values */ + outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1); + default_pdi = NULL; + if (outdoor_pdi) { + pdi = outdoor_pdi; + printk(KERN_DEBUG PFX + "Using outdoor record 0x%04x at %p\n", + record_id + 1, pdi); + } + break; + case 0x5: /* HWIF Compatiblity */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); + break; + case 0x108: /* PPPPSign */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); + break; + case 0x109: /* PPPPProf */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); + break; + case 0x150: /* Antenna diversity */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); + break; + case 0x160: /* Modem VCO band Set-up */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); + break; + case 0x161: /* Modem Rx Gain Table Values */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); + break; + default: + default_pdi = NULL; + break; + } + if (!pdi && default_pdi) { + /* Use default */ + pdi = default_pdi; + printk(KERN_DEBUG PFX + "Using default record 0x%04x at %p\n", + record_id, pdi); + } + + if (pdi) { + /* Lengths of the data in PDI and PDR must match */ + if (pdi_len(pdi) == pdr_len(pdr)) { + /* do the actual plugging */ + hermes_aux_setaddr(hw, pdr_addr(pdr)); + hermes_write_bytes(hw, HERMES_AUXDATA, + pdi->data, pdi_len(pdi)); + } + } + + pdr++; + } + return 0; +} +EXPORT_SYMBOL(hermes_apply_pda_with_defaults); diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h index af75c030b11..6fcb2627799 100644 --- a/drivers/net/wireless/hermes_dld.h +++ b/drivers/net/wireless/hermes_dld.h @@ -27,6 +27,8 @@ #include "hermes.h" +int hermesi_program_init(hermes_t *hw, u32 offset); +int hermesi_program_end(hermes_t *hw); int hermes_program(hermes_t *hw, const char *first_block, const char *end); int hermes_read_pda(hermes_t *hw, @@ -37,6 +39,9 @@ int hermes_read_pda(hermes_t *hw, int hermes_apply_pda(hermes_t *hw, const char *first_pdr, const __le16 *pda); +int hermes_apply_pda_with_defaults(hermes_t *hw, + const char *first_pdr, + const __le16 *pda); size_t hermes_blocks_length(const char *first_block); -- cgit v1.2.3 From 3994d502017a2239e30152d1231843ad05d04a7b Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:54 +0100 Subject: orinoco: Invoke firmware download in main driver Firmware download is enabled for Agere in orinoco_cs. Symbol firmware download has been moved out of spectrum_cs into orinoco_cs. Firmware download is not enabled for Intersil. Symbol based firmware is restricted to only download on spectrum_cs based cards. The firmware names are hardcoded for each firmware type. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/airport.c | 3 +- drivers/net/wireless/orinoco.c | 314 +++++++++++++++++++++++++++++++++- drivers/net/wireless/orinoco.h | 9 +- drivers/net/wireless/orinoco_cs.c | 3 +- drivers/net/wireless/orinoco_nortel.c | 3 +- drivers/net/wireless/orinoco_pci.c | 3 +- drivers/net/wireless/orinoco_plx.c | 3 +- drivers/net/wireless/orinoco_tmd.c | 3 +- drivers/net/wireless/spectrum_cs.c | 159 ++--------------- 10 files changed, 346 insertions(+), 156 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 1ac46ad48c3..ea7da7117f4 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -335,6 +335,7 @@ config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 select WIRELESS_EXT + select FW_LOADER ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast @@ -424,7 +425,6 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" depends on PCMCIA && HERMES - select FW_LOADER ---help--- This is a driver for 802.11b cards using RAM-loadable Symbol diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c index 6f7eb9f5922..ce03a2e865f 100644 --- a/drivers/net/wireless/airport.c +++ b/drivers/net/wireless/airport.c @@ -180,7 +180,8 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match) } /* Allocate space for private device-specific data */ - dev = alloc_orinocodev(sizeof(*card), airport_hard_reset); + dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, + airport_hard_reset, NULL); if (! dev) { printk(KERN_ERR PFX "Cannot allocate network device\n"); return -ENODEV; diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 00b1d595fa3..306697aa333 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -82,12 +82,14 @@ #include #include #include +#include #include #include #include #include #include "hermes_rid.h" +#include "hermes_dld.h" #include "orinoco.h" /********************************************************************/ @@ -301,6 +303,272 @@ static void orinoco_bss_data_init(struct orinoco_private *priv) list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); } + +/********************************************************************/ +/* Download functionality */ +/********************************************************************/ + +struct fw_info { + char *pri_fw; + char *sta_fw; + char *ap_fw; + u32 pda_addr; + u16 pda_size; +}; + +const static struct fw_info orinoco_fw[] = { + { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, + { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, + { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 0x100 } +}; + +/* Structure used to access fields in FW + * Make sure LE decoding macros are used + */ +struct orinoco_fw_header { + char hdr_vers[6]; /* ASCII string for header version */ + __le16 headersize; /* Total length of header */ + __le32 entry_point; /* NIC entry point */ + __le32 blocks; /* Number of blocks to program */ + __le32 block_offset; /* Offset of block data from eof header */ + __le32 pdr_offset; /* Offset to PDR data from eof header */ + __le32 pri_offset; /* Offset to primary plug data */ + __le32 compat_offset; /* Offset to compatibility data*/ + char signature[0]; /* FW signature length headersize-20 */ +} __attribute__ ((packed)); + +/* Download either STA or AP firmware into the card. */ +static int +orinoco_dl_firmware(struct orinoco_private *priv, + const struct fw_info *fw, + int ap) +{ + /* Plug Data Area (PDA) */ + __le16 pda[512] = { 0 }; + + hermes_t *hw = &priv->hw; + const struct firmware *fw_entry; + const struct orinoco_fw_header *hdr; + const unsigned char *first_block; + const unsigned char *end; + const char *firmware; + struct net_device *dev = priv->ndev; + int err; + + if (ap) + firmware = fw->ap_fw; + else + firmware = fw->sta_fw; + + printk(KERN_DEBUG "%s: Attempting to download firmware %s\n", + dev->name, firmware); + + /* Read current plug data */ + err = hermes_read_pda(hw, pda, fw->pda_addr, + min_t(u16, fw->pda_size, sizeof(pda)), 0); + printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err); + if (err) + return err; + + err = request_firmware(&fw_entry, firmware, priv->dev); + if (err) { + printk(KERN_ERR "%s: Cannot find firmware %s\n", + dev->name, firmware); + return -ENOENT; + } + + hdr = (const struct orinoco_fw_header *) fw_entry->data; + + /* Enable aux port to allow programming */ + err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point)); + printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err); + if (err != 0) + goto abort; + + /* Program data */ + first_block = (fw_entry->data + + le16_to_cpu(hdr->headersize) + + le32_to_cpu(hdr->block_offset)); + end = fw_entry->data + fw_entry->size; + + err = hermes_program(hw, first_block, end); + printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, err); + if (err != 0) + goto abort; + + /* Update production data */ + first_block = (fw_entry->data + + le16_to_cpu(hdr->headersize) + + le32_to_cpu(hdr->pdr_offset)); + + err = hermes_apply_pda_with_defaults(hw, first_block, pda); + printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err); + if (err) + goto abort; + + /* Tell card we've finished */ + err = hermesi_program_end(hw); + printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err); + if (err != 0) + goto abort; + + /* Check if we're running */ + printk(KERN_DEBUG "%s: hermes_present returned %d\n", + dev->name, hermes_present(hw)); + +abort: + release_firmware(fw_entry); + return err; +} + +/* End markers */ +#define TEXT_END 0x1A /* End of text header */ + +/* + * Process a firmware image - stop the card, load the firmware, reset + * the card and make sure it responds. For the secondary firmware take + * care of the PDA - read it and then write it on top of the firmware. + */ +static int +symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, + const unsigned char *image, const unsigned char *end, + int secondary) +{ + hermes_t *hw = &priv->hw; + int ret; + const unsigned char *ptr; + const unsigned char *first_block; + + /* Plug Data Area (PDA) */ + __le16 pda[256]; + + /* Binary block begins after the 0x1A marker */ + ptr = image; + while (*ptr++ != TEXT_END); + first_block = ptr; + + /* Read the PDA from EEPROM */ + if (secondary) { + ret = hermes_read_pda(hw, pda, fw->pda_addr, sizeof(pda), 1); + if (ret) + return ret; + } + + /* Stop the firmware, so that it can be safely rewritten */ + if (priv->stop_fw) { + ret = priv->stop_fw(priv, 1); + if (ret) + return ret; + } + + /* Program the adapter with new firmware */ + ret = hermes_program(hw, first_block, end); + if (ret) + return ret; + + /* Write the PDA to the adapter */ + if (secondary) { + size_t len = hermes_blocks_length(first_block); + ptr = first_block + len; + ret = hermes_apply_pda(hw, ptr, pda); + if (ret) + return ret; + } + + /* Run the firmware */ + if (priv->stop_fw) { + ret = priv->stop_fw(priv, 0); + if (ret) + return ret; + } + + /* Reset hermes chip and make sure it responds */ + ret = hermes_init(hw); + + /* hermes_reset() should return 0 with the secondary firmware */ + if (secondary && ret != 0) + return -ENODEV; + + /* And this should work with any firmware */ + if (!hermes_present(hw)) + return -ENODEV; + + return 0; +} + + +/* + * Download the firmware into the card, this also does a PCMCIA soft + * reset on the card, to make sure it's in a sane state. + */ +static int +symbol_dl_firmware(struct orinoco_private *priv, + const struct fw_info *fw) +{ + struct net_device *dev = priv->ndev; + int ret; + const struct firmware *fw_entry; + + if (request_firmware(&fw_entry, fw->pri_fw, + priv->dev) != 0) { + printk(KERN_ERR "%s: Cannot find firmware: %s\n", + dev->name, fw->pri_fw); + return -ENOENT; + } + + /* Load primary firmware */ + ret = symbol_dl_image(priv, fw, fw_entry->data, + fw_entry->data + fw_entry->size, 0); + release_firmware(fw_entry); + if (ret) { + printk(KERN_ERR "%s: Primary firmware download failed\n", + dev->name); + return ret; + } + + if (request_firmware(&fw_entry, fw->sta_fw, + priv->dev) != 0) { + printk(KERN_ERR "%s: Cannot find firmware: %s\n", + dev->name, fw->sta_fw); + return -ENOENT; + } + + /* Load secondary firmware */ + ret = symbol_dl_image(priv, fw, fw_entry->data, + fw_entry->data + fw_entry->size, 1); + release_firmware(fw_entry); + if (ret) { + printk(KERN_ERR "%s: Secondary firmware download failed\n", + dev->name); + } + + return ret; +} + +static int orinoco_download(struct orinoco_private *priv) +{ + int err = 0; + /* Reload firmware */ + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* case FIRMWARE_TYPE_INTERSIL: */ + err = orinoco_dl_firmware(priv, + &orinoco_fw[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 */ /********************************************************************/ @@ -2043,6 +2311,12 @@ static void orinoco_reset(struct work_struct *work) } } + 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", @@ -2254,6 +2528,7 @@ static int determine_firmware(struct net_device *dev) priv->has_ibss = 1; priv->has_wep = 0; priv->has_big_wep = 0; + priv->do_fw_download = 0; /* Determine capabilities from the firmware version */ switch (priv->firmware_type) { @@ -2273,6 +2548,7 @@ static int determine_firmware(struct net_device *dev) 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); /* Tested with Agere firmware : @@ -2317,6 +2593,21 @@ static int determine_firmware(struct net_device *dev) 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) || @@ -2387,6 +2678,20 @@ static int orinoco_init(struct net_device *dev) 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) @@ -2529,8 +2834,11 @@ static int orinoco_init(struct net_device *dev) return err; } -struct net_device *alloc_orinocodev(int sizeof_card, - int (*hard_reset)(struct orinoco_private *)) +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; @@ -2545,6 +2853,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, + sizeof(struct orinoco_private)); else priv->card = NULL; + priv->dev = device; if (orinoco_bss_data_allocate(priv)) goto err_out_free; @@ -2570,6 +2879,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, 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; diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index c6b1858abde..e0acb633e9d 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -44,7 +44,9 @@ typedef struct { struct orinoco_private { void *card; /* Pointer to card dependent structure */ + struct device *dev; int (*hard_reset)(struct orinoco_private *); + int (*stop_fw)(struct orinoco_private *, int); /* Synchronisation stuff */ spinlock_t lock; @@ -83,6 +85,7 @@ struct orinoco_private { unsigned int has_preamble:1; unsigned int has_sensitivity:1; unsigned int has_hostscan:1; + unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; @@ -130,8 +133,10 @@ extern int orinoco_debug; /* Exported prototypes */ /********************************************************************/ -extern struct net_device *alloc_orinocodev(int sizeof_card, - int (*hard_reset)(struct orinoco_private *)); +extern struct net_device *alloc_orinocodev( + int sizeof_card, struct device *device, + int (*hard_reset)(struct orinoco_private *), + int (*stop_fw)(struct orinoco_private *, int)); extern void free_orinocodev(struct net_device *dev); extern int __orinoco_up(struct net_device *dev); extern int __orinoco_down(struct net_device *dev); diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index 1c216e015f6..1ccf5a40cf0 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -109,7 +109,8 @@ orinoco_cs_probe(struct pcmcia_device *link) struct orinoco_private *priv; struct orinoco_pccard *card; - dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset); + dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), + orinoco_cs_hard_reset, NULL); if (! dev) return -ENOMEM; priv = netdev_priv(dev); diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c index 35ec5fcf81a..2fc86596302 100644 --- a/drivers/net/wireless/orinoco_nortel.c +++ b/drivers/net/wireless/orinoco_nortel.c @@ -182,7 +182,8 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), orinoco_nortel_cor_reset); + dev = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_nortel_cor_reset, NULL); if (!dev) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c index 2547d5dac0d..4ebd638a073 100644 --- a/drivers/net/wireless/orinoco_pci.c +++ b/drivers/net/wireless/orinoco_pci.c @@ -139,7 +139,8 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset); + dev = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_pci_cor_reset, NULL); if (!dev) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c index 98fe165337d..ef761857bb3 100644 --- a/drivers/net/wireless/orinoco_plx.c +++ b/drivers/net/wireless/orinoco_plx.c @@ -221,7 +221,8 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), orinoco_plx_cor_reset); + dev = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_plx_cor_reset, NULL); if (!dev) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco_tmd.c index df493185a4a..ede24ec309c 100644 --- a/drivers/net/wireless/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco_tmd.c @@ -124,7 +124,8 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), orinoco_tmd_cor_reset); + dev = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_tmd_cor_reset, NULL); if (!dev) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 2fb00183cd7..e368759d1d8 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -33,10 +32,6 @@ #include #include "orinoco.h" -#include "hermes_dld.h" - -static const char primary_fw_name[] = "symbol_sp24t_prim_fw"; -static const char secondary_fw_name[] = "symbol_sp24t_sec_fw"; /********************************************************************/ /* Module stuff */ @@ -72,26 +67,11 @@ struct orinoco_pccard { static int spectrum_cs_config(struct pcmcia_device *link); static void spectrum_cs_release(struct pcmcia_device *link); -/********************************************************************/ -/* Firmware downloader */ -/********************************************************************/ - -/* Position of PDA in the adapter memory */ -#define EEPROM_ADDR 0x3000 -#define EEPROM_LEN 0x200 -#define PDA_OFFSET 0x100 - -#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET) -#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2) - /* Constants for the CISREG_CCSR register */ #define HCR_RUN 0x07 /* run firmware after reset */ #define HCR_IDLE 0x0E /* don't run firmware after reset */ #define HCR_MEM16 0x10 /* memory width bit, should be preserved */ -/* End markers */ -#define TEXT_END 0x1A /* End of text header */ - #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) @@ -158,142 +138,29 @@ spectrum_reset(struct pcmcia_device *link, int idle) return -ENODEV; } +/********************************************************************/ +/* Device methods */ +/********************************************************************/ -/* - * Process a firmware image - stop the card, load the firmware, reset - * the card and make sure it responds. For the secondary firmware take - * care of the PDA - read it and then write it on top of the firmware. - */ static int -spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, - const unsigned char *image, const unsigned char *end, - int secondary) +spectrum_cs_hard_reset(struct orinoco_private *priv) { - int ret; - const unsigned char *ptr; - const unsigned char *first_block; - - /* Plug Data Area (PDA) */ - __le16 pda[PDA_WORDS]; - - /* Binary block begins after the 0x1A marker */ - ptr = image; - while (*ptr++ != TEXT_END); - first_block = ptr; - - /* Read the PDA from EEPROM */ - if (secondary) { - ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1); - if (ret) - return ret; - } - - /* Stop the firmware, so that it can be safely rewritten */ - ret = spectrum_reset(link, 1); - if (ret) - return ret; - - /* Program the adapter with new firmware */ - ret = hermes_program(hw, first_block, end); - if (ret) - return ret; - - /* Write the PDA to the adapter */ - if (secondary) { - size_t len = hermes_blocks_length(first_block); - ptr = first_block + len; - ret = hermes_apply_pda(hw, ptr, pda); - if (ret) - return ret; - } - - /* Run the firmware */ - ret = spectrum_reset(link, 0); - if (ret) - return ret; - - /* Reset hermes chip and make sure it responds */ - ret = hermes_init(hw); - - /* hermes_reset() should return 0 with the secondary firmware */ - if (secondary && ret != 0) - return -ENODEV; + struct orinoco_pccard *card = priv->card; + struct pcmcia_device *link = card->p_dev; - /* And this should work with any firmware */ - if (!hermes_present(hw)) - return -ENODEV; + /* Soft reset using COR and HCR */ + spectrum_reset(link, 0); return 0; } - -/* - * Download the firmware into the card, this also does a PCMCIA soft - * reset on the card, to make sure it's in a sane state. - */ static int -spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) -{ - int ret; - const struct firmware *fw_entry; - - if (request_firmware(&fw_entry, primary_fw_name, - &handle_to_dev(link)) != 0) { - printk(KERN_ERR PFX "Cannot find firmware: %s\n", - primary_fw_name); - return -ENOENT; - } - - /* Load primary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, - fw_entry->data + fw_entry->size, 0); - release_firmware(fw_entry); - if (ret) { - printk(KERN_ERR PFX "Primary firmware download failed\n"); - return ret; - } - - if (request_firmware(&fw_entry, secondary_fw_name, - &handle_to_dev(link)) != 0) { - printk(KERN_ERR PFX "Cannot find firmware: %s\n", - secondary_fw_name); - return -ENOENT; - } - - /* Load secondary firmware */ - ret = spectrum_dl_image(hw, link, fw_entry->data, - fw_entry->data + fw_entry->size, 1); - release_firmware(fw_entry); - if (ret) { - printk(KERN_ERR PFX "Secondary firmware download failed\n"); - } - - return ret; -} - -/********************************************************************/ -/* Device methods */ -/********************************************************************/ - -static int -spectrum_cs_hard_reset(struct orinoco_private *priv) +spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) { struct orinoco_pccard *card = priv->card; struct pcmcia_device *link = card->p_dev; - int err; - if (!hermes_present(&priv->hw)) { - /* The firmware needs to be reloaded */ - if (spectrum_dl_firmware(&priv->hw, link) != 0) { - printk(KERN_ERR PFX "Firmware download failed\n"); - err = -ENODEV; - } - } else { - /* Soft reset using COR and HCR */ - spectrum_reset(link, 0); - } - - return 0; + return spectrum_reset(link, idle); } /********************************************************************/ @@ -315,7 +182,9 @@ spectrum_cs_probe(struct pcmcia_device *link) struct orinoco_private *priv; struct orinoco_pccard *card; - dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset); + dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), + spectrum_cs_hard_reset, + spectrum_cs_stop_firmware); if (! dev) return -ENOMEM; priv = netdev_priv(dev); @@ -517,7 +386,7 @@ spectrum_cs_config(struct pcmcia_device *link) dev->irq = link->irq.AssignedIRQ; card->node.major = card->node.minor = 0; - /* Reset card and download firmware */ + /* Reset card */ if (spectrum_cs_hard_reset(priv) != 0) { goto failed; } -- cgit v1.2.3 From 6eecad77861b193bcfc109de12835ca0bddf7c58 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:56 +0100 Subject: orinoco: Fix transmit for Agere/Lucent with fw 9.x The tx control word has moved into the 802.11 header area on these firmwares. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes.h | 1 + drivers/net/wireless/orinoco.c | 59 +++++++++++++++++++++++++++++++----------- drivers/net/wireless/orinoco.h | 1 + 3 files changed, 46 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h index 287f5363fda..1d0c5840f82 100644 --- a/drivers/net/wireless/hermes.h +++ b/drivers/net/wireless/hermes.h @@ -179,6 +179,7 @@ #define HERMES_802_11_OFFSET (14) #define HERMES_802_3_OFFSET (14+32) #define HERMES_802_2_OFFSET (14+32+14) +#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2) #define HERMES_RXSTAT_ERR (0x0003) #define HERMES_RXSTAT_BADCRC (0x0001) diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 306697aa333..b83415daa72 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -722,7 +722,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) u16 txfid = priv->txfid; struct ethhdr *eh; int data_off; - struct hermes_tx_descriptor desc; + int tx_control; unsigned long flags; if (! netif_running(dev)) { @@ -756,21 +756,48 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) eh = (struct ethhdr *)skb->data; - memset(&desc, 0, sizeof(desc)); - desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX); - 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; - } + tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; - /* 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); + 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 */ @@ -2528,6 +2555,7 @@ static int determine_firmware(struct net_device *dev) 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 */ @@ -2550,6 +2578,7 @@ static int determine_firmware(struct net_device *dev) 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 diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index e0acb633e9d..f93752ffb07 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -85,6 +85,7 @@ struct orinoco_private { unsigned int has_preamble:1; unsigned int has_sensitivity:1; unsigned int has_hostscan:1; + unsigned int has_alt_txcntl:1; unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; -- cgit v1.2.3 From 3056c40426b9cd4f7c334d773d69db64e195431d Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:57 +0100 Subject: orinoco: address checkpatch typedef warning Just sprinkle the necessary structs around... Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 13 +++++++------ drivers/net/wireless/orinoco.h | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index b83415daa72..3d5570d0a26 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -279,7 +279,8 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv) return 0; priv->bss_data = - kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(bss_element), GFP_KERNEL); + kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element), + GFP_KERNEL); if (!priv->bss_data) { printk(KERN_WARNING "Out of memory allocating beacons"); return -ENOMEM; @@ -1413,8 +1414,8 @@ static void orinoco_send_wevents(struct work_struct *work) static inline void orinoco_clear_scan_results(struct orinoco_private *priv, unsigned long scan_age) { - bss_element *bss; - bss_element *tmp_bss; + 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) { @@ -1489,7 +1490,7 @@ static int orinoco_process_scan_results(struct net_device *dev, /* Read the entries one by one */ for (; offset + atom_len <= len; offset += atom_len) { int found = 0; - bss_element *bss = NULL; + struct bss_element *bss = NULL; /* Get next atom */ atom = (union hermes_scan_info *) (buf + offset); @@ -1511,7 +1512,7 @@ static int orinoco_process_scan_results(struct net_device *dev, /* Grab a bss off the free list */ if (!found && !list_empty(&priv->bss_free_list)) { bss = list_entry(priv->bss_free_list.next, - bss_element, list); + struct bss_element, list); list_del(priv->bss_free_list.next); list_add_tail(&bss->list, &priv->bss_list); @@ -4547,7 +4548,7 @@ static int orinoco_ioctl_getscan(struct net_device *dev, char *extra) { struct orinoco_private *priv = netdev_priv(dev); - bss_element *bss; + struct bss_element *bss; int err = 0; unsigned long flags; char *current_ev = extra; diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index f93752ffb07..584d8c92db1 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -36,11 +36,11 @@ typedef enum { FIRMWARE_TYPE_SYMBOL } fwtype_t; -typedef struct { +struct bss_element { union hermes_scan_info bss; unsigned long last_scanned; struct list_head list; -} bss_element; +}; struct orinoco_private { void *card; /* Pointer to card dependent structure */ @@ -117,7 +117,7 @@ struct orinoco_private { /* Scanning support */ struct list_head bss_list; struct list_head bss_free_list; - bss_element *bss_data; + struct bss_element *bss_data; int scan_inprogress; /* Scan pending... */ u32 scan_mode; /* Type of scan done */ -- cgit v1.2.3 From 01632fa4af34addf78ce999eabb4430f33942ee2 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:58 +0100 Subject: orinoco: Use extended Agere scans available on 9.x series firmwares This provides more information than the standard Agere scan, including the WPA IE. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes.h | 34 +++ drivers/net/wireless/hermes_rid.h | 1 + drivers/net/wireless/orinoco.c | 448 ++++++++++++++++++++++++++++++++++---- drivers/net/wireless/orinoco.h | 9 +- 4 files changed, 448 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h index 1d0c5840f82..113f3f6cb2d 100644 --- a/drivers/net/wireless/hermes.h +++ b/drivers/net/wireless/hermes.h @@ -303,6 +303,40 @@ union hermes_scan_info { struct symbol_scan_apinfo s; }; +/* Extended scan struct for HERMES_INQ_CHANNELINFO. + * wl_lkm calls this an ACS scan (Automatic Channel Select). + * Keep out of union hermes_scan_info because it is much bigger than + * the older scan structures. */ +struct agere_ext_scan_info { + __le16 reserved0; + + u8 noise; + u8 level; + u8 rx_flow; + u8 rate; + __le16 reserved1[2]; + + __le16 frame_control; + __le16 dur_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le16 sequence; + u8 addr4[ETH_ALEN]; + + __le16 data_length; + + /* Next 3 fields do not get filled in. */ + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + __le16 len_type; + + __le64 timestamp; + __le16 beacon_interval; + __le16 capabilities; + u8 data[316]; +} __attribute__ ((packed)); + #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) #define HERMES_LINKSTATUS_CONNECTED (0x0001) #define HERMES_LINKSTATUS_DISCONNECTED (0x0002) diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h index 4f46b4809e5..bcd9c82fe93 100644 --- a/drivers/net/wireless/hermes_rid.h +++ b/drivers/net/wireless/hermes_rid.h @@ -85,6 +85,7 @@ #define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 #define HERMES_RID_CNFBASICRATES 0xFCB3 #define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2 #define HERMES_RID_CNFTICKTIME 0xFCE0 #define HERMES_RID_CNFSCANREQUEST 0xFCE1 #define HERMES_RID_CNFJOINREQUEST 0xFCE2 diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 3d5570d0a26..22718e8176f 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -275,13 +275,19 @@ static inline void set_port_type(struct orinoco_private *priv) #define ORINOCO_MAX_BSS_COUNT 64 static int orinoco_bss_data_allocate(struct orinoco_private *priv) { - if (priv->bss_data) + if (priv->bss_xbss_data) return 0; - priv->bss_data = - kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element), - GFP_KERNEL); - if (!priv->bss_data) { + if (priv->has_ext_scan) + priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * + sizeof(struct xbss_element), + GFP_KERNEL); + else + priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * + sizeof(struct bss_element), + GFP_KERNEL); + + if (!priv->bss_xbss_data) { printk(KERN_WARNING "Out of memory allocating beacons"); return -ENOMEM; } @@ -290,18 +296,53 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv) static void orinoco_bss_data_free(struct orinoco_private *priv) { - kfree(priv->bss_data); - priv->bss_data = NULL; + kfree(priv->bss_xbss_data); + priv->bss_xbss_data = NULL; } +#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data) +#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data) static void orinoco_bss_data_init(struct orinoco_private *priv) { int i; INIT_LIST_HEAD(&priv->bss_free_list); INIT_LIST_HEAD(&priv->bss_list); - for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) - list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); + if (priv->has_ext_scan) + for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) + list_add_tail(&(PRIV_XBSS[i].list), + &priv->bss_free_list); + else + for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) + list_add_tail(&(PRIV_BSS[i].list), + &priv->bss_free_list); + +} + +static inline u8 *orinoco_get_ie(u8 *data, size_t len, + enum ieee80211_mfie eid) +{ + u8 *p = data; + while ((p + 2) < (data + len)) { + if (p[0] == eid) + return p; + p += p[1] + 2; + } + return NULL; +} + +#define WPA_OUI_TYPE "\x00\x50\xF2\x01" +#define WPA_SELECTOR_LEN 4 +static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) +{ + u8 *p = data; + while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { + if ((p[0] == MFIE_TYPE_GENERIC) && + (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) + return p; + p += p[1] + 2; + } + return NULL; } @@ -1414,18 +1455,72 @@ static void orinoco_send_wevents(struct work_struct *work) 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; + if (priv->has_ext_scan) { + struct xbss_element *bss; + struct xbss_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->bss, 0, sizeof(bss->bss)); + bss->last_scanned = 0; + } } + } else { + 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->bss, 0, sizeof(bss->bss)); + bss->last_scanned = 0; + } + } + } +} + +static void orinoco_add_ext_scan_result(struct orinoco_private *priv, + struct agere_ext_scan_info *atom) +{ + struct xbss_element *bss = NULL; + int found = 0; + + /* Try to update an existing bss first */ + list_for_each_entry(bss, &priv->bss_list, list) { + if (compare_ether_addr(bss->bss.bssid, atom->bssid)) + continue; + /* ESSID lengths */ + if (bss->bss.data[1] != atom->data[1]) + continue; + if (memcmp(&bss->bss.data[2], &atom->data[2], + atom->data[1])) + 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 xbss_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; } } @@ -1700,6 +1795,63 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) kfree(buf); } break; + case HERMES_INQ_CHANNELINFO: + { + struct agere_ext_scan_info *bss; + + if (!priv->scan_inprogress) { + printk(KERN_DEBUG "%s: Got chaninfo without scan, " + "len=%d\n", dev->name, len); + break; + } + + /* An empty result indicates that the scan is complete */ + if (len == 0) { + union iwreq_data wrqu; + + /* Scan is no longer in progress */ + priv->scan_inprogress = 0; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + break; + } + + /* Sanity check */ + else if (len > sizeof(*bss)) { + printk(KERN_WARNING + "%s: Ext scan results too large (%d bytes). " + "Truncating results to %zd bytes.\n", + dev->name, len, sizeof(*bss)); + len = sizeof(*bss); + } else if (len < (offsetof(struct agere_ext_scan_info, + data) + 2)) { + /* Drop this result now so we don't have to + * keep checking later */ + printk(KERN_WARNING + "%s: Ext scan results too short (%d bytes)\n", + dev->name, len); + break; + } + + bss = kmalloc(sizeof(*bss), GFP_ATOMIC); + if (bss == NULL) + break; + + /* Read scan data */ + err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len, + infofid, sizeof(info)); + if (err) { + kfree(bss); + break; + } + + orinoco_add_ext_scan_result(priv, bss); + + kfree(bss); + break; + } case HERMES_INQ_SEC_STAT_AGERE: /* Security status (Agere specific) */ /* Ignore this frame for now */ @@ -2557,6 +2709,7 @@ static int determine_firmware(struct net_device *dev) priv->has_wep = 0; priv->has_big_wep = 0; priv->has_alt_txcntl = 0; + priv->has_ext_scan = 0; priv->do_fw_download = 0; /* Determine capabilities from the firmware version */ @@ -2580,7 +2733,7 @@ static int determine_firmware(struct net_device *dev) priv->do_fw_download = 1; priv->broken_monitor = (firmver >= 0x80000); priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ - + priv->has_ext_scan = (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 */ @@ -2735,6 +2888,12 @@ static int orinoco_init(struct net_device *dev) printk("40-bit key\n"); } + /* Now we have the firmware capabilities, allocate appropiate + * sized scan buffers */ + if (orinoco_bss_data_allocate(priv)) + goto out; + orinoco_bss_data_init(priv); + /* Get the MAC address */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, ETH_ALEN, NULL, dev->dev_addr); @@ -2885,10 +3044,6 @@ struct net_device 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; @@ -2924,10 +3079,6 @@ struct net_device priv->last_linkstatus = 0xffff; return dev; - -err_out_free: - free_netdev(dev); - return NULL; } void free_orinocodev(struct net_device *dev) @@ -4375,7 +4526,25 @@ static int orinoco_ioctl_setscan(struct net_device *dev, if (err) break; - err = hermes_inquire(hw, HERMES_INQ_SCAN); + if (priv->has_ext_scan) { + /* Clear scan results at the start of + * an extended scan */ + orinoco_clear_scan_results(priv, + msecs_to_jiffies(15000)); + + /* TODO: Is this available on older firmware? + * Can we use it to scan specific channels + * for IW_SCAN_THIS_FREQ? */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANCHANNELS2GHZ, + 0x7FFF); + if (err) + goto out; + + err = hermes_inquire(hw, + HERMES_INQ_CHANNELINFO); + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); break; } } else @@ -4541,6 +4710,171 @@ static inline char *orinoco_translate_scan(struct net_device *dev, return current_ev; } +static inline char *orinoco_translate_ext_scan(struct net_device *dev, + struct iw_request_info *info, + char *current_ev, + char *end_buf, + struct agere_ext_scan_info *bss, + unsigned int last_scanned) +{ + u16 capabilities; + u16 channel; + struct iw_event iwe; /* Temporary buffer */ + char custom[MAX_CUSTOM_LEN]; + u8 *ie; + + memset(&iwe, 0, sizeof(iwe)); + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + /* Add the ESSID */ + ie = bss->data; + iwe.u.data.length = ie[1]; + if (iwe.u.data.length) { + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, &ie[2]); + } + + /* Add mode */ + capabilities = le16_to_cpu(bss->capabilities); + if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + iwe.cmd = SIOCGIWMODE; + if (capabilities & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET); + channel = ie ? ie[2] : 0; + if ((channel >= 1) && (channel <= NUM_CHANNELS)) { + /* Add channel and frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = channel; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + + iwe.u.freq.m = channel_frequency[channel-1] * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + } + + /* Add quality statistics. level and noise in dB. No link quality */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; + iwe.u.qual.level = bss->level - 0x95; + iwe.u.qual.noise = bss->noise - 0x95; + /* Wireless tools prior to 27.pre22 will show link quality + * anyway, so we provide a reasonable value. */ + if (iwe.u.qual.level > iwe.u.qual.noise) + iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; + else + iwe.u.qual.qual = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (capabilities & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, NULL); + + /* WPA IE */ + ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); + if (ie) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie[1] + 2; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie); + } + + /* RSN IE */ + ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN); + if (ie) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie[1] + 2; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie); + } + + ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES); + if (ie) { + char *p = current_ev + iwe_stream_lcp_len(info); + int i; + + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 2; i < (ie[1] + 2); i++) { + iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); + p = iwe_stream_add_value(info, current_ev, p, end_buf, + &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if (p > (current_ev + iwe_stream_lcp_len(info))) + current_ev = p; + } + + /* Timestamp */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + "tsf=%016llx", + le64_to_cpu(bss->timestamp)); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + /* Beacon interval */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + "bcn_int=%d", + le16_to_cpu(bss->beacon_interval)); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + /* Capabilites */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + "capab=0x%04x", + capabilities); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. */ + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, + " Last beacon: %dms ago", + jiffies_to_msecs(jiffies - last_scanned)); + if (iwe.u.data.length) + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); + + return current_ev; +} + /* Return results of a scan */ static int orinoco_ioctl_getscan(struct net_device *dev, struct iw_request_info *info, @@ -4548,7 +4882,6 @@ static int orinoco_ioctl_getscan(struct net_device *dev, char *extra) { struct orinoco_private *priv = netdev_priv(dev); - struct bss_element *bss; int err = 0; unsigned long flags; char *current_ev = extra; @@ -4568,18 +4901,47 @@ static int orinoco_ioctl_getscan(struct net_device *dev, goto out; } - list_for_each_entry(bss, &priv->bss_list, list) { - /* Translate to WE format this entry */ - current_ev = orinoco_translate_scan(dev, info, current_ev, - extra + srq->length, - &bss->bss, - bss->last_scanned); - - /* Check if there is space for one more entry */ - if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a bigger buffer */ - err = -E2BIG; - goto out; + if (priv->has_ext_scan) { + struct xbss_element *bss; + + list_for_each_entry(bss, &priv->bss_list, list) { + /* Translate this entry to WE format */ + current_ev = + orinoco_translate_ext_scan(dev, info, + current_ev, + extra + srq->length, + &bss->bss, + bss->last_scanned); + + /* Check if there is space for one more entry */ + if ((extra + srq->length - current_ev) + <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a + * bigger buffer */ + err = -E2BIG; + goto out; + } + } + + } else { + struct bss_element *bss; + + list_for_each_entry(bss, &priv->bss_list, list) { + /* Translate this entry to WE format */ + current_ev = orinoco_translate_scan(dev, info, + current_ev, + extra + srq->length, + &bss->bss, + bss->last_scanned); + + /* Check if there is space for one more entry */ + if ((extra + srq->length - current_ev) + <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a + * bigger buffer */ + err = -E2BIG; + goto out; + } } } diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index 584d8c92db1..f510994ea9f 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -42,6 +42,12 @@ struct bss_element { struct list_head list; }; +struct xbss_element { + struct agere_ext_scan_info bss; + unsigned long last_scanned; + struct list_head list; +}; + struct orinoco_private { void *card; /* Pointer to card dependent structure */ struct device *dev; @@ -86,6 +92,7 @@ struct orinoco_private { unsigned int has_sensitivity:1; unsigned int has_hostscan:1; unsigned int has_alt_txcntl:1; + unsigned int has_ext_scan:1; unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; @@ -117,7 +124,7 @@ struct orinoco_private { /* Scanning support */ struct list_head bss_list; struct list_head bss_free_list; - struct bss_element *bss_data; + void *bss_xbss_data; int scan_inprogress; /* Scan pending... */ u32 scan_mode; /* Type of scan done */ -- cgit v1.2.3 From 4ae6ee2d72e4904909d63c6ebb0240b254b72e85 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:27:59 +0100 Subject: orinoco: Don't use boolean parameter to record encoding type For WPA support we need to encode NONE, WEP and TKIP in the encoding parameter. In anticipation of this we need to change the usage away from the current boolean usage. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 31 +++++++++++++++++++------------ drivers/net/wireless/orinoco.h | 2 +- 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 22718e8176f..b853ba0cd4e 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -2100,8 +2100,9 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) int err = 0; int master_wep_flag; int auth_flag; + int enc_flag; - if (priv->wep_on) + if (priv->encode_alg == IW_ENCODE_ALG_WEP) __orinoco_hw_setup_wepkeys(priv); if (priv->wep_restrict) @@ -2109,9 +2110,14 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) else auth_flag = HERMES_AUTH_OPEN; + if (priv->encode_alg == IW_ENCODE_ALG_WEP) + enc_flag = 1; + else + enc_flag = 0; + switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ - if (priv->wep_on) { + if (priv->encode_alg == IW_ENCODE_ALG_WEP) { /* Enable the shared-key authentication. */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION_AGERE, @@ -2119,14 +2125,14 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPENABLED_AGERE, - priv->wep_on); + enc_flag); 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->encode_alg == IW_ENCODE_ALG_WEP) { if (priv->wep_restrict || (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | @@ -3008,7 +3014,7 @@ static int orinoco_init(struct net_device *dev) priv->channel = 0; /* use firmware default */ priv->promiscuous = 0; - priv->wep_on = 0; + priv->encode_alg = IW_ENCODE_ALG_NONE; priv->tx_key = 0; /* Make the hardware available, as long as it hasn't been @@ -3497,7 +3503,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, 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 encode_alg = priv->encode_alg; int restricted = priv->wep_restrict; u16 xlen = 0; int err = -EINPROGRESS; /* Call commit handler */ @@ -3531,9 +3537,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, xlen = 0; /* Switch on WEP if off */ - if ((!enable) && (xlen > 0)) { + if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) { setindex = index; - enable = 1; + encode_alg = IW_ENCODE_ALG_WEP; } } else { /* Important note : if the user do "iwconfig eth0 enc off", @@ -3555,7 +3561,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, } if (erq->flags & IW_ENCODE_DISABLED) - enable = 0; + encode_alg = IW_ENCODE_ALG_NONE; if (erq->flags & IW_ENCODE_OPEN) restricted = 0; if (erq->flags & IW_ENCODE_RESTRICTED) @@ -3570,14 +3576,15 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, priv->tx_key = setindex; /* Try fast key change if connected and only keys are changed */ - if (priv->wep_on && enable && (priv->wep_restrict == restricted) && + if ((priv->encode_alg == encode_alg) && + (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->encode_alg = encode_alg; priv->wep_restrict = restricted; out: @@ -3606,7 +3613,7 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, index = priv->tx_key; erq->flags = 0; - if (! priv->wep_on) + if (!priv->encode_alg) erq->flags |= IW_ENCODE_DISABLED; erq->flags |= index + 1; diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index f510994ea9f..5605fd30b81 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -100,7 +100,7 @@ struct orinoco_private { /* Configuration paramaters */ u32 iw_mode; int prefer_port3; - u16 wep_on, wep_restrict, tx_key; + u16 encode_alg, wep_restrict, tx_key; struct orinoco_key keys[ORINOCO_MAX_KEYS]; int bitratemode; char nick[IW_ESSID_MAX_SIZE+1]; -- cgit v1.2.3 From 6cd90b1cac290c64a93b7e992bb1ef495988465f Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:00 +0100 Subject: orinoco: Split wevent work thread from wevent sending This allows us to send more wevents from the work thread. We will need to do this to support WPA. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index b853ba0cd4e..21bed6c28ad 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1424,34 +1424,38 @@ static void orinoco_join_ap(struct work_struct *work) } /* Send new BSSID to userspace */ -static void orinoco_send_wevents(struct work_struct *work) +static void orinoco_send_bssid_wevent(struct orinoco_private *priv) { - 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; + return; wrqu.ap_addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +} + +static void orinoco_send_wevents(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, wevent_work); + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return; + + orinoco_send_bssid_wevent(priv); - out: orinoco_unlock(priv, &flags); } - static inline void orinoco_clear_scan_results(struct orinoco_private *priv, unsigned long scan_age) { -- cgit v1.2.3 From 409644a98bfffef79985f2c39924a06288b1dfcf Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:01 +0100 Subject: orinoco: Use a macro to define wireless handlers The macro identifiers for the various ioctls required for WPA support are longer than those currently used by the driver. This makes it messy to keep line length below 80 character. By defining a macro to initialise the handler table, we recover the common text. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 66 ++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 21bed6c28ad..e91e240b637 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -5043,39 +5043,41 @@ static const struct iw_priv_args orinoco_privtab[] = { * Structures to export the Wireless Handlers */ +#define STD_IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = (iw_handler) func static const iw_handler orinoco_handler[] = { - [SIOCSIWCOMMIT-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_commit, - [SIOCGIWNAME -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getname, - [SIOCSIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfreq, - [SIOCGIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfreq, - [SIOCSIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setmode, - [SIOCGIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getmode, - [SIOCSIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setsens, - [SIOCGIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getsens, - [SIOCGIWRANGE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwrange, - [SIOCSIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_set_spy, - [SIOCGIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_get_spy, - [SIOCSIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy, - [SIOCGIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy, - [SIOCSIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setwap, - [SIOCGIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getwap, - [SIOCSIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setscan, - [SIOCGIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getscan, - [SIOCSIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setessid, - [SIOCGIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getessid, - [SIOCSIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setnick, - [SIOCGIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getnick, - [SIOCSIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrate, - [SIOCGIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrate, - [SIOCSIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrts, - [SIOCGIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrts, - [SIOCSIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfrag, - [SIOCGIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfrag, - [SIOCGIWRETRY -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getretry, - [SIOCSIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setiwencode, - [SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwencode, - [SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setpower, - [SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getpower, + STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), + STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname), + STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), + STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), + STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode), + STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode), + STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), + STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), + STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange), + STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), + STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), + STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan), + STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan), + STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), + STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), + STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick), + STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick), + STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), + STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), + STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), + STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), + STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), + STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), + STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), + STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), + STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), + STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), + STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), }; -- cgit v1.2.3 From d03032af511c56d3c1580fa4f54f6285f650e638 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:02 +0100 Subject: orinoco: Add WE-18 ioctls for WPA Includes basic plumbing to get the data into firmware, and retrieve it. SIOCxIWGENIE simply record (and return) the IE, and do not act on it. SIOCxIWENCODEEXT, SIOCxIWAUTH and SIOCSIWMLME should be as functional as the driver will support. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/hermes_rid.h | 16 ++ drivers/net/wireless/orinoco.c | 526 +++++++++++++++++++++++++++++++++++++- drivers/net/wireless/orinoco.h | 20 ++ 3 files changed, 554 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h index bcd9c82fe93..42eb67dea1d 100644 --- a/drivers/net/wireless/hermes_rid.h +++ b/drivers/net/wireless/hermes_rid.h @@ -30,6 +30,7 @@ #define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 #define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 #define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 +#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22 #define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 #define HERMES_RID_CNFDEFAULTKEY0 0xFC24 #define HERMES_RID_CNFDEFAULTKEY1 0xFC25 @@ -85,7 +86,16 @@ #define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 #define HERMES_RID_CNFBASICRATES 0xFCB3 #define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4 +#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5 +#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6 +#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7 +#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8 +#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9 +#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA +#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB #define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2 +#define HERMES_RID_CNFDISASSOCIATE 0xFCC8 #define HERMES_RID_CNFTICKTIME 0xFCE0 #define HERMES_RID_CNFSCANREQUEST 0xFCE1 #define HERMES_RID_CNFJOINREQUEST 0xFCE2 @@ -138,6 +148,12 @@ #define HERMES_RID_CURRENTTXRATE6 0xFD85 #define HERMES_RID_OWNMACADDR 0xFD86 #define HERMES_RID_SCANRESULTSTABLE 0xFD88 +#define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89 +#define HERMES_RID_CURRENT_WPA_IE 0xFD8A +#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B +#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C +#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D +#define HERMES_RID_TXQUEUEEMPTY 0xFD91 #define HERMES_RID_PHYTYPE 0xFDC0 #define HERMES_RID_CURRENTCHANNEL 0xFDC1 #define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index e91e240b637..36b1dc2db89 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -2038,7 +2039,7 @@ static int __orinoco_hw_set_wap(struct orinoco_private *priv) } /* Change the WEP keys and/or the current keys. Can be called - * either from __orinoco_hw_setup_wep() or directly from + * either from __orinoco_hw_setup_enc() 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. */ @@ -2098,7 +2099,7 @@ static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) return 0; } -static int __orinoco_hw_setup_wep(struct orinoco_private *priv) +static int __orinoco_hw_setup_enc(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; @@ -2106,7 +2107,8 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) int auth_flag; int enc_flag; - if (priv->encode_alg == IW_ENCODE_ALG_WEP) + /* Setup WEP keys for WEP and WPA */ + if (priv->encode_alg) __orinoco_hw_setup_wepkeys(priv); if (priv->wep_restrict) @@ -2114,7 +2116,9 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) else auth_flag = HERMES_AUTH_OPEN; - if (priv->encode_alg == IW_ENCODE_ALG_WEP) + if (priv->wpa_enabled) + enc_flag = 2; + else if (priv->encode_alg == IW_ENCODE_ALG_WEP) enc_flag = 1; else enc_flag = 0; @@ -2132,6 +2136,16 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) enc_flag); if (err) return err; + + if (priv->has_wpa) { + /* Set WPA key management */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, + priv->key_mgmt); + if (err) + return err; + } + break; case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ @@ -2168,6 +2182,84 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) return 0; } +/* key must be 32 bytes, including the tx and rx MIC keys. + * rsc must be 8 bytes + * tsc must be 8 bytes or NULL + */ +static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, + u8 *key, u8 *rsc, u8 *tsc) +{ + struct { + __le16 idx; + u8 rsc[IW_ENCODE_SEQ_MAX_SIZE]; + u8 key[TKIP_KEYLEN]; + u8 tx_mic[MIC_KEYLEN]; + u8 rx_mic[MIC_KEYLEN]; + u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; + } __attribute__ ((packed)) buf; + int ret; + int err; + int k; + u16 xmitting; + + key_idx &= 0x3; + + if (set_tx) + key_idx |= 0x8000; + + buf.idx = cpu_to_le16(key_idx); + memcpy(buf.key, key, + sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); + + if (rsc == NULL) + memset(buf.rsc, 0, sizeof(buf.rsc)); + else + memcpy(buf.rsc, rsc, sizeof(buf.rsc)); + + if (tsc == NULL) { + memset(buf.tsc, 0, sizeof(buf.tsc)); + buf.tsc[4] = 0x10; + } else { + memcpy(buf.tsc, tsc, sizeof(buf.tsc)); + } + + /* Wait upto 100ms for tx queue to empty */ + k = 100; + do { + k--; + udelay(1000); + ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, + &xmitting); + if (ret) + break; + } while ((k > 0) && xmitting); + + if (k == 0) + ret = -ETIMEDOUT; + + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, + &buf); + + return ret ? ret : err; +} + +static int orinoco_clear_tkip_key(struct orinoco_private *priv, + int key_idx) +{ + hermes_t *hw = &priv->hw; + int err; + + memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx])); + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, + key_idx); + if (err) + printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", + priv->ndev->name, err, key_idx); + return err; +} + static int __orinoco_program_rids(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); @@ -2364,10 +2456,10 @@ static int __orinoco_program_rids(struct net_device *dev) } /* Set up encryption */ - if (priv->has_wep) { - err = __orinoco_hw_setup_wep(priv); + if (priv->has_wep || priv->has_wpa) { + err = __orinoco_hw_setup_enc(priv); if (err) { - printk(KERN_ERR "%s: Error %d activating WEP\n", + printk(KERN_ERR "%s: Error %d activating encryption\n", dev->name, err); return err; } @@ -2720,6 +2812,7 @@ static int determine_firmware(struct net_device *dev) priv->has_big_wep = 0; priv->has_alt_txcntl = 0; priv->has_ext_scan = 0; + priv->has_wpa = 0; priv->do_fw_download = 0; /* Determine capabilities from the firmware version */ @@ -2744,6 +2837,7 @@ static int determine_firmware(struct net_device *dev) priv->broken_monitor = (firmver >= 0x80000); priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ + priv->has_wpa = (firmver >= 0x9002a); /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * Tested CableTron firmware : 4.32 => Anton */ @@ -2897,6 +2991,8 @@ static int orinoco_init(struct net_device *dev) else printk("40-bit key\n"); } + if (priv->has_wpa) + printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name); /* Now we have the firmware capabilities, allocate appropiate * sized scan buffers */ @@ -3020,6 +3116,11 @@ static int orinoco_init(struct net_device *dev) priv->promiscuous = 0; priv->encode_alg = IW_ENCODE_ALG_NONE; priv->tx_key = 0; + priv->wpa_enabled = 0; + priv->tkip_cm_active = 0; + priv->key_mgmt = 0; + priv->wpa_ie_len = 0; + priv->wpa_ie = NULL; /* Make the hardware available, as long as it hasn't been * removed elsewhere (e.g. by PCMCIA hot unplug) */ @@ -3095,6 +3196,8 @@ void free_orinocodev(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); + priv->wpa_ie_len = 0; + kfree(priv->wpa_ie); orinoco_bss_data_free(priv); free_netdev(dev); } @@ -3406,7 +3509,7 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, memset(range, 0, sizeof(struct iw_range)); range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 14; + range->we_version_source = 22; /* Set available channels/frequencies */ range->num_channels = NUM_CHANNELS; @@ -3436,6 +3539,9 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, } } + if (priv->has_wpa) + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; + if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){ /* Quality stats meaningless in ad-hoc mode */ } else { @@ -3528,6 +3634,10 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, if (orinoco_lock(priv, &flags) != 0) return -EBUSY; + /* Clear any TKIP key we have */ + if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP)) + (void) orinoco_clear_tkip_key(priv, setindex); + if (erq->length > 0) { if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; @@ -4192,6 +4302,399 @@ static int orinoco_ioctl_getpower(struct net_device *dev, return err; } +static int orinoco_ioctl_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, alg = ext->alg, set_key = 1; + unsigned long flags; + int err = -EINVAL; + u16 key_len; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Determine and validate the key index */ + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if ((idx < 1) || (idx > WEP_KEYS)) + goto out; + idx--; + } else + idx = priv->tx_key; + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { + /* Clear any TKIP TX key we had */ + (void) orinoco_clear_tkip_key(priv, priv->tx_key); + } + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + priv->tx_key = idx; + set_key = ((alg == IW_ENCODE_ALG_TKIP) || + (ext->key_len > 0)) ? 1 : 0; + } + + if (set_key) { + /* Set the requested key first */ + switch (alg) { + case IW_ENCODE_ALG_NONE: + priv->encode_alg = alg; + priv->keys[idx].len = 0; + break; + + case IW_ENCODE_ALG_WEP: + if (ext->key_len > SMALL_KEY_SIZE) + key_len = LARGE_KEY_SIZE; + else if (ext->key_len > 0) + key_len = SMALL_KEY_SIZE; + else + goto out; + + priv->encode_alg = alg; + priv->keys[idx].len = cpu_to_le16(key_len); + + key_len = min(ext->key_len, key_len); + + memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE); + memcpy(priv->keys[idx].data, ext->key, key_len); + break; + + case IW_ENCODE_ALG_TKIP: + { + hermes_t *hw = &priv->hw; + u8 *tkip_iv = NULL; + + if (!priv->has_wpa || + (ext->key_len > sizeof(priv->tkip_key[0]))) + goto out; + + priv->encode_alg = alg; + memset(&priv->tkip_key[idx], 0, + sizeof(priv->tkip_key[idx])); + memcpy(&priv->tkip_key[idx], ext->key, ext->key_len); + + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) + tkip_iv = &ext->rx_seq[0]; + + err = __orinoco_hw_set_tkip_key(hw, idx, + ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, + (u8 *) &priv->tkip_key[idx], + tkip_iv, NULL); + if (err) + printk(KERN_ERR "%s: Error %d setting TKIP key" + "\n", dev->name, err); + + goto out; + } + default: + goto out; + } + } + err = -EINPROGRESS; + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + unsigned long flags; + int err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = -EINVAL; + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if ((idx < 1) || (idx > WEP_KEYS)) + goto out; + idx--; + } else + idx = priv->tx_key; + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + ext->alg = priv->encode_alg; + switch (priv->encode_alg) { + case IW_ENCODE_ALG_NONE: + ext->key_len = 0; + encoding->flags |= IW_ENCODE_DISABLED; + break; + case IW_ENCODE_ALG_WEP: + ext->key_len = min(le16_to_cpu(priv->keys[idx].len), + (u16) max_key_len); + memcpy(ext->key, priv->keys[idx].data, ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + break; + case IW_ENCODE_ALG_TKIP: + ext->key_len = min((u16) sizeof(struct orinoco_tkip_key), + (u16) max_key_len); + memcpy(ext->key, &priv->tkip_key[idx], ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + break; + } + + err = 0; + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + struct iw_param *param = &wrqu->param; + unsigned long flags; + int ret = -EINPROGRESS; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_PRIVACY_INVOKED: + case IW_AUTH_DROP_UNENCRYPTED: + /* + * orinoco does not use these parameters + */ + break; + + case IW_AUTH_KEY_MGMT: + /* wl_lkm implies value 2 == PSK for Hermes I + * which ties in with WEXT + * no other hints tho :( + */ + priv->key_mgmt = param->value; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + /* When countermeasures are enabled, shut down the + * card; when disabled, re-enable the card. This must + * take effect immediately. + * + * TODO: Make sure that the EAPOL message is getting + * out before card disabled + */ + if (param->value) { + priv->tkip_cm_active = 1; + ret = hermes_enable_port(hw, 0); + } else { + priv->tkip_cm_active = 0; + ret = hermes_disable_port(hw, 0); + } + break; + + case IW_AUTH_80211_AUTH_ALG: + if (param->value & IW_AUTH_ALG_SHARED_KEY) + priv->wep_restrict = 1; + else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) + priv->wep_restrict = 0; + else + ret = -EINVAL; + break; + + case IW_AUTH_WPA_ENABLED: + if (priv->has_wpa) { + priv->wpa_enabled = param->value ? 1 : 0; + } else { + if (param->value) + ret = -EOPNOTSUPP; + /* else silently accept disable of WPA */ + priv->wpa_enabled = 0; + } + break; + + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + +static int orinoco_ioctl_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct iw_param *param = &wrqu->param; + unsigned long flags; + int ret = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_KEY_MGMT: + param->value = priv->key_mgmt; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + param->value = priv->tkip_cm_active; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (priv->wep_restrict) + param->value = IW_AUTH_ALG_SHARED_KEY; + else + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = priv->wpa_enabled; + break; + + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + +static int orinoco_ioctl_set_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + u8 *buf; + unsigned long flags; + int err = 0; + + if ((wrqu->data.length > MAX_WPA_IE_LEN) || + (wrqu->data.length && (extra == NULL))) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (wrqu->data.length) { + buf = kmalloc(wrqu->data.length, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto out; + } + + memcpy(buf, extra, wrqu->data.length); + kfree(priv->wpa_ie); + priv->wpa_ie = buf; + priv->wpa_ie_len = wrqu->data.length; + } else { + kfree(priv->wpa_ie); + priv->wpa_ie = NULL; + priv->wpa_ie_len = 0; + } + + if (priv->wpa_ie) { + /* Looks like wl_lkm wants to check the auth alg, and + * somehow pass it to the firmware. + * Instead it just calls the key mgmt rid + * - we do this in set auth. + */ + } + +out: + orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; + int err = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { + wrqu->data.length = 0; + goto out; + } + + if (wrqu->data.length < priv->wpa_ie_len) { + err = -E2BIG; + goto out; + } + + wrqu->data.length = priv->wpa_ie_len; + memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); + +out: + orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + unsigned long flags; + int ret = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + /* silently ignore */ + break; + + case IW_MLME_DISASSOC: + { + struct { + u8 addr[ETH_ALEN]; + __le16 reason_code; + } __attribute__ ((packed)) buf; + + memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN); + buf.reason_code = cpu_to_le16(mlme->reason_code); + ret = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFDISASSOCIATE, + &buf); + break; + } + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, @@ -5078,6 +5581,13 @@ static const iw_handler orinoco_handler[] = { STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), + STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), + STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), + STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), + STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), + STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), + STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), + STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), }; diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index 5605fd30b81..bfab88f5136 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -30,6 +30,15 @@ struct orinoco_key { char data[ORINOCO_MAX_KEY_SIZE]; } __attribute__ ((packed)); +#define TKIP_KEYLEN 16 +#define MIC_KEYLEN 8 + +struct orinoco_tkip_key { + u8 tkip[TKIP_KEYLEN]; + u8 tx_mic[MIC_KEYLEN]; + u8 rx_mic[MIC_KEYLEN]; +}; + typedef enum { FIRMWARE_TYPE_AGERE, FIRMWARE_TYPE_INTERSIL, @@ -93,6 +102,7 @@ struct orinoco_private { unsigned int has_hostscan:1; unsigned int has_alt_txcntl:1; unsigned int has_ext_scan:1; + unsigned int has_wpa:1; unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; @@ -128,6 +138,16 @@ struct orinoco_private { int scan_inprogress; /* Scan pending... */ u32 scan_mode; /* Type of scan done */ + + /* WPA support */ + u8 *wpa_ie; + int wpa_ie_len; + + struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS]; + + unsigned int wpa_enabled:1; + unsigned int tkip_cm_active:1; + unsigned int key_mgmt:3; }; #ifdef ORINOCO_DEBUG -- cgit v1.2.3 From 06009fda9fde1b97074ab3d932d0468396bf5d10 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:03 +0100 Subject: orinoco: Send association events to userspace Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 62 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 36b1dc2db89..5e6f90ba908 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1443,6 +1443,66 @@ static void orinoco_send_bssid_wevent(struct orinoco_private *priv) wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } +static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + u8 buf[88]; + u8 *ie; + + if (!priv->has_wpa) + return; + + err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, + sizeof(buf), NULL, &buf); + if (err != 0) + return; + + ie = orinoco_get_wpa_ie(buf, sizeof(buf)); + if (ie) { + int rem = sizeof(buf) - (ie - &buf[0]); + wrqu.data.length = ie[1] + 2; + if (wrqu.data.length > rem) + wrqu.data.length = rem; + + if (wrqu.data.length) + /* Send event to user space */ + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie); + } +} + +static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */ + u8 *ie; + + if (!priv->has_wpa) + return; + + err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO, + sizeof(buf), NULL, &buf); + if (err != 0) + return; + + ie = orinoco_get_wpa_ie(buf, sizeof(buf)); + if (ie) { + int rem = sizeof(buf) - (ie - &buf[0]); + wrqu.data.length = ie[1] + 2; + if (wrqu.data.length > rem) + wrqu.data.length = rem; + + if (wrqu.data.length) + /* Send event to user space */ + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie); + } +} + static void orinoco_send_wevents(struct work_struct *work) { struct orinoco_private *priv = @@ -1452,6 +1512,8 @@ static void orinoco_send_wevents(struct work_struct *work) if (orinoco_lock(priv, &flags) != 0) return; + orinoco_send_assocreqie_wevent(priv); + orinoco_send_assocrespie_wevent(priv); orinoco_send_bssid_wevent(priv); orinoco_unlock(priv, &flags); -- cgit v1.2.3 From 31afcef385bb8bf528c6fbe05b359af9f456f02a Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:04 +0100 Subject: orinoco: Process bulk of receive interrupt in a tasklet Read the packet data off the hardware and straight into an skb in the interrupt. We have to do this in case we don't process the tasklet in time. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 105 +++++++++++++++++++++++++++++++++-------- drivers/net/wireless/orinoco.h | 14 ++++++ 2 files changed, 100 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 5e6f90ba908..768b3945267 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1178,15 +1178,23 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; - u16 rxfid, status, fc; + u16 rxfid, status; int length; - struct hermes_rx_descriptor desc; - struct ethhdr *hdr; + struct hermes_rx_descriptor *desc; + struct orinoco_rx_data *rx_data; int err; + desc = kmalloc(sizeof(*desc), GFP_ATOMIC); + if (!desc) { + printk(KERN_WARNING + "%s: Can't allocate space for RX descriptor\n", + dev->name); + goto update_stats; + } + rxfid = hermes_read_regn(hw, RXFID); - err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), + err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), rxfid, 0); if (err) { printk(KERN_ERR "%s: error %d reading Rx descriptor. " @@ -1194,7 +1202,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) goto update_stats; } - status = le16_to_cpu(desc.status); + status = le16_to_cpu(desc->status); if (status & HERMES_RXSTAT_BADCRC) { DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", @@ -1205,8 +1213,8 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) /* Handle frames in monitor mode */ if (priv->iw_mode == IW_MODE_MONITOR) { - orinoco_rx_monitor(dev, rxfid, &desc); - return; + orinoco_rx_monitor(dev, rxfid, desc); + goto out; } if (status & HERMES_RXSTAT_UNDECRYPTABLE) { @@ -1216,15 +1224,14 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) goto update_stats; } - length = le16_to_cpu(desc.data_len); - fc = le16_to_cpu(desc.frame_ctl); + length = le16_to_cpu(desc->data_len); /* 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; + goto out; } if (length > IEEE80211_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", @@ -1259,6 +1266,43 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) goto drop; } + /* Add desc and skb to rx queue */ + rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC); + if (!rx_data) { + printk(KERN_WARNING "%s: Can't allocate RX packet\n", + dev->name); + goto drop; + } + rx_data->desc = desc; + rx_data->skb = skb; + list_add_tail(&rx_data->list, &priv->rx_list); + tasklet_schedule(&priv->rx_tasklet); + + return; + +drop: + dev_kfree_skb_irq(skb); +update_stats: + stats->rx_errors++; + stats->rx_dropped++; +out: + kfree(desc); +} + +static void orinoco_rx(struct net_device *dev, + struct hermes_rx_descriptor *desc, + struct sk_buff *skb) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + u16 status, fc; + int length; + struct ethhdr *hdr; + + status = le16_to_cpu(desc->status); + length = le16_to_cpu(desc->data_len); + fc = le16_to_cpu(desc->frame_ctl); + /* Handle decapsulation * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs @@ -1277,11 +1321,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); hdr->h_proto = htons(length); } - memcpy(hdr->h_dest, desc.addr1, ETH_ALEN); + memcpy(hdr->h_dest, desc->addr1, ETH_ALEN); if (fc & IEEE80211_FCTL_FROMDS) - memcpy(hdr->h_source, desc.addr3, ETH_ALEN); + memcpy(hdr->h_source, desc->addr3, ETH_ALEN); else - memcpy(hdr->h_source, desc.addr2, ETH_ALEN); + memcpy(hdr->h_source, desc->addr2, ETH_ALEN); dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, dev); @@ -1290,7 +1334,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) skb->pkt_type = PACKET_OTHERHOST; /* Process the wireless stats if needed */ - orinoco_stat_gather(dev, skb, &desc); + orinoco_stat_gather(dev, skb, desc); /* Pass the packet to the networking stack */ netif_rx(skb); @@ -1298,12 +1342,27 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) stats->rx_bytes += length; return; +} - drop: - dev_kfree_skb_irq(skb); - update_stats: - stats->rx_errors++; - stats->rx_dropped++; +static void orinoco_rx_isr_tasklet(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_rx_data *rx_data, *temp; + struct hermes_rx_descriptor *desc; + struct sk_buff *skb; + + /* extract desc and skb from queue */ + list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { + desc = rx_data->desc; + skb = rx_data->skb; + list_del(&rx_data->list); + kfree(rx_data); + + orinoco_rx(dev, desc, skb); + + kfree(desc); + } } /********************************************************************/ @@ -3248,6 +3307,10 @@ struct net_device INIT_WORK(&priv->join_work, orinoco_join_ap); INIT_WORK(&priv->wevent_work, orinoco_send_wevents); + INIT_LIST_HEAD(&priv->rx_list); + tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet, + (unsigned long) dev); + netif_carrier_off(dev); priv->last_linkstatus = 0xffff; @@ -3258,6 +3321,10 @@ void free_orinocodev(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); + /* No need to empty priv->rx_list: if the tasklet is scheduled + * when we call tasklet_kill it will run one final time, + * emptying the list */ + tasklet_kill(&priv->rx_tasklet); priv->wpa_ie_len = 0; kfree(priv->wpa_ie); orinoco_bss_data_free(priv); diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index bfab88f5136..e0c9be3daa3 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -9,6 +9,7 @@ #define DRIVER_VERSION "0.15" +#include #include #include #include @@ -57,6 +58,14 @@ struct xbss_element { struct list_head list; }; +struct hermes_rx_descriptor; + +struct orinoco_rx_data { + struct hermes_rx_descriptor *desc; + struct sk_buff *skb; + struct list_head list; +}; + struct orinoco_private { void *card; /* Pointer to card dependent structure */ struct device *dev; @@ -68,6 +77,11 @@ struct orinoco_private { int hw_unavailable; struct work_struct reset_work; + /* Interrupt tasklets */ + struct tasklet_struct rx_tasklet; + struct list_head rx_list; + struct orinoco_rx_data *rx_data; + /* driver state */ int open; u16 last_linkstatus; -- cgit v1.2.3 From 23edcc4147ad36f8d55f0eb79c21e245ffb9f211 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Thu, 21 Aug 2008 23:28:05 +0100 Subject: orinoco: Add MIC on TX and check on RX Use the MIC algorithm from the crypto subsystem. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 2 + drivers/net/wireless/hermes.h | 7 ++ drivers/net/wireless/orinoco.c | 234 ++++++++++++++++++++++++++++++++++++++--- drivers/net/wireless/orinoco.h | 2 + 4 files changed, 229 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ea7da7117f4..45bdf0b339b 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -336,6 +336,8 @@ config HERMES depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 select WIRELESS_EXT select FW_LOADER + select CRYPTO + select CRYPTO_MICHAEL_MIC ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h index 113f3f6cb2d..8b13c8fef3d 100644 --- a/drivers/net/wireless/hermes.h +++ b/drivers/net/wireless/hermes.h @@ -184,13 +184,18 @@ #define HERMES_RXSTAT_ERR (0x0003) #define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) +#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */ #define HERMES_RXSTAT_MACPORT (0x0700) #define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ +#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */ #define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ #define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ #define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */ +/* Shift amount for key ID in RXSTAT and TXCTRL */ +#define HERMES_MIC_KEY_ID_SHIFT 11 + struct hermes_tx_descriptor { __le16 status; __le16 reserved1; @@ -209,6 +214,8 @@ struct hermes_tx_descriptor { #define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */ #define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */ #define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */ +#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */ +#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */ #define HERMES_TXCTRL_ALT_RTRY (0x0020) /* Inquiry constants and data types */ diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 768b3945267..6a196c31de4 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -89,6 +89,9 @@ #include #include +#include +#include + #include "hermes_rid.h" #include "hermes_dld.h" #include "orinoco.h" @@ -243,6 +246,74 @@ struct hermes_rx_descriptor { static int __orinoco_program_rids(struct net_device *dev); static void __orinoco_set_multicast_list(struct net_device *dev); +/********************************************************************/ +/* Michael MIC crypto setup */ +/********************************************************************/ +#define MICHAEL_MIC_LEN 8 +static int orinoco_mic_init(struct orinoco_private *priv) +{ + priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + if (IS_ERR(priv->tx_tfm_mic)) { + printk(KERN_DEBUG "orinoco_mic_init: could not allocate " + "crypto API michael_mic\n"); + priv->tx_tfm_mic = NULL; + return -ENOMEM; + } + + priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + if (IS_ERR(priv->rx_tfm_mic)) { + printk(KERN_DEBUG "orinoco_mic_init: could not allocate " + "crypto API michael_mic\n"); + priv->rx_tfm_mic = NULL; + return -ENOMEM; + } + + return 0; +} + +static void orinoco_mic_free(struct orinoco_private *priv) +{ + if (priv->tx_tfm_mic) + crypto_free_hash(priv->tx_tfm_mic); + if (priv->rx_tfm_mic) + crypto_free_hash(priv->rx_tfm_mic); +} + +static int michael_mic(struct crypto_hash *tfm_michael, u8 *key, + u8 *da, u8 *sa, u8 priority, + u8 *data, size_t data_len, u8 *mic) +{ + struct hash_desc desc; + struct scatterlist sg[2]; + u8 hdr[ETH_HLEN + 2]; /* size of header + padding */ + + if (tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + + /* Copy header into buffer. We need the padding on the end zeroed */ + memcpy(&hdr[0], da, ETH_ALEN); + memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN); + hdr[ETH_ALEN*2] = priority; + hdr[ETH_ALEN*2+1] = 0; + hdr[ETH_ALEN*2+2] = 0; + hdr[ETH_ALEN*2+3] = 0; + + /* Use scatter gather to MIC header and data in one go */ + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, sizeof(hdr)); + sg_set_buf(&sg[1], data, data_len); + + if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN)) + return -1; + + desc.tfm = tfm_michael; + desc.flags = 0; + return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr), + mic); +} + /********************************************************************/ /* Internal helper functions */ /********************************************************************/ @@ -764,7 +835,6 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) int err = 0; u16 txfid = priv->txfid; struct ethhdr *eh; - int data_off; int tx_control; unsigned long flags; @@ -797,10 +867,12 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len < ETH_HLEN) goto drop; - eh = (struct ethhdr *)skb->data; - tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; + if (priv->encode_alg == IW_ENCODE_ALG_TKIP) + tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | + HERMES_TXCTRL_MIC; + if (priv->has_alt_txcntl) { /* WPA enabled firmwares have tx_cntl at the end of * the 802.11 header. So write zeroed descriptor and @@ -842,6 +914,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); } + eh = (struct ethhdr *)skb->data; + /* Encapsulate Ethernet-II frames */ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ struct header_struct { @@ -851,33 +925,65 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) /* 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; + /* Insert the SNAP header */ + if (skb_headroom(skb) < sizeof(hdr)) { + printk(KERN_ERR + "%s: Not enough headroom for 802.2 headers %d\n", + dev->name, skb_headroom(skb)); + goto drop; } - } else { /* IEEE 802.3 frame */ - data_off = HERMES_802_3_OFFSET; + eh = (struct ethhdr *) skb_push(skb, sizeof(hdr)); + memcpy(eh, &hdr, sizeof(hdr)); } err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len, - txfid, data_off); + txfid, HERMES_802_3_OFFSET); if (err) { printk(KERN_ERR "%s: Error %d writing packet to BAP\n", dev->name, err); goto busy; } + /* Calculate Michael MIC */ + if (priv->encode_alg == IW_ENCODE_ALG_TKIP) { + u8 mic_buf[MICHAEL_MIC_LEN + 1]; + u8 *mic; + size_t offset; + size_t len; + + if (skb->len % 2) { + /* MIC start is on an odd boundary */ + mic_buf[0] = skb->data[skb->len - 1]; + mic = &mic_buf[1]; + offset = skb->len - 1; + len = MICHAEL_MIC_LEN + 1; + } else { + mic = &mic_buf[0]; + offset = skb->len; + len = MICHAEL_MIC_LEN; + } + + michael_mic(priv->tx_tfm_mic, + priv->tkip_key[priv->tx_key].tx_mic, + eh->h_dest, eh->h_source, 0 /* priority */, + skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic); + + /* Write the MIC */ + err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len, + txfid, HERMES_802_3_OFFSET + offset); + if (err) { + printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", + dev->name, err); + goto busy; + } + } + /* Finally, we actually initiate the send */ netif_stop_queue(dev); @@ -892,7 +998,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) } dev->trans_start = jiffies; - stats->tx_bytes += data_off + skb->len; + stats->tx_bytes += HERMES_802_3_OFFSET + skb->len; goto ok; drop: @@ -1172,6 +1278,25 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, stats->rx_dropped++; } +/* Get tsc from the firmware */ +static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, + u8 *tsc) +{ + hermes_t *hw = &priv->hw; + int err = 0; + u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE]; + + if ((key < 0) || (key > 4)) + return -EINVAL; + + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, + sizeof(tsc_arr), NULL, &tsc_arr); + if (!err) + memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); + + return err; +} + static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); @@ -1240,6 +1365,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) goto update_stats; } + /* Payload size does not include Michael MIC. Increase payload + * size to read it together with the data. */ + if (status & HERMES_RXSTAT_MIC) + length += MICHAEL_MIC_LEN; + /* 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 @@ -1303,6 +1433,63 @@ static void orinoco_rx(struct net_device *dev, length = le16_to_cpu(desc->data_len); fc = le16_to_cpu(desc->frame_ctl); + /* Calculate and check MIC */ + if (status & HERMES_RXSTAT_MIC) { + int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >> + HERMES_MIC_KEY_ID_SHIFT); + u8 mic[MICHAEL_MIC_LEN]; + u8 *rxmic; + u8 *src = (fc & IEEE80211_FCTL_FROMDS) ? + desc->addr3 : desc->addr2; + + /* Extract Michael MIC from payload */ + rxmic = skb->data + skb->len - MICHAEL_MIC_LEN; + + skb_trim(skb, skb->len - MICHAEL_MIC_LEN); + length -= MICHAEL_MIC_LEN; + + michael_mic(priv->rx_tfm_mic, + priv->tkip_key[key_id].rx_mic, + desc->addr1, + src, + 0, /* priority or QoS? */ + skb->data, + skb->len, + &mic[0]); + + if (memcmp(mic, rxmic, + MICHAEL_MIC_LEN)) { + union iwreq_data wrqu; + struct iw_michaelmicfailure wxmic; + DECLARE_MAC_BUF(mac); + + printk(KERN_WARNING "%s: " + "Invalid Michael MIC in data frame from %s, " + "using key %i\n", + dev->name, print_mac(mac, src), key_id); + + /* TODO: update stats */ + + /* Notify userspace */ + memset(&wxmic, 0, sizeof(wxmic)); + wxmic.flags = key_id & IW_MICFAILURE_KEY_ID; + wxmic.flags |= (desc->addr1[0] & 1) ? + IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE; + wxmic.src_addr.sa_family = ARPHRD_ETHER; + memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN); + + (void) orinoco_hw_get_tkip_iv(priv, key_id, + &wxmic.tsc[0]); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(wxmic); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, + (char *) &wxmic); + + goto drop; + } + } + /* Handle decapsulation * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs @@ -1342,6 +1529,11 @@ static void orinoco_rx(struct net_device *dev, stats->rx_bytes += length; return; + + drop: + dev_kfree_skb(skb); + stats->rx_errors++; + stats->rx_dropped++; } static void orinoco_rx_isr_tasklet(unsigned long data) @@ -3112,8 +3304,14 @@ static int orinoco_init(struct net_device *dev) else printk("40-bit key\n"); } - if (priv->has_wpa) + if (priv->has_wpa) { printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name); + if (orinoco_mic_init(priv)) { + printk(KERN_ERR "%s: Failed to setup MIC crypto " + "algorithm. Disabling WPA support\n", dev->name); + priv->has_wpa = 0; + } + } /* Now we have the firmware capabilities, allocate appropiate * sized scan buffers */ @@ -3292,6 +3490,9 @@ struct net_device dev->set_multicast_list = orinoco_set_multicast_list; /* we use the default eth_mac_addr for setting the MAC addr */ + /* Reserve space in skb for the SNAP header */ + dev->hard_header_len += ENCAPS_OVERHEAD; + /* Set up default callbacks */ dev->open = orinoco_open; dev->stop = orinoco_stop; @@ -3327,6 +3528,7 @@ void free_orinocodev(struct net_device *dev) tasklet_kill(&priv->rx_tasklet); priv->wpa_ie_len = 0; kfree(priv->wpa_ie); + orinoco_mic_free(priv); orinoco_bss_data_free(priv); free_netdev(dev); } diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index e0c9be3daa3..981570bd3b9 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -158,6 +158,8 @@ struct orinoco_private { int wpa_ie_len; struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS]; + struct crypto_hash *rx_tfm_mic; + struct crypto_hash *tx_tfm_mic; unsigned int wpa_enabled:1; unsigned int tkip_cm_active:1; -- cgit v1.2.3 From c09ff7e1744047be1043cb32453ef90b7f6b8451 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 25 Aug 2008 20:22:21 +1000 Subject: linux-next: fix x86 tree build failure Today's linux-next build (powerpc allyesconfig) failed like this: drivers/char/agp/uninorth-agp.c:541: error: unknown field 'agp_destroy_paged' specified in initializer Caused by commit 5f310b63781f6777bf4e812570560ec0f8ea42d8 ("agp: enable optimized agp_alloc_pages methods"). A simple typo ... Signed-off-by: Stephen Rothwell Signed-off-by: Ingo Molnar --- drivers/char/agp/uninorth-agp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 2accc974470..0f004b65ec0 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -538,7 +538,7 @@ const struct agp_bridge_driver u3_agp_driver = { .agp_alloc_page = agp_generic_alloc_page, .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_paged = agp_generic_destroy_pages, + .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = agp_generic_type_to_mask_type, .cant_use_aperture = true, .needs_scratch_page = true, -- cgit v1.2.3 From 28501336f8b9fb5ec6c2d7bb07b4dfa88ceed272 Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Tue, 5 Aug 2008 13:52:14 +0100 Subject: [ARM] 5186/1: metronomefb: convert printk to dev_dbg/err messages This patch converts the printk messages in metronomefb to dev_dbg/err type messages. Signed-off-by: Jaya Kumar Acked-by: Eric Miao Signed-off-by: Russell King --- drivers/video/metronomefb.c | 47 +++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index cc4c038a1b3..7cda4f80af5 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -40,15 +40,6 @@ #include - -#define DEBUG 1 -#ifdef DEBUG -#define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -#define DPRINTK(f, a...) -#endif - - /* Display specific information */ #define DPY_W 832 #define DPY_H 622 @@ -135,8 +126,8 @@ static u16 calc_img_cksum(u16 *start, int length) /* here we decode the incoming waveform file and populate metromem */ #define EXP_WFORM_SIZE 47001 -static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, - u8 *frame_count) +static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, + struct metronomefb_par *par) { int tta; int wmta; @@ -148,9 +139,11 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, int wfm_idx, owfm_idx; int mem_idx = 0; struct waveform_hdr *wfm_hdr; + u8 *metromem = par->metromem_wfm; + struct device *dev = par->info->dev; if (size != EXP_WFORM_SIZE) { - printk(KERN_ERR "Error: unexpected size %d != %d\n", size, + dev_err(dev, "Error: unexpected size %d != %d\n", size, EXP_WFORM_SIZE); return -EINVAL; } @@ -158,16 +151,16 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, wfm_hdr = (struct waveform_hdr *) mem; if (wfm_hdr->fvsn != 1) { - printk(KERN_ERR "Error: bad fvsn %x\n", wfm_hdr->fvsn); + dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn); return -EINVAL; } if (wfm_hdr->luts != 0) { - printk(KERN_ERR "Error: bad luts %x\n", wfm_hdr->luts); + dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts); return -EINVAL; } cksum = calc_cksum(32, 47, mem); if (cksum != wfm_hdr->wfm_cs) { - printk(KERN_ERR "Error: bad cksum %x != %x\n", cksum, + dev_err(dev, "Error: bad cksum %x != %x\n", cksum, wfm_hdr->wfm_cs); return -EINVAL; } @@ -175,7 +168,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, wfm_hdr->trc += 1; for (i = 0; i < 5; i++) { if (*(wfm_hdr->stuff2a + i) != 0) { - printk(KERN_ERR "Error: unexpected value in padding\n"); + dev_err(dev, "Error: unexpected value in padding\n"); return -EINVAL; } } @@ -200,7 +193,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad temperature range table cksum" + dev_err(dev, "Error: bad temperature range table cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -212,7 +205,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad mode table address cksum" + dev_err(dev, "Error: bad mode table address cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -224,7 +217,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad temperature table address cksum" + dev_err(dev, "Error: bad temperature table address cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -259,11 +252,11 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(owfm_idx, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad waveform data cksum" + dev_err(dev, "Error: bad waveform data cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } - *frame_count = (mem_idx/64); + par->frame_count = (mem_idx/64); return 0; } @@ -662,15 +655,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev) b) process waveform and decode into metromem */ retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); if (retval < 0) { - printk(KERN_ERR "metronomefb: couldn't get waveform\n"); + dev_err(&dev->dev, "Failed to get waveform\n"); goto err_dma_free; } - retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, - par->metromem_wfm, 3, 31, &par->frame_count); + retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31, + par); release_firmware(fw_entry); if (retval < 0) { - printk(KERN_ERR "metronomefb: couldn't process waveform\n"); + dev_err(&dev->dev, "Failed processing waveform\n"); goto err_dma_free; } @@ -688,7 +681,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { - printk(KERN_ERR "Failed to allocate colormap\n"); + dev_err(&dev->dev, "Failed to allocate colormap\n"); goto err_fb_rel; } @@ -704,7 +697,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) platform_set_drvdata(dev, info); - printk(KERN_INFO + dev_dbg(&dev->dev, "fb%d: Metronome frame buffer device, using %dK of video" " memory\n", info->node, videomemorysize >> 10); -- cgit v1.2.3 From e935508515cc5592d7c80d7f51f21103f73efb2d Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Tue, 19 Aug 2008 11:17:55 +0100 Subject: [ARM] 5209/1: metronomefb: changes to use platform framebuffer These changes are used in order to support the use of the framebuffer provided by the platform device driver rather than to directly allocate one. Other changes are cleanup to error handling and order of release. Signed-off-by: Jaya Kumar Acked-by: Krzysztof Helt Acked-by: Eric Miao Signed-off-by: Russell King --- drivers/video/Kconfig | 18 +++- drivers/video/metronomefb.c | 241 ++++++++++++++++++++++++-------------------- 2 files changed, 147 insertions(+), 112 deletions(-) (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 70d135e0cc4..6fd4a2f63cf 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -172,11 +172,6 @@ config FB_DEFERRED_IO bool depends on FB -config FB_METRONOME - tristate - depends on FB - depends on FB_DEFERRED_IO - config FB_HECUBA tristate depends on FB @@ -2041,6 +2036,19 @@ config XEN_FBDEV_FRONTEND frame buffer driver. It communicates with a back-end in another domain. +config FB_METRONOME + tristate "E-Ink Metronome/8track controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + This driver implements support for the E-Ink Metronome + controller. The pre-release name for this device was 8track + and could also have been called by some vendors as PVI-nnnn. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index 7cda4f80af5..afeed0611e3 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -44,16 +44,59 @@ #define DPY_W 832 #define DPY_H 622 +static int user_wfm_size; + /* frame differs from image. frame includes non-visible pixels */ struct epd_frame { int fw; /* frame width */ int fh; /* frame height */ + u16 config[4]; + int wfm_size; }; static struct epd_frame epd_frame_table[] = { { - .fw = 832, - .fh = 622 + .fw = 832, + .fh = 622, + .config = { + 15 /* sdlew */ + | 2 << 8 /* sdosz */ + | 0 << 11 /* sdor */ + | 0 << 12 /* sdces */ + | 0 << 15, /* sdcer */ + 42 /* gdspl */ + | 1 << 8 /* gdr1 */ + | 1 << 9 /* sdshr */ + | 0 << 15, /* gdspp */ + 18 /* gdspw */ + | 0 << 15, /* dispc */ + 599 /* vdlc */ + | 0 << 11 /* dsi */ + | 0 << 12, /* dsic */ + }, + .wfm_size = 47001, + }, + { + .fw = 1088, + .fh = 791, + .config = { + 0x0104, + 0x031f, + 0x0088, + 0x02ff, + }, + .wfm_size = 46770, + }, + { + .fw = 1200, + .fh = 842, + .config = { + 0x0101, + 0x030e, + 0x0012, + 0x0280, + }, + .wfm_size = 46770, }, }; @@ -125,7 +168,6 @@ static u16 calc_img_cksum(u16 *start, int length) } /* here we decode the incoming waveform file and populate metromem */ -#define EXP_WFORM_SIZE 47001 static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, struct metronomefb_par *par) { @@ -142,9 +184,12 @@ static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, u8 *metromem = par->metromem_wfm; struct device *dev = par->info->dev; - if (size != EXP_WFORM_SIZE) { + if (user_wfm_size) + epd_frame_table[par->dt].wfm_size = user_wfm_size; + + if (size != epd_frame_table[par->dt].wfm_size) { dev_err(dev, "Error: unexpected size %d != %d\n", size, - EXP_WFORM_SIZE); + epd_frame_table[par->dt].wfm_size); return -EINVAL; } @@ -267,15 +312,12 @@ static int metronome_display_cmd(struct metronomefb_par *par) u16 cs; u16 opcode; static u8 borderval; - u8 *ptr; /* setup display command we can't immediately set the opcode since the controller will try parse the command before we've set it all up so we just set cs here and set the opcode at the end */ - ptr = par->metromem; - if (par->metromem_cmd->opcode == 0xCC40) opcode = cs = 0xCC41; else @@ -328,44 +370,17 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) static int __devinit metronome_config_cmd(struct metronomefb_par *par) { - int i; - u16 cs; - /* setup config command we can't immediately set the opcode since the controller - will try parse the command before we've set it all up - so we just set cs here and set the opcode at the end */ - - cs = 0xCC10; - - /* set the 12 args ( 8 bytes ) for config. see spec for meanings */ - i = 0; - par->metromem_cmd->args[i] = 15 /* sdlew */ - | 2 << 8 /* sdosz */ - | 0 << 11 /* sdor */ - | 0 << 12 /* sdces */ - | 0 << 15; /* sdcer */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 42 /* gdspl */ - | 1 << 8 /* gdr1 */ - | 1 << 9 /* sdshr */ - | 0 << 15; /* gdspp */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 18 /* gdspw */ - | 0 << 15; /* dispc */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 599 /* vdlc */ - | 0 << 11 /* dsi */ - | 0 << 12; /* dsic */ - cs += par->metromem_cmd->args[i++]; + will try parse the command before we've set it all up */ + memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config, + sizeof(epd_frame_table[par->dt].config)); /* the rest are 0 */ - memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); + memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2); - par->metromem_cmd->csum = cs; + par->metromem_cmd->csum = 0xCC10; + par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4); par->metromem_cmd->opcode = 0xCC10; /* config cmd */ return par->board->met_wait_event(par); @@ -401,12 +416,9 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) { int res; - par->board->init_gpio_regs(par); - - par->board->init_lcdc_regs(par); - - /* now that lcd is setup, setup dma descriptor */ - par->board->post_dma_setup(par); + res = par->board->setup_io(par); + if (res) + return res; res = metronome_powerup_cmd(par); if (res) @@ -423,16 +435,16 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) static void metronomefb_dpy_update(struct metronomefb_par *par) { + int fbsize; u16 cksum; unsigned char *buf = (unsigned char __force *)par->info->screen_base; + fbsize = par->info->fix.smem_len; /* copy from vm to metromem */ - memcpy(par->metromem_img, buf, DPY_W*DPY_H); + memcpy(par->metromem_img, buf, fbsize); - cksum = calc_img_cksum((u16 *) par->metromem_img, - (epd_frame_table[0].fw * DPY_H)/2); - *((u16 *)(par->metromem_img) + - (epd_frame_table[0].fw * DPY_H)/2) = cksum; + cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2); + *((u16 *)(par->metromem_img) + fbsize/2) = cksum; metronome_display_cmd(par); } @@ -567,8 +579,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev) unsigned char *videomemory; struct metronomefb_par *par; const struct firmware *fw_entry; - int cmd_size, wfm_size, img_size, padding_size, totalsize; int i; + int panel_type; + int fw, fh; + int epd_dt_index; /* pick up board specific routines */ board = dev->dev.platform_data; @@ -579,76 +593,88 @@ static int __devinit metronomefb_probe(struct platform_device *dev) if (!try_module_get(board->owner)) return -ENODEV; + info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); + if (!info) + goto err; + /* we have two blocks of memory. info->screen_base which is vm, and is the fb used by apps. par->metromem which is physically contiguous memory and contains the display controller commands, waveform, processed image data and padding. this is the data pulled - by the device's LCD controller and pushed to Metronome */ + by the device's LCD controller and pushed to Metronome. + the metromem memory is allocated by the board driver and + is provided to us */ + + panel_type = board->get_panel_type(); + switch (panel_type) { + case 6: + epd_dt_index = 0; + break; + case 8: + epd_dt_index = 1; + break; + case 97: + epd_dt_index = 2; + break; + default: + dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n"); + epd_dt_index = 0; + break; + } - videomemorysize = (DPY_W*DPY_H); + fw = epd_frame_table[epd_dt_index].fw; + fh = epd_frame_table[epd_dt_index].fh; + + /* we need to add a spare page because our csum caching scheme walks + * to the end of the page */ + videomemorysize = PAGE_SIZE + (fw * fh); videomemory = vmalloc(videomemorysize); if (!videomemory) - return -ENOMEM; + goto err_fb_rel; memset(videomemory, 0, videomemorysize); - info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); - if (!info) - goto err_vfree; - info->screen_base = (char __force __iomem *)videomemory; info->fbops = &metronomefb_ops; + metronomefb_fix.line_length = fw; + metronomefb_var.xres = fw; + metronomefb_var.yres = fh; + metronomefb_var.xres_virtual = fw; + metronomefb_var.yres_virtual = fh; info->var = metronomefb_var; info->fix = metronomefb_fix; info->fix.smem_len = videomemorysize; par = info->par; par->info = info; par->board = board; + par->dt = epd_dt_index; init_waitqueue_head(&par->waitq); /* this table caches per page csum values. */ par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); if (!par->csum_table) + goto err_vfree; + + /* the physical framebuffer that we use is setup by + * the platform device driver. It will provide us + * with cmd, wfm and image memory in a contiguous area. */ + retval = board->setup_fb(par); + if (retval) { + dev_err(&dev->dev, "Failed to setup fb\n"); goto err_csum_table; + } - /* the metromem buffer is divided as follows: - command | CRC | padding - 16kb waveform data | CRC | padding - image data | CRC - and an extra 256 bytes for dma descriptors - eg: IW=832 IH=622 WS=128 - */ - - cmd_size = 1 * epd_frame_table[0].fw; - wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1) - / epd_frame_table[0].fw) * epd_frame_table[0].fw; - img_size = epd_frame_table[0].fh * epd_frame_table[0].fw; - padding_size = 4 * epd_frame_table[0].fw; - totalsize = cmd_size + wfm_size + img_size + padding_size; - par->metromemsize = PAGE_ALIGN(totalsize + 256); - DPRINTK("desired memory size = %d\n", par->metromemsize); - dev->dev.coherent_dma_mask = 0xffffffffull; - par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize, - &par->metromem_dma, GFP_KERNEL); - if (!par->metromem) { - printk(KERN_ERR - "metronomefb: unable to allocate dma buffer\n"); - goto err_vfree; + /* after this point we should have a framebuffer */ + if ((!par->metromem_wfm) || (!par->metromem_img) || + (!par->metromem_dma)) { + dev_err(&dev->dev, "fb access failure\n"); + retval = -EINVAL; + goto err_csum_table; } info->fix.smem_start = par->metromem_dma; - par->metromem_cmd = (struct metromem_cmd *) par->metromem; - par->metromem_wfm = par->metromem + cmd_size; - par->metromem_img = par->metromem + cmd_size + wfm_size; - par->metromem_img_csum = (u16 *) (par->metromem_img + - (epd_frame_table[0].fw * DPY_H)); - DPRINTK("img offset=0x%x\n", cmd_size + wfm_size); - par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size - + wfm_size + img_size + padding_size); - par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size - + img_size + padding_size; /* load the waveform in. assume mode 3, temp 31 for now a) request the waveform file from userspace @@ -656,7 +682,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); if (retval < 0) { dev_err(&dev->dev, "Failed to get waveform\n"); - goto err_dma_free; + goto err_csum_table; } retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31, @@ -664,11 +690,11 @@ static int __devinit metronomefb_probe(struct platform_device *dev) release_firmware(fw_entry); if (retval < 0) { dev_err(&dev->dev, "Failed processing waveform\n"); - goto err_dma_free; + goto err_csum_table; } if (board->setup_irq(info)) - goto err_dma_free; + goto err_csum_table; retval = metronome_init_regs(par); if (retval < 0) @@ -682,7 +708,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { dev_err(&dev->dev, "Failed to allocate colormap\n"); - goto err_fb_rel; + goto err_free_irq; } /* set cmap */ @@ -705,17 +731,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev) err_cmap: fb_dealloc_cmap(&info->cmap); -err_fb_rel: - framebuffer_release(info); err_free_irq: - board->free_irq(info); -err_dma_free: - dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem, - par->metromem_dma); + board->cleanup(par); err_csum_table: vfree(par->csum_table); err_vfree: vfree(videomemory); +err_fb_rel: + framebuffer_release(info); +err: module_put(board->owner); return retval; } @@ -726,15 +750,15 @@ static int __devexit metronomefb_remove(struct platform_device *dev) if (info) { struct metronomefb_par *par = info->par; + + unregister_framebuffer(info); fb_deferred_io_cleanup(info); - dma_free_writecombine(&dev->dev, par->metromemsize, - par->metromem, par->metromem_dma); fb_dealloc_cmap(&info->cmap); + par->board->cleanup(par); vfree(par->csum_table); - unregister_framebuffer(info); vfree((void __force *)info->screen_base); - par->board->free_irq(info); module_put(par->board->owner); + dev_dbg(&dev->dev, "calling release\n"); framebuffer_release(info); } return 0; @@ -759,6 +783,9 @@ static void __exit metronomefb_exit(void) platform_driver_unregister(&metronomefb_driver); } +module_param(user_wfm_size, uint, 0); +MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size"); + module_init(metronomefb_init); module_exit(metronomefb_exit); -- cgit v1.2.3 From 922613436ae562a2903698f3a6e16998382a549d Mon Sep 17 00:00:00 2001 From: Jaya Kumar Date: Sun, 17 Aug 2008 05:59:32 +0100 Subject: [ARM] 5200/1: am200epd: use fb notifiers and gpio api The original am200epd driver was designed with bad assumptions. It manipulated GPSR/GPLR registers directly. It relied on direct access to the pxa LCDC registers which have since conflicted with commit ce4fb7b892a6d6c6a0f87366b26fd834d2923dd7 . This patch moves it into mach-pxa and overhauls it to use a fb obtained through fb notifiers. It now uses the generic GPIO api. Signed-off-by: Jaya Kumar Acked-by: Krzysztof Helt Acked-by: Eric Miao Signed-off-by: Russell King --- drivers/video/Kconfig | 13 --- drivers/video/Makefile | 1 - drivers/video/am200epd.c | 295 ----------------------------------------------- 3 files changed, 309 deletions(-) delete mode 100644 drivers/video/am200epd.c (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6fd4a2f63cf..d85a74c64b5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1969,19 +1969,6 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. -config FB_AM200EPD - tristate "AM-200 E-Ink EPD devkit support" - depends on FB && ARCH_PXA && MMU - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO - select FB_METRONOME - help - This enables support for the Metronome display controller used on - the E-Ink AM-200 EPD devkit. - config FB_COBALT tristate "Cobalt server LCD frame buffer support" depends on FB && MIPS_COBALT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a6b55297a7f..ad0330bf9be 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o # Hardware specific drivers go first obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o -obj-$(CONFIG_FB_AM200EPD) += am200epd.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c deleted file mode 100644 index 0c35b8b0160..00000000000 --- a/drivers/video/am200epd.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * linux/drivers/video/am200epd.c -- Platform device for AM200 EPD kit - * - * Copyright (C) 2008, Jaya Kumar - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * - * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. - * - * This work was made possible by help and equipment support from E-Ink - * Corporation. http://support.eink.com/community - * - * This driver is written to be used with the Metronome display controller. - * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600 - * Vizplex EPD on a Gumstix board using the Lyre interface board. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include