diff --git a/MAINTAINERS b/MAINTAINERS index cc12f843ba7e3b48e0a8d8f788ef0a0afd882e5f..4a8e6df873034d13d5c84c83d6657ab183e7221e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1132,7 +1132,6 @@ NETWORKING [GENERAL] P: Networking Team M: netdev@oss.sgi.com L: linux-net@vger.kernel.org -W: http://www.uk.linux.org/NetNews.html (2.0 only) S: Maintained NETWORKING [IPv4/IPv6] diff --git a/Makefile b/Makefile index 18acdc4d73e30daaf1774c2434b5ddf9ecc06e04..8d0e49ea731f4472dfd0992f3c749e84543e2b3c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 5 -SUBLEVEL = 38 +SUBLEVEL = 39 EXTRAVERSION = # *DOCUMENTATION* @@ -173,6 +173,9 @@ noconfig_targets := xconfig menuconfig config oldconfig randconfig \ help tags TAGS sgmldocs psdocs pdfdocs htmldocs \ checkconfig checkhelp checkincludes +RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \) -prune -o +RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS + # Helpers built in scripts/ # --------------------------------------------------------------------------- @@ -581,13 +584,13 @@ spec: # will become invalid rpm: clean spec - find . -name SCCS -prune -o -name BitKeeper -prune -o \ + find . $(RCS_FIND_IGNORE) \ \( -size 0 -o -name .depend -o -name .hdepend \) \ -type f -print | xargs rm -f set -e; \ cd $(TOPDIR)/.. ; \ ln -sf $(TOPDIR) $(KERNELPATH) ; \ - tar -cvz --exclude CVS -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ; \ + tar -cvz $(RCS_TAR_IGNORE) -f $(KERNELPATH).tar.gz $(KERNELPATH)/. ; \ rm $(KERNELPATH) ; \ cd $(TOPDIR) ; \ . scripts/mkversion > .version ; \ @@ -717,7 +720,7 @@ include arch/$(ARCH)/Makefile clean: archclean @echo 'Cleaning up' - @find . -name SCCS -prune -o -name BitKeeper -prune -o \ + @find . $(RCS_FIND_IGNORE) \ \( -name \*.[oas] -o -name core -o -name .\*.cmd -o \ -name .\*.tmp -o -name .\*.d \) -type f -print \ | grep -v lxdialog/ | xargs rm -f @@ -726,7 +729,7 @@ clean: archclean mrproper: clean archmrproper @echo 'Making mrproper' - @find . -name SCCS -prune -o -name BitKeeper -prune -o \ + @find . $(RCS_FIND_IGNORE) \ \( -name .depend -o -name .\*.cmd \) \ -type f -print | xargs rm -f @rm -f $(MRPROPER_FILES) @@ -736,7 +739,7 @@ mrproper: clean archmrproper distclean: mrproper @echo 'Making distclean' - @find . -name SCCS -prune -o -name BitKeeper -prune -o \ + @find . $(RCS_FIND_IGNORE) \ \( -not -type d \) -and \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ @@ -747,18 +750,18 @@ distclean: mrproper # --------------------------------------------------------------------------- define all-sources - ( find . \( -name SCCS -o -name BitKeeper -o -name include -o \ - -name arch \) -prune \ - -o -name '*.[chS]' -print; \ - find arch/$(ARCH) \( -name SCCS -o -name BitKeeper \) -prune \ - -o -name '*.[chS]' -print; \ - find include \( -name SCCS -o -name BitKeeper -o -name config -o \ - -name 'asm-*' \) -prune \ - -o -name '*.[chS]' -print; \ - find include/asm-$(ARCH) \( -name SCCS -o -name BitKeeper \) -prune \ + ( find . $(RCS_FIND_IGNORE) \ + \( -name include -o -name arch \) -prune -o \ + -name '*.[chS]' -print; \ + find arch/$(ARCH) $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + find include $(RCS_FIND_IGNORE) \ + \( -name config -o -name 'asm-*' \) -prune -o \ -o -name '*.[chS]' -print; \ - find include/asm-generic \( -name SCCS -o -name BitKeeper \) -prune \ - -o -name '*.[chS]' -print ) + find include/asm-$(ARCH) $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print; \ + find include/asm-generic $(RCS_FIND_IGNORE) \ + -name '*.[chS]' -print ) endef quiet_cmd_TAGS = MAKE $@ @@ -825,17 +828,17 @@ sgmldocs psdocs pdfdocs htmldocs: scripts # --------------------------------------------------------------------------- checkconfig: - find * -name SCCS -prune -o -name BitKeeper -prune -o \ + find * $(RCS_FIND_IGNORE) \ -name '*.[hcS]' -type f -print | sort \ | xargs $(PERL) -w scripts/checkconfig.pl checkhelp: - find * -name SCCS -prune -o -name BitKeeper -prune -o \ + find * $(RCS_FIND_IGNORE) \ -name [cC]onfig.in -print | sort \ | xargs $(PERL) -w scripts/checkhelp.pl checkincludes: - find * -name SCCS -prune -o -name BitKeeper -prune -o \ + find * $(RCS_FIND_IGNORE) \ -name '*.[hcS]' -type f -print | sort \ | xargs $(PERL) -w scripts/checkincludes.pl diff --git a/drivers/acpi/Config.in b/drivers/acpi/Config.in index 4be660c2b8d348f8e02365a08b287353166a30bb..5dbeb53a7b239e6b8569cfa5d37b348ba2030c25 100644 --- a/drivers/acpi/Config.in +++ b/drivers/acpi/Config.in @@ -34,6 +34,7 @@ if [ "$CONFIG_X86" = "y" ]; then define_bool CONFIG_ACPI_EC y define_bool CONFIG_ACPI_POWER y define_bool CONFIG_ACPI_PCI $CONFIG_PCI + define_bool CONFIG_ACPI_SLEEP $CONFIG_SOFTWARE_SUSPEND define_bool CONFIG_ACPI_SYSTEM y fi fi diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index dd7f58a86716d867b6026488edcd7e01cffed4f7..14819f3f51c7ab333c62d5d823b98e4a8d4c804b 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -55,7 +55,8 @@ static char *acpi_table_signatures[ACPI_TABLE_COUNT] = { [ACPI_SPCR] = "SPCR", [ACPI_SRAT] = "SRAT", [ACPI_SSDT] = "SSDT", - [ACPI_SPMI] = "SPMI" + [ACPI_SPMI] = "SPMI", + [ACPI_HPET] = "HPET" }; /* System Description Table (RSDT/XSDT) */ @@ -320,7 +321,7 @@ acpi_table_parse_madt_family ( handler(entry); } entry = (acpi_table_entry_header *) - ((unsigned long) entry += entry->length); + ((unsigned long) entry + entry->length); } return count; diff --git a/drivers/base/Makefile b/drivers/base/Makefile index f609ddee181ea4725ce69983476f0f15a5f80114..a3e459cc1f6e75b7e306400f477c3066ab082fe4 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -6,6 +6,8 @@ obj-y := core.o sys.o interface.o power.o bus.o \ obj-y += fs/ +obj-$(CONFIG_HOTPLUG) += hotplug.o + export-objs := core.o power.o sys.o bus.o driver.o \ class.o intf.o platform.o cpu.o diff --git a/drivers/base/base.h b/drivers/base/base.h index b6a3f3ec145ca8966f238964ccd68b0a3e47a40d..0c05ae058971cc4ab0b275610091d7279a4b62ef 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -50,3 +50,13 @@ extern void interface_remove(struct device_class *, struct device *); extern int driver_attach(struct device_driver * drv); extern void driver_detach(struct device_driver * drv); + +#ifdef CONFIG_HOTPLUG +extern int dev_hotplug(struct device *dev, const char *action); +#else +static inline int dev_hotplug(struct device *dev, const char *action) +{ + return 0; +} +#endif + diff --git a/drivers/base/core.c b/drivers/base/core.c index 1d0cb0d0b0362c074635f4f16429dd7f71794b66..9b958256c1bbf3d6e6c2ffdb3021c4be03ed67d0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -200,6 +200,9 @@ int device_register(struct device *dev) if (platform_notify) platform_notify(dev); + /* notify userspace of device entry */ + dev_hotplug(dev, "add"); + register_done: if (error) { spin_lock(&device_lock); @@ -255,6 +258,9 @@ void put_device(struct device * dev) if (platform_notify_remove) platform_notify_remove(dev); + /* notify userspace that this device is about to disappear */ + dev_hotplug (dev, "remove"); + device_detach(dev); bus_remove_device(dev); diff --git a/drivers/base/hotplug.c b/drivers/base/hotplug.c new file mode 100644 index 0000000000000000000000000000000000000000..231d42cb7a18f5044d9a3b9f46e1c6a6919e6fe5 --- /dev/null +++ b/drivers/base/hotplug.c @@ -0,0 +1,103 @@ +/* + * drivers/base/hotplug.c - hotplug call code + * + * Copyright (c) 2000-2001 David Brownell + * Copyright (c) 2002 Greg Kroah-Hartman + * Copyright (c) 2002 IBM Corp. + * + * Based off of drivers/usb/core/usb.c:call_agent(), which was + * written by David Brownell. + * + */ + +#define DEBUG 0 + +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/kmod.h> +#include <linux/interrupt.h> +#include "base.h" + +/* + * hotplugging invokes what /proc/sys/kernel/hotplug says (normally + * /sbin/hotplug) when devices get added or removed. + * + * This invokes a user mode policy agent, typically helping to load driver + * or other modules, configure the device, and more. Drivers can provide + * a MODULE_DEVICE_TABLE to help with module loading subtasks. + */ +#define BUFFER_SIZE 1024 /* should be enough memory for the env */ +#define NUM_ENVP 32 /* number of env pointers */ +int dev_hotplug (struct device *dev, const char *action) +{ + char *argv [3], **envp, *buffer, *scratch; + int retval; + int i = 0; + + pr_debug ("%s\n", __FUNCTION__); + if (!dev) + return -ENODEV; + + if (!dev->bus || !dev->bus->hotplug) + return -ENODEV; + + if (!hotplug_path [0]) + return -ENODEV; + + if (in_interrupt ()) { + pr_debug ("%s - in_interrupt, not allowed!", __FUNCTION__); + return -EIO; + } + + if (!current->fs->root) { + /* don't try to do anything unless we have a root partition */ + pr_debug ("%s - %s -- no FS yet\n", __FUNCTION__, action); + return -EIO; + } + + envp = (char **) kmalloc (NUM_ENVP * sizeof (char *), GFP_KERNEL); + if (!envp) + return -ENOMEM; + + buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); + if (!buffer) { + kfree (envp); + return -ENOMEM; + } + + /* only one standardized param to hotplug command: the bus name */ + argv [0] = hotplug_path; + argv [1] = dev->bus->name; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + scratch = buffer; + + /* action: add, remove */ + envp [i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", action) + 1; + + /* have the bus specific function set up the rest of the environment */ + retval = dev->bus->hotplug (dev, &envp[i], NUM_ENVP - i, + scratch, BUFFER_SIZE - (scratch - buffer)); + if (retval) { + pr_debug ("%s - hotplug() returned %d\n", __FUNCTION__, retval); + goto exit; + } + + pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv [0], argv[1], + action, envp[0], envp[1], envp[2]); + retval = call_usermodehelper (argv [0], argv, envp); + if (retval) + pr_debug ("%s - call_usermodehelper returned %d\n", + __FUNCTION__, retval); + +exit: + kfree (buffer); + kfree (envp); + return retval; +} diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index 462dcb346c354f02c94ab1866df3f0f02bbf106d..6ec9b3bdafb15603f0f400f71ab1cce4cdad74ca 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -137,8 +137,24 @@ deadline_find_hash(struct deadline_data *dd, sector_t offset) return rq; } +static sector_t deadline_get_last_sector(struct deadline_data *dd) +{ + sector_t last_sec = dd->last_sector; + + /* + * if dispatch is non-empty, disregard last_sector and check last one + */ + if (!list_empty(dd->dispatch)) { + struct request *__rq = list_entry_rq(dd->dispatch->prev); + + last_sec = __rq->sector + __rq->nr_sectors; + } + + return last_sec; +} + static int -deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) +deadline_merge(request_queue_t *q, struct list_head **insert, struct bio *bio) { struct deadline_data *dd = q->elevator.elevator_data; const int data_dir = bio_data_dir(bio); @@ -150,9 +166,11 @@ deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) /* * try last_merge to avoid going to hash */ - ret = elv_try_last_merge(q, req, bio); - if (ret != ELEVATOR_NO_MERGE) + ret = elv_try_last_merge(q, bio); + if (ret != ELEVATOR_NO_MERGE) { + *insert = q->last_merge; goto out; + } /* * see if the merge hash can satisfy a back merge @@ -161,12 +179,15 @@ deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector); if (elv_rq_merge_ok(__rq, bio)) { - *req = __rq; + *insert = &__rq->queuelist; ret = ELEVATOR_BACK_MERGE; goto out; } } + /* + * scan list from back to find insertion point. + */ entry = sort_list = &dd->sort_list[data_dir]; while ((entry = entry->prev) != sort_list) { __rq = list_entry_rq(entry); @@ -177,8 +198,8 @@ deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) if (!(__rq->flags & REQ_CMD)) continue; - if (!*req && bio_rq_in_between(bio, __rq, sort_list)) - *req = __rq; + if (!*insert && bio_rq_in_between(bio, __rq, sort_list)) + *insert = &__rq->queuelist; if (__rq->flags & REQ_BARRIER) break; @@ -189,12 +210,23 @@ deadline_merge(request_queue_t *q, struct request **req, struct bio *bio) if (__rq->sector - bio_sectors(bio) == bio->bi_sector) { ret = elv_try_merge(__rq, bio); if (ret != ELEVATOR_NO_MERGE) { - *req = __rq; + *insert = &__rq->queuelist; break; } } } + /* + * no insertion point found, check the very front + */ + if (!*insert && !list_empty(sort_list)) { + __rq = list_entry_rq(sort_list->next); + + if (bio->bi_sector + bio_sectors(bio) < __rq->sector && + bio->bi_sector > deadline_get_last_sector(dd)) + *insert = sort_list; + } + out: return ret; } @@ -254,18 +286,9 @@ deadline_move_to_dispatch(struct deadline_data *dd, struct request *rq) static void deadline_move_requests(struct deadline_data *dd, struct request *rq) { struct list_head *sort_head = &dd->sort_list[rq_data_dir(rq)]; - sector_t last_sec = dd->last_sector; + sector_t last_sec = deadline_get_last_sector(dd); int batch_count = dd->fifo_batch; - /* - * if dispatch is non-empty, disregard last_sector and check last one - */ - if (!list_empty(dd->dispatch)) { - struct request *__rq = list_entry_rq(dd->dispatch->prev); - - last_sec = __rq->sector + __rq->nr_sectors; - } - do { struct list_head *nxt = rq->queuelist.next; int this_rq_cost; diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 8d0349e46b9b523d25547044e09d6c82e556be64..9237eec87ba417ef0f243cfa85f0af97ae9d51ab 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -136,8 +136,7 @@ inline int elv_try_merge(struct request *__rq, struct bio *bio) return ret; } -inline int elv_try_last_merge(request_queue_t *q, struct request **req, - struct bio *bio) +inline int elv_try_last_merge(request_queue_t *q, struct bio *bio) { int ret = ELEVATOR_NO_MERGE; @@ -150,8 +149,8 @@ inline int elv_try_last_merge(request_queue_t *q, struct request **req, if (!rq_mergeable(__rq)) q->last_merge = NULL; - else if ((ret = elv_try_merge(__rq, bio))) - *req = __rq; + else + ret = elv_try_merge(__rq, bio); } return ret; @@ -162,15 +161,17 @@ inline int elv_try_last_merge(request_queue_t *q, struct request **req, * * See if we can find a request that this buffer can be coalesced with. */ -int elevator_noop_merge(request_queue_t *q, struct request **req, +int elevator_noop_merge(request_queue_t *q, struct list_head **insert, struct bio *bio) { struct list_head *entry = &q->queue_head; struct request *__rq; int ret; - if ((ret = elv_try_last_merge(q, req, bio))) + if ((ret = elv_try_last_merge(q, bio))) { + *insert = q->last_merge; return ret; + } while ((entry = entry->prev) != &q->queue_head) { __rq = list_entry_rq(entry); @@ -182,7 +183,7 @@ int elevator_noop_merge(request_queue_t *q, struct request **req, continue; if ((ret = elv_try_merge(__rq, bio))) { - *req = __rq; + *insert = &__rq->queuelist; q->last_merge = &__rq->queuelist; return ret; } @@ -240,12 +241,12 @@ int elevator_global_init(void) return 0; } -int elv_merge(request_queue_t *q, struct request **rq, struct bio *bio) +int elv_merge(request_queue_t *q, struct list_head **entry, struct bio *bio) { elevator_t *e = &q->elevator; if (e->elevator_merge_fn) - return e->elevator_merge_fn(q, rq, bio); + return e->elevator_merge_fn(q, entry, bio); return ELEVATOR_NO_MERGE; } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 510c767c436416ce93b566b5c6ecaf55a7342119..13f769d27a5dfcd82591ea3ce2be4793fac984be 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1583,7 +1583,6 @@ static int __make_request(request_queue_t *q, struct bio *bio) spin_lock_irq(q->queue_lock); again: - req = NULL; insert_here = NULL; if (blk_queue_empty(q)) { @@ -1593,10 +1592,13 @@ static int __make_request(request_queue_t *q, struct bio *bio) if (barrier) goto get_rq; - el_ret = elv_merge(q, &req, bio); + el_ret = elv_merge(q, &insert_here, bio); switch (el_ret) { case ELEVATOR_BACK_MERGE: + req = list_entry_rq(insert_here); + BUG_ON(!rq_mergeable(req)); + if (!q->back_merge_fn(q, req, bio)) { insert_here = &req->queuelist; break; @@ -1611,7 +1613,10 @@ static int __make_request(request_queue_t *q, struct bio *bio) goto out; case ELEVATOR_FRONT_MERGE: + req = list_entry_rq(insert_here); + BUG_ON(!rq_mergeable(req)); + if (!q->front_merge_fn(q, req, bio)) { insert_here = req->queuelist.prev; break; @@ -1638,13 +1643,6 @@ static int __make_request(request_queue_t *q, struct bio *bio) * elevator says don't/can't merge. get new request */ case ELEVATOR_NO_MERGE: - /* - * use elevator hints as to where to insert the - * request. if no hints, just add it to the back - * of the queue - */ - if (req) - insert_here = &req->queuelist; break; default: diff --git a/drivers/char/agp/Config.in b/drivers/char/agp/Config.in index 91c2fbdf5e4ae12d3e015ebcc580e24568c46d6c..a6c13cb4ae4c048017fc1b6f62ced87b69ff75cd 100644 --- a/drivers/char/agp/Config.in +++ b/drivers/char/agp/Config.in @@ -1,4 +1,9 @@ -dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP + +if [ "$CONFIG_GART_IOMMU" = "y" ]; then + dep_bool '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP +else + dep_tristate '/dev/agpgart (AGP Support)' CONFIG_AGP $CONFIG_DRM_AGP +fi if [ "$CONFIG_AGP" != "n" ]; then bool ' Intel 440LX/BX/GX and I815/I820/I830M/I830MP/I840/I845/I850/I860 support' CONFIG_AGP_INTEL bool ' Intel I810/I815/I830M (on-board) support' CONFIG_AGP_I810 @@ -7,6 +12,9 @@ if [ "$CONFIG_AGP" != "n" ]; then bool ' Generic SiS support' CONFIG_AGP_SIS bool ' ALI chipset support' CONFIG_AGP_ALI bool ' Serverworks LE/HE support' CONFIG_AGP_SWORKS + if [ "$CONFIG_GART_IOMMU" != "y" ]; then + bool ' AMD 8151 support' CONFIG_AGP_AMD_8151 + fi if [ "$CONFIG_IA64" = "y" ]; then bool ' Intel 460GX support' CONFIG_AGP_I460 bool ' HP ZX1 AGP support' CONFIG_AGP_HP_ZX1 diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index 940b293804a8f4e5ffafcdb13e364e0ff230d550..f0e0fce556efe98f151fd5f87751a8f51b99f249 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -16,6 +16,7 @@ agpgart-$(CONFIG_AGP_ALI) += ali-agp.o agpgart-$(CONFIG_AGP_SWORKS) += sworks-agp.o agpgart-$(CONFIG_AGP_I460) += i460-agp.o agpgart-$(CONFIG_AGP_HP_ZX1) += hp-agp.o +agpgart-$(CONFIG_AGP_AMD_8151) += k8-agp.o agpgart-objs := $(agpgart-y) obj-$(CONFIG_AGP) += agpgart.o diff --git a/drivers/char/agp/agp.c b/drivers/char/agp/agp.c index 97948f5b329dce5b92b08fb49726592934347a52..94954bd450b6bbb19fbe8eac5bd88c3a83f09fcc 100644 --- a/drivers/char/agp/agp.c +++ b/drivers/char/agp/agp.c @@ -50,6 +50,9 @@ EXPORT_SYMBOL(agp_backend_release); struct agp_bridge_data agp_bridge = { type: NOT_SUPPORTED }; static int agp_try_unsupported __initdata = 0; +int agp_memory_reserved; +__u32 *agp_gatt_table; + int agp_backend_acquire(void) { if (agp_bridge.type == NOT_SUPPORTED) @@ -243,7 +246,7 @@ static int agp_return_size(void) /* Routine to copy over information structure */ -void agp_copy_info(agp_kern_info * info) +int agp_copy_info(agp_kern_info * info) { unsigned long page_mask = 0; int i; @@ -251,7 +254,7 @@ void agp_copy_info(agp_kern_info * info) memset(info, 0, sizeof(agp_kern_info)); if (agp_bridge.type == NOT_SUPPORTED) { info->chipset = agp_bridge.type; - return; + return -EIO; } info->version.major = agp_bridge.version->major; info->version.minor = agp_bridge.version->minor; @@ -268,6 +271,7 @@ void agp_copy_info(agp_kern_info * info) page_mask |= agp_bridge.mask_memory(page_mask, i); info->page_mask = ~page_mask; + return 0; } /* End - Routine to copy over information structure */ @@ -518,6 +522,7 @@ int agp_generic_create_gatt_table(void) SetPageReserved(page); agp_bridge.gatt_table_real = (unsigned long *) table; + agp_gatt_table = (void *)table; CACHE_FLUSH(); agp_bridge.gatt_table = ioremap_nocache(virt_to_phys(table), (PAGE_SIZE * (1 << page_order))); @@ -625,6 +630,9 @@ int agp_generic_insert_memory(agp_memory * mem, off_t pg_start, int type) break; } + num_entries -= agp_memory_reserved/PAGE_SIZE; + if (num_entries < 0) num_entries = 0; + if (type != 0 || mem->type != 0) { /* The generic routines know nothing of memory types */ return -EINVAL; @@ -824,6 +832,17 @@ static struct { }, #endif /* CONFIG_AGP_ALI */ +#ifdef CONFIG_AGP_AMD_8151 + { + .device_id = PCI_DEVICE_ID_AMD_8151_0, + .vendor_id = PCI_VENDOR_ID_AMD, + .chipset = AMD_8151, + .vendor_name = "AMD", + .chipset_name = "8151", + .chipset_setup = amd_8151_setup + }, +#endif /* CONFIG_AGP_AMD */ + #ifdef CONFIG_AGP_AMD { .device_id = PCI_DEVICE_ID_AMD_FE_GATE_7006, @@ -858,7 +877,6 @@ static struct { .chipset_setup = amd_irongate_setup, }, #endif /* CONFIG_AGP_AMD */ - #ifdef CONFIG_AGP_INTEL { .device_id = PCI_DEVICE_ID_INTEL_82443LX_0, @@ -1632,7 +1650,7 @@ static struct pci_driver agp_pci_driver = { .probe = agp_probe, }; -static int __init agp_init(void) +int __init agp_init(void) { int ret_val; @@ -1658,5 +1676,7 @@ static void __exit agp_cleanup(void) } } +#ifndef CONFIG_GART_IOMMU module_init(agp_init); module_exit(agp_cleanup); +#endif diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 5bc98712b2d3a31f5b9fc582490fcb5b43d9e222..f5f8c37ed67973c3b9595839144073365dc98208 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -49,6 +49,7 @@ void agp_free_key(int key); /* chipset specific init routines. */ int __init ali_generic_setup (struct pci_dev *pdev); int __init amd_irongate_setup (struct pci_dev *pdev); +int __init amd_8151_setup (struct pci_dev *pdev); int __init hp_zx1_setup (struct pci_dev *pdev); int __init intel_i460_setup (struct pci_dev *pdev); int __init intel_generic_setup (struct pci_dev *pdev); @@ -319,6 +320,22 @@ struct agp_bridge_data { #define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */ #define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */ +#define AMD_8151_APSIZE 0xb4 +#define AMD_8151_GARTBLOCK 0xb8 + +#define AMD_X86_64_GARTAPERTURECTL 0x90 +#define AMD_X86_64_GARTAPERTUREBASE 0x94 +#define AMD_X86_64_GARTTABLEBASE 0x98 +#define AMD_X86_64_GARTCACHECTL 0x9c +#define AMD_X86_64_GARTEN 1<<0 + +#define AMD_8151_VMAPERTURE 0x10 +#define AMD_8151_AGP_CTL 0xb0 +#define AMD_8151_APERTURESIZE 0xb4 +#define AMD_8151_GARTPTR 0xb8 +#define AMD_8151_GTLBEN 1<<7 +#define AMD_8151_APEREN 1<<8 + /* ALi registers */ #define ALI_APBASE 0x10 #define ALI_AGPCTRL 0xb8 diff --git a/drivers/char/agp/k8-agp.c b/drivers/char/agp/k8-agp.c new file mode 100644 index 0000000000000000000000000000000000000000..931211adf94849073f0aa9492da142c3b96042fd --- /dev/null +++ b/drivers/char/agp/k8-agp.c @@ -0,0 +1,476 @@ +/* + * Copyright 2001,2002 SuSE Labs + * Distributed under the GNU public license, v2. + * + * This is a GART driver for the AMD K8 northbridge and the AMD 8151 + * AGP bridge. The main work is done in the northbridge. The configuration + * is only mirrored in the 8151 for compatibility (could be likely + * removed now). + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/agp_backend.h> +#include "agp.h" + +extern int agp_memory_reserved; +extern __u32 *agp_gatt_table; + +static u_int64_t pci_read64 (struct pci_dev *dev, int reg) +{ + union { + u64 full; + struct { + u32 high; + u32 low; + } split; + } tmp; + pci_read_config_dword(dev, reg, &tmp.split.high); + pci_read_config_dword(dev, reg+4, &tmp.split.low); + return tmp.full; +} + +static void pci_write64 (struct pci_dev *dev, int reg, u64 value) +{ + union { + u64 full; + struct { + u32 high; + u32 low; + } split; + } tmp; + tmp.full = value; + pci_write_config_dword(dev, reg, tmp.split.high); + pci_write_config_dword(dev, reg+4, tmp.split.low); +} + + +static int x86_64_insert_memory(agp_memory * mem, off_t pg_start, int type) +{ + int i, j, num_entries; + void *temp; + long tmp; + u32 pte; + u64 addr; + + temp = agp_bridge.current_size; + + num_entries = A_SIZE_32(temp)->num_entries; + + num_entries -= agp_memory_reserved>>PAGE_SHIFT; + + if (type != 0 || mem->type != 0) + return -EINVAL; + + /* Make sure we can fit the range in the gatt table. */ + if ((pg_start + mem->page_count) > num_entries) + return -EINVAL; + + j = pg_start; + + /* gatt table should be empty. */ + while (j < (pg_start + mem->page_count)) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) + return -EBUSY; + j++; + } + + if (mem->is_flushed == FALSE) { + CACHE_FLUSH(); + mem->is_flushed = TRUE; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + addr = mem->memory[i]; + + tmp = addr; + BUG_ON(tmp & 0xffffff0000000ffc); + pte = (tmp & 0x000000ff00000000) >> 28; + pte |=(tmp & 0x00000000fffff000); + pte |= 1<<1|1<<0; + + agp_bridge.gatt_table[j] = pte; + } + agp_bridge.tlb_flush(mem); + return 0; +} + +/* + * This hack alters the order element according + * to the size of a long. It sucks. I totally disown this, even + * though it does appear to work for the most part. + */ +static struct aper_size_info_32 x86_64_aperture_sizes[7] = +{ + {32, 8192, 3+(sizeof(long)/8), 0 }, + {64, 16384, 4+(sizeof(long)/8), 1<<1 }, + {128, 32768, 5+(sizeof(long)/8), 1<<2 }, + {256, 65536, 6+(sizeof(long)/8), 1<<1 | 1<<2 }, + {512, 131072, 7+(sizeof(long)/8), 1<<3 }, + {1024, 262144, 8+(sizeof(long)/8), 1<<1 | 1<<3}, + {2048, 524288, 9+(sizeof(long)/8), 1<<2 | 1<<3} +}; + + +/* + * Get the current Aperture size from the x86-64. + * Note, that there may be multiple x86-64's, but we just return + * the value from the first one we find. The set_size functions + * keep the rest coherent anyway. Or at least should do. + */ +static int amd_x86_64_fetch_size(void) +{ + struct pci_dev *dev; + int i; + u32 temp; + struct aper_size_info_32 *values; + + pci_for_each_dev(dev) { + if (dev->bus->number==0 && + PCI_FUNC(dev->devfn)==3 && + PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) { + + pci_read_config_dword(dev, AMD_X86_64_GARTAPERTURECTL, &temp); + temp = (temp & 0xe); + values = A_SIZE_32(x86_64_aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + } + } + /* erk, couldn't find an x86-64 ? */ + return 0; +} + + +static void inline flush_x86_64_tlb(struct pci_dev *dev) +{ + u32 tmp; + + pci_read_config_dword (dev, AMD_X86_64_GARTCACHECTL, &tmp); + tmp |= 1<<0; + pci_write_config_dword (dev, AMD_X86_64_GARTCACHECTL, tmp); +} + + +void amd_x86_64_tlbflush(agp_memory * temp) +{ + struct pci_dev *dev; + + pci_for_each_dev(dev) { + if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && + PCI_SLOT(dev->devfn) >=24 && PCI_SLOT(dev->devfn) <=31) { + flush_x86_64_tlb (dev); + } + } +} + + +/* + * In a multiprocessor x86-64 system, this function gets + * called once for each CPU. + */ +u64 amd_x86_64_configure (struct pci_dev *hammer, u64 gatt_table) +{ + u64 aperturebase; + u32 tmp; + u64 addr, aper_base; + + /* Address to map to */ + pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &tmp); + aperturebase = tmp << 25; + aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK); + + /* address of the mappings table */ + addr = (u64) gatt_table; + addr >>= 12; + tmp = (u32) addr<<4; + tmp &= ~0xf; + pci_write_config_dword (hammer, AMD_X86_64_GARTTABLEBASE, tmp); + + /* Enable GART translation for this hammer. */ + pci_read_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, &tmp); + tmp &= 0x3f; + tmp |= 1<<0; + pci_write_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, tmp); + + /* keep CPU's coherent. */ + flush_x86_64_tlb (hammer); + + return aper_base; +} + + +static struct aper_size_info_32 amd_8151_sizes[7] = +{ + {2048, 524288, 9, 0x00000000 }, /* 0 0 0 0 0 0 */ + {1024, 262144, 8, 0x00000400 }, /* 1 0 0 0 0 0 */ + {512, 131072, 7, 0x00000600 }, /* 1 1 0 0 0 0 */ + {256, 65536, 6, 0x00000700 }, /* 1 1 1 0 0 0 */ + {128, 32768, 5, 0x00000720 }, /* 1 1 1 1 0 0 */ + {64, 16384, 4, 0x00000730 }, /* 1 1 1 1 1 0 */ + {32, 8192, 3, 0x00000738 } /* 1 1 1 1 1 1 */ +}; + +static int amd_8151_configure(void) +{ + struct pci_dev *dev, *hammer=NULL; + int current_size; + int tmp, tmp2, i; + u64 aperbar; + unsigned long gatt_bus = virt_to_phys(agp_bridge.gatt_table_real); + + /* Configure AGP regs in each x86-64 host bridge. */ + pci_for_each_dev(dev) { + if (dev->bus->number==0 && + PCI_FUNC(dev->devfn)==3 && + PCI_SLOT(dev->devfn)>=24 && PCI_SLOT(dev->devfn)<=31) { + agp_bridge.gart_bus_addr = amd_x86_64_configure(dev,gatt_bus); + hammer = dev; + + /* + * TODO: Cache pci_dev's of x86-64's in private struct to save us + * having to scan the pci list each time. + */ + } + } + + if (hammer == NULL) { + return -ENODEV; + } + + /* Shadow x86-64 registers into 8151 registers. */ + + dev = agp_bridge.dev; + if (!dev) + return -ENODEV; + + current_size = amd_x86_64_fetch_size(); + + pci_read_config_dword(dev, AMD_8151_APERTURESIZE, &tmp); + tmp &= ~(0xfff); + + /* translate x86-64 size bits to 8151 size bits*/ + for (i=0 ; i<7; i++) { + if (amd_8151_sizes[i].size == current_size) + tmp |= (amd_8151_sizes[i].size_value) << 3; + } + pci_write_config_dword(dev, AMD_8151_APERTURESIZE, tmp); + + pci_read_config_dword (hammer, AMD_X86_64_GARTAPERTUREBASE, &tmp); + aperbar = pci_read64 (dev, AMD_8151_VMAPERTURE); + aperbar |= (tmp & 0x7fff) <<25; + aperbar &= 0x000000ffffffffff; + aperbar |= 1<<2; /* This address is a 64bit ptr FIXME: Make conditional in 32bit mode */ + pci_write64 (dev, AMD_8151_VMAPERTURE, aperbar); + + pci_read_config_dword(dev, AMD_8151_AGP_CTL , &tmp); + tmp &= ~(AMD_8151_GTLBEN | AMD_8151_APEREN); + + pci_read_config_dword(hammer, AMD_X86_64_GARTAPERTURECTL, &tmp2); + if (tmp2 & AMD_X86_64_GARTEN) + tmp |= AMD_8151_APEREN; + // FIXME: bit 7 of AMD_8151_AGP_CTL (GTLBEN) must be copied if set. + // But where is it set ? + pci_write_config_dword(dev, AMD_8151_AGP_CTL, tmp); + + return 0; +} + + +static void amd_8151_cleanup(void) +{ + struct pci_dev *dev; + u32 tmp; + + pci_for_each_dev(dev) { + /* disable gart translation */ + if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && + (PCI_SLOT(dev->devfn) >=24) && (PCI_SLOT(dev->devfn) <=31)) { + + pci_read_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, &tmp); + tmp &= ~(AMD_X86_64_GARTEN); + pci_write_config_dword (dev, AMD_X86_64_GARTAPERTURECTL, tmp); + } + + /* Now shadow the disable in the 8151 */ + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_8151_0) { + + pci_read_config_dword (dev, AMD_8151_AGP_CTL, &tmp); + tmp &= ~(AMD_8151_APEREN); + pci_write_config_dword (dev, AMD_8151_AGP_CTL, tmp); + } + } +} + + + +static unsigned long amd_8151_mask_memory(unsigned long addr, int type) +{ + return addr | agp_bridge.masks[0].mask; +} + + +static struct gatt_mask amd_8151_masks[] = +{ + {0x00000001, 0} +}; + + +/* + * Try to configure an AGP v3 capable setup. + * If we fail (typically because we don't have an AGP v3 + * card in the system) we fall back to the generic AGP v2 + * routines. + */ +static void agp_x86_64_agp_enable(u32 mode) +{ + struct pci_dev *device = NULL; + u32 command, scratch; + u8 cap_ptr; + u8 agp_v3; + u8 v3_devs=0; + + /* FIXME: If 'mode' is x1/x2/x4 should we call the AGPv2 routines directly ? + * Messy, as some AGPv3 cards can only do x4 as a minimum. + */ + + /* PASS1: Count # of devs capable of AGPv3 mode. */ + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) { + pci_read_config_dword(device, cap_ptr, &scratch); + scratch &= (1<<20|1<<21|1<<22|1<<23); + scratch = scratch>>20; + /* AGP v3 capable ? */ + if (scratch>=3) { + v3_devs++; + printk (KERN_INFO "AGP: Found AGPv3 capable device at %d:%d:%d\n", + device->bus->number, PCI_FUNC(device->devfn), PCI_SLOT(device->devfn)); + } else { + printk (KERN_INFO "AGP: Meh. version %x AGP device found.\n", scratch); + } + } + } + /* If not enough, go to AGP v2 setup */ + if (v3_devs<2) { + printk (KERN_INFO "AGP: Only %d devices found, not enough, trying AGPv2\n", v3_devs); + return agp_generic_agp_enable(mode); + } else { + printk (KERN_INFO "AGP: Enough AGPv3 devices found, setting up...\n"); + } + + + pci_read_config_dword(agp_bridge.dev, agp_bridge.capndx + 4, &command); + + /* + * PASS2: go through all devices that claim to be + * AGP devices and collect their data. + */ + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) { + /* + * Ok, here we have a AGP device. Disable impossible + * settings, and adjust the readqueue to the minimum. + */ + + printk (KERN_INFO "AGP: Setting up AGPv3 capable device at %d:%d:%d\n", + device->bus->number, PCI_FUNC(device->devfn), PCI_SLOT(device->devfn)); + pci_read_config_dword(device, cap_ptr + 4, &scratch); + agp_v3 = (scratch & (1<<3) ) >>3; + + /* adjust RQ depth */ + command = + ((command & ~0xff000000) | + min_t(u32, (mode & 0xff000000), + min_t(u32, (command & 0xff000000), + (scratch & 0xff000000)))); + + /* disable SBA if it's not supported */ + if (!((command & 0x200) && (scratch & 0x200) && (mode & 0x200))) + command &= ~0x200; + + /* disable FW if it's not supported */ + if (!((command & 0x10) && (scratch & 0x10) && (mode & 0x10))) + command &= ~0x10; + + if (!((command & 2) && (scratch & 2) && (mode & 2))) { + command &= ~2; /* 8x */ + printk (KERN_INFO "AGP: Putting device into 8x mode\n"); + } + + if (!((command & 1) && (scratch & 1) && (mode & 1))) { + command &= ~1; /* 4x */ + printk (KERN_INFO "AGP: Putting device into 4x mode\n"); + } + } + } + /* + * PASS3: Figure out the 8X/4X setting and enable the + * target (our motherboard chipset). + */ + + if (command & 2) + command &= ~5; /* 8X */ + + if (command & 1) + command &= ~6; /* 4X */ + + command |= 0x100; + + pci_write_config_dword(agp_bridge.dev, agp_bridge.capndx + 8, command); + + /* + * PASS4: Go through all AGP devices and update the + * command registers. + */ + + pci_for_each_dev(device) { + cap_ptr = pci_find_capability(device, PCI_CAP_ID_AGP); + if (cap_ptr != 0x00) + pci_write_config_dword(device, cap_ptr + 8, command); + } +} + + +int __init amd_8151_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = amd_8151_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) amd_8151_sizes; + agp_bridge.size_type = U32_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = amd_8151_configure; + agp_bridge.fetch_size = amd_x86_64_fetch_size; + agp_bridge.cleanup = amd_8151_cleanup; + agp_bridge.tlb_flush = amd_x86_64_tlbflush; + agp_bridge.mask_memory = amd_8151_mask_memory; + agp_bridge.agp_enable = agp_x86_64_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = x86_64_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + agp_bridge.agp_alloc_page = agp_generic_alloc_page; + agp_bridge.agp_destroy_page = agp_generic_destroy_page; + agp_bridge.suspend = agp_generic_suspend; + agp_bridge.resume = agp_generic_resume; + agp_bridge.cant_use_aperture = 0; + + return 0; +} diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c745940468b81124fa0fa6cb6db99734cc96626b..3eb86b8e4b66750d8394396809f9424b5fd0980e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -121,6 +121,8 @@ extern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */ extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */ #endif +extern void disable_early_printk(void); + /* * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. @@ -2185,6 +2187,9 @@ void __init console_init(void) * set up the console device so that later boot sequences can * inform about problems etc.. */ +#ifdef CONFIG_EARLY_PRINTK + disable_early_printk(); +#endif #ifdef CONFIG_VT con_init(); #endif diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 2af88118b309500f3a63519f8f0a65dfea3c8fe6..cddfe9c338047e4dc646767b243912d381bbb1e2 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -36,14 +36,11 @@ * This driver has been tested SUCCESSFULLY with the following drivers : * o usb-uhci-hcd (For Intel/Via USB controllers) * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) + * o ohci-hcd (For other USB controllers) * * This driver has NOT been tested with the following drivers : * o ehci-hcd (USB 2.0 controllers) * - * This driver DOESN'T SEEM TO WORK with the following drivers : - * o ohci-hcd (For other USB controllers) - * The first outgoing URB never calls its completion/failure callback. - * * Note that all HCD drivers do USB_ZERO_PACKET and timeout properly, * so we don't have to worry about that anymore. * One common problem is the failure to set the address on the dongle, @@ -104,8 +101,8 @@ MODULE_DEVICE_TABLE(usb, dongles); /*------------------------------------------------------------------*/ -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum); -static void irda_usb_disconnect(struct usb_device *dev, void *ptr); +static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); +static void irda_usb_disconnect(struct usb_interface *intf); static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev); static int irda_usb_open(struct irda_usb_cb *self); @@ -1340,7 +1337,7 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) /*------------------------------------------------------------------*/ /* - * Function irda_usb_find_class_desc(dev, ifnum) + * Function irda_usb_find_class_desc(intf) * * Returns instance of IrDA class descriptor, or NULL if not found * @@ -1348,8 +1345,9 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) * offer to us, describing their IrDA characteristics. We will use that in * irda_usb_init_qos() */ -static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf) { + struct usb_device *dev = interface_to_usbdev (intf); struct irda_class_desc *desc; int ret; @@ -1368,7 +1366,8 @@ static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_device ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), IU_REQ_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, ifnum, desc, sizeof(*desc), MSECS_TO_JIFFIES(500)); + 0, intf->altsetting->bInterfaceNumber, desc, + sizeof(*desc), MSECS_TO_JIFFIES(500)); IRDA_DEBUG(1, "%s(), ret=%d\n", __FUNCTION__, ret); if (ret < sizeof(*desc)) { @@ -1403,9 +1402,10 @@ static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_device * Note : it might be worth protecting this function by a global * spinlock... Or not, because maybe USB already deal with that... */ -static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id) +static int irda_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) { + struct usb_device *dev = interface_to_usbdev(intf); struct irda_usb_cb *self = NULL; struct usb_interface_descriptor *interface; struct irda_class_desc *irda_desc; @@ -1430,7 +1430,7 @@ static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, (irda->present == 0) && (irda->netopen == 0)) { IRDA_DEBUG(0, "%s(), found a zombie instance !!!\n", __FUNCTION__); - irda_usb_disconnect(irda->usbdev, (void *) irda); + irda_usb_disconnect(irda->usbintf); } } @@ -1445,7 +1445,7 @@ static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, if(self == NULL) { WARNING("Too many USB IrDA devices !!! (max = %d)\n", NIRUSB); - return NULL; + return -ENFILE; } /* Reset the instance */ @@ -1459,35 +1459,35 @@ static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, int j; for (j = 0; j < i; j++) usb_free_urb(self->rx_urb[j]); - return NULL; + return -ENOMEM; } } self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!self->tx_urb) { for (i = 0; i < IU_MAX_RX_URBS; i++) usb_free_urb(self->rx_urb[i]); - return NULL; + return -ENOMEM; } self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); if (!self->speed_urb) { for (i = 0; i < IU_MAX_RX_URBS; i++) usb_free_urb(self->rx_urb[i]); usb_free_urb(self->tx_urb); - return NULL; + return -ENOMEM; } /* Is this really necessary? */ if (usb_set_configuration (dev, dev->config[0].bConfigurationValue) < 0) { err("set_configuration failed"); - return NULL; + return -EIO; } /* Is this really necessary? */ /* Note : some driver do hardcode the interface number, some others * specify an alternate, but very few driver do like this. * Jean II */ - ret = usb_set_interface(dev, ifnum, 0); - IRDA_DEBUG(1, "usb-irda: set interface %d result %d\n", ifnum, ret); + ret = usb_set_interface(dev, intf->altsetting->bInterfaceNumber, 0); + IRDA_DEBUG(1, "usb-irda: set interface %d result %d\n", intf->altsetting->bInterfaceNumber, ret); switch (ret) { case 0: break; @@ -1497,33 +1497,35 @@ static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, break; default: IRDA_DEBUG(0, "%s(), Unknown error %d\n", __FUNCTION__, ret); - return NULL; + return -EIO; break; } /* Find our endpoints */ - interface = &dev->actconfig->interface[ifnum].altsetting[0]; + interface = &intf->altsetting[0]; if(!irda_usb_parse_endpoints(self, interface->endpoint, interface->bNumEndpoints)) { ERROR("%s(), Bogus endpoints...\n", __FUNCTION__); - return NULL; + return -EIO; } /* Find IrDA class descriptor */ - irda_desc = irda_usb_find_class_desc(dev, ifnum); + irda_desc = irda_usb_find_class_desc(intf); if (irda_desc == NULL) - return NULL; + return -ENODEV; self->irda_desc = irda_desc; self->present = 1; self->netopen = 0; self->capability = id->driver_info; self->usbdev = dev; + self->usbintf = intf; ret = irda_usb_open(self); if (ret) - return NULL; + return -ENOMEM; - return self; + dev_set_drvdata(&intf->dev, self); + return 0; } /*------------------------------------------------------------------*/ @@ -1538,14 +1540,18 @@ static void *irda_usb_probe(struct usb_device *dev, unsigned int ifnum, * So, we must make bloody sure that everything gets deactivated. * Jean II */ -static void irda_usb_disconnect(struct usb_device *dev, void *ptr) +static void irda_usb_disconnect(struct usb_interface *intf) { unsigned long flags; - struct irda_usb_cb *self = (struct irda_usb_cb *) ptr; + struct irda_usb_cb *self = dev_get_drvdata (&intf->dev); int i; IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + dev_set_drvdata(&intf->dev, NULL); + if (!self) + return; + /* Make sure that the Tx path is not executing. - Jean II */ spin_lock_irqsave(&self->lock, flags); @@ -1577,6 +1583,7 @@ static void irda_usb_disconnect(struct usb_device *dev, void *ptr) irda_usb_close(self); /* No longer attached to USB bus */ self->usbdev = NULL; + self->usbintf = NULL; /* Clean up our urbs */ for (i = 0; i < IU_MAX_RX_URBS; i++) @@ -1635,7 +1642,7 @@ void __exit usb_irda_cleanup(void) /* If the Device is zombie */ if((irda->usbdev != NULL) && (irda->present == 0)) { IRDA_DEBUG(0, "%s(), disconnect zombie now !\n", __FUNCTION__); - irda_usb_disconnect(irda->usbdev, (void *) irda); + irda_usb_disconnect(irda->usbintf); } } diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c index 4c3009ef30afab2e2dc97d0d14f8dc5fd53c4672..fdb4b96c03293b7a082391034156b14a17c3558d 100644 --- a/drivers/net/wireless/airport.c +++ b/drivers/net/wireless/airport.c @@ -1,4 +1,4 @@ -/* airport.c 0.11b +/* airport.c 0.13 * * A driver for "Hermes" chipset based Apple Airport wireless * card. @@ -30,7 +30,6 @@ #include <linux/if_arp.h> #include <linux/etherdevice.h> #include <linux/wireless.h> -#include <linux/list.h> #include <linux/adb.h> #include <linux/pmu.h> @@ -39,22 +38,15 @@ #include <asm/pmac_feature.h> #include <asm/irq.h> -#include "hermes.h" #include "orinoco.h" -static char version[] __initdata = "airport.c 0.11b (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; -MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); -MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); -MODULE_LICENSE("Dual MPL/GPL"); - #define AIRPORT_IO_LEN (0x1000) /* one page */ struct airport { - struct device_node* node; + struct device_node *node; void *vaddr; int irq_requested; int ndev_registered; - int open; }; #ifdef CONFIG_PMAC_PBOOK @@ -70,101 +62,77 @@ static struct pmu_sleep_notifier airport_sleep_notifier = { static struct net_device *airport_attach(struct device_node *of_node); static void airport_detach(struct net_device *dev); -static int airport_open(struct net_device *dev); -static int airport_stop(struct net_device *dev); - -/* - A linked list of "instances" of the dummy device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor - device numbers are used to derive the corresponding array index. -*/ static struct net_device *airport_dev; -static int -airport_open(struct net_device *dev) -{ - struct orinoco_private *priv = dev->priv; - struct airport* card = (struct airport *)priv->card; - int rc; - - TRACE_ENTER(dev->name); - - netif_device_attach(dev); - - rc = orinoco_reset(priv); - if (rc) - airport_stop(dev); - else { - card->open = 1; - netif_start_queue(dev); - } - - TRACE_EXIT(dev->name); - - return rc; -} - -static int -airport_stop(struct net_device *dev) -{ - struct orinoco_private *priv = dev->priv; - struct airport* card = (struct airport *)priv->card; - - TRACE_ENTER(dev->name); - - netif_stop_queue(dev); - orinoco_shutdown(priv); - card->open = 0; - - TRACE_EXIT(dev->name); - - return 0; -} - #ifdef CONFIG_PMAC_PBOOK static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when) { struct net_device *dev = airport_dev; - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; - struct hermes *hw = &priv->hw; - struct airport* card = (struct airport *)priv->card; - int rc; + struct orinoco_private *priv = dev->priv; + struct airport *card = priv->card; + unsigned long flags; + int err; if (! airport_dev) return PBOOK_SLEEP_OK; switch (when) { - case PBOOK_SLEEP_REQUEST: - break; - case PBOOK_SLEEP_REJECT: - break; case PBOOK_SLEEP_NOW: - printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name); - if (card->open) { - netif_stop_queue(dev); - orinoco_shutdown(priv); - netif_device_detach(dev); + printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); + + err = orinoco_lock(priv, &flags); + if (err) { + printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", + dev->name); + break; } + + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: PBOOK_SLEEP_NOW: Error %d downing interface\n", + dev->name, err); + + netif_device_detach(dev); + + priv->hw_unavailable = 1; + + orinoco_unlock(priv, &flags); + disable_irq(dev->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0); break; + case PBOOK_WAKE: - printk(KERN_INFO "%s: Airport waking up\n", dev->name); + printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1); mdelay(200); - hermes_reset(hw); - rc = orinoco_reset(priv); - if (rc) - printk(KERN_ERR "airport: Error %d re-initing card !\n", rc); - else if (card->open) - netif_device_attach(dev); + enable_irq(dev->irq); + + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware on PBOOK_WAKE\n", + dev->name, err); + break; + } + + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); + + if (priv->open) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n", + dev->name, err); + } + + priv->hw_unavailable = 0; + + spin_unlock_irqrestore(&priv->lock, flags); + break; } return PBOOK_SLEEP_OK; @@ -172,7 +140,7 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when) #endif /* CONFIG_PMAC_PBOOK */ static struct net_device * -airport_attach(struct device_node* of_node) +airport_attach(struct device_node *of_node) { struct orinoco_private *priv; struct net_device *dev; @@ -180,15 +148,13 @@ airport_attach(struct device_node* of_node) unsigned long phys_addr; hermes_t *hw; - TRACE_ENTER("orinoco"); - if (of_node->n_addrs < 1 || of_node->n_intrs < 1) { printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n"); return NULL; } /* Allocate space for private device-specific data */ - dev = alloc_orinocodev(sizeof(*card)); + dev = alloc_orinocodev(sizeof(*card), NULL); if (! dev) { printk(KERN_ERR "airport: can't allocate device datas\n"); return NULL; @@ -204,17 +170,14 @@ airport_attach(struct device_node* of_node) kfree(dev); return NULL; } - + dev->name[0] = '\0'; /* register_netdev will give us an ethX name */ SET_MODULE_OWNER(dev); - /* Overrides */ - dev->open = airport_open; - dev->stop = airport_stop; - /* Setup interrupts & base address */ dev->irq = of_node->intrs[0].line; - phys_addr = of_node->addrs[0].address; /* Physical address */ + phys_addr = of_node->addrs[0].address; /* Physical address */ + printk(KERN_DEBUG "Airport at physical address %lx\n", phys_addr); dev->base_addr = phys_addr; card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); if (! card->vaddr) { @@ -231,14 +194,14 @@ airport_attach(struct device_node* of_node) schedule_timeout(HZ); /* Reset it before we get the interrupt */ - hermes_reset(hw); + hermes_init(hw); if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) { printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq); goto failed; } card->irq_requested = 1; - + /* Tell the stack we exist */ if (register_netdev(dev) != 0) { printk(KERN_ERR "airport: register_netdev() failed\n"); @@ -248,7 +211,7 @@ airport_attach(struct device_node* of_node) card->ndev_registered = 1; /* And give us the proc nodes for debugging */ - if (orinoco_proc_dev_init(priv) != 0) + if (orinoco_proc_dev_init(dev) != 0) printk(KERN_ERR "airport: Failed to create /proc node for %s\n", dev->name); @@ -257,7 +220,7 @@ airport_attach(struct device_node* of_node) #endif return dev; -failed: + failed: airport_detach(dev); return NULL; } /* airport_attach */ @@ -273,7 +236,7 @@ airport_detach(struct net_device *dev) struct airport *card = priv->card; /* Unregister proc entry */ - orinoco_proc_dev_cleanup(priv); + orinoco_proc_dev_cleanup(dev); #ifdef CONFIG_PMAC_PBOOK pmu_unregister_sleep_notifier(&airport_sleep_notifier); @@ -281,7 +244,7 @@ airport_detach(struct net_device *dev) if (card->ndev_registered) unregister_netdev(dev); card->ndev_registered = 0; - + if (card->irq_requested) free_irq(dev->irq, priv); card->irq_requested = 0; @@ -289,22 +252,28 @@ airport_detach(struct net_device *dev) if (card->vaddr) iounmap(card->vaddr); card->vaddr = 0; - + dev->base_addr = 0; release_OF_resource(card->node, 0); - + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0); current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ); - + kfree(dev); } /* airport_detach */ +static char version[] __initdata = "airport.c 0.13 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); +MODULE_LICENSE("Dual MPL/GPL"); +EXPORT_NO_SYMBOLS; + static int __init init_airport(void) { - struct device_node* airport_node; + struct device_node *airport_node; printk(KERN_DEBUG "%s\n", version); diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index e691667c3e03649e59b8336ae891723b2fb93d8f..8b3103bd457d1dd3f638d5ba8da5f607a0ae563b 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -45,7 +45,6 @@ #include <linux/threads.h> #include <linux/smp.h> #include <asm/io.h> -#include <linux/ptrace.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/kernel.h> @@ -53,7 +52,7 @@ #include "hermes.h" -static char version[] __initdata = "hermes.c: 5 Apr 2002 David Gibson <hermes@gibson.dropbear.id.au>"; +static char version[] __initdata = "hermes.c: 4 Jul 2002 David Gibson <hermes@gibson.dropbear.id.au>"; MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>"); #ifdef MODULE_LICENSE @@ -70,14 +69,14 @@ MODULE_LICENSE("Dual MPL/GPL"); * Debugging helpers */ +#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ") +#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %s0x%x: " , IO_TYPE(hw), hw->iobase); \ + printk(stuff);} while (0) + #undef HERMES_DEBUG #ifdef HERMES_DEBUG - #include <stdarg.h> -#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \ - printk(stuff);} while (0) - #define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff) #else /* ! HERMES_DEBUG */ @@ -86,7 +85,6 @@ MODULE_LICENSE("Dual MPL/GPL"); #endif /* ! HERMES_DEBUG */ -#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ") /* * Internal functions @@ -111,7 +109,6 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) udelay(1); reg = hermes_read_regn(hw, CMD); } - DEBUG(3, "hermes_issue_cmd: did %d retries.\n", CMD_BUSY_TIMEOUT-k); if (reg & HERMES_CMD_BUSY) { return -EBUSY; } @@ -143,7 +140,7 @@ void hermes_struct_init(hermes_t *hw, ulong address, #endif } -int hermes_reset(hermes_t *hw) +int hermes_init(hermes_t *hw) { u16 status, reg; int err = 0; @@ -195,8 +192,6 @@ int hermes_reset(hermes_t *hw) reg = hermes_read_regn(hw, EVSTAT); } - DEBUG(0, "Reset completed in %d iterations\n", CMD_INIT_TIMEOUT - k); - hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); if (! hermes_present(hw)) { @@ -303,9 +298,6 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); if (err) { - printk(KERN_WARNING "hermes @ %s0x%lx: " - "Frame allocation command failed (0x%X).\n", - IO_TYPE(hw), hw->iobase, err); return err; } @@ -393,12 +385,10 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) } if (reg & HERMES_OFFSET_BUSY) { - DEBUG(1,"hermes_bap_seek: timeout\n"); return -ETIMEDOUT; } if (reg & HERMES_OFFSET_ERR) { - DEBUG(1,"hermes_bap_seek: BAP error\n"); return -EIO; } @@ -472,6 +462,7 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; u16 rlength, rtype; + int nwords; if ( (bufsize < 0) || (bufsize % 2) ) return -EINVAL; @@ -500,10 +491,9 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, "(rid=0x%04x, len=0x%04x)\n", IO_TYPE(hw), hw->iobase, HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); - - /* FIXME: we should read the min of the requested length and - the actual record length */ - hermes_read_words(hw, dreg, buf, bufsize / 2); + + nwords = min_t(int, rlength - 1, bufsize / 2); + hermes_read_words(hw, dreg, buf, nwords); out: return err; @@ -516,9 +506,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, int err = 0; int count; - DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n", - bap, rid, length, * ((u16 *)value)); - err = hermes_bap_seek(hw, bap, rid, 0); if (err) goto out; @@ -538,7 +525,7 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, } EXPORT_SYMBOL(hermes_struct_init); -EXPORT_SYMBOL(hermes_reset); +EXPORT_SYMBOL(hermes_init); EXPORT_SYMBOL(hermes_docmd_wait); EXPORT_SYMBOL(hermes_allocate); diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h index fed0f2d137e541dfec8ff0c86a6e30653bffffe6..f5faaf1f5f88471f2ae2f34bd91a6cb288227710 100644 --- a/drivers/net/wireless/hermes.h +++ b/drivers/net/wireless/hermes.h @@ -171,6 +171,7 @@ struct hermes_rx_descriptor { #define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_RXSTAT_MACPORT (0x0700) +#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ #define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ #define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ @@ -299,7 +300,7 @@ typedef struct hermes_response { /* Function prototypes */ void hermes_struct_init(hermes_t *hw, ulong address, int io_space, int reg_spacing); -int hermes_reset(hermes_t *hw); +int hermes_init(hermes_t *hw); int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp); int hermes_allocate(hermes_t *hw, u16 size, u16 *fid); @@ -319,12 +320,6 @@ static inline int hermes_present(hermes_t *hw) return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; } -static inline void hermes_enable_interrupt(hermes_t *hw, u16 events) -{ - hw->inten |= events; - hermes_write_regn(hw, INTEN, hw->inten); -} - static inline void hermes_set_irqmask(hermes_t *hw, u16 events) { hw->inten = events; @@ -350,7 +345,7 @@ static inline int hermes_inquire(hermes_t *hw, u16 rid) return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); } -#define HERMES_BYTES_TO_RECLEN(n) ( ((n) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 ) +#define HERMES_BYTES_TO_RECLEN(n) ( (((n)+1)/2) + 1 ) #define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 ) /* Note that for the next two, the count is in 16-bit words, not bytes */ diff --git a/drivers/net/wireless/ieee802_11.h b/drivers/net/wireless/ieee802_11.h index e399f13562c327870f8805bea8c8a5b5786dd148..2be7864a782a314d8205e5a6142b12e670bfc3c0 100644 --- a/drivers/net/wireless/ieee802_11.h +++ b/drivers/net/wireless/ieee802_11.h @@ -3,8 +3,8 @@ #define IEEE802_11_DATA_LEN 2304 /* Actually, the standard seems to be inconsistent about what the - maximum frame size really is. S6.2.1.1.2 says 2304 octets, but the - figure in section 7.1.2 says 2312 octects. */ + maximum frame size really is. Section 6.2.1.1.2 says 2304 octets, + but the figure in Section 7.1.2 says 2312 octects. */ #define IEEE802_11_HLEN 30 #define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN) diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 5f4876da8461cfbc85702068b5aa331e32b08709..61d5fe33075a7f857cbb439d3b69d13f713ba4dd 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1,4 +1,4 @@ -/* orinoco.c 0.11b - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* orinoco.c 0.13 - (formerly known as dldwd_cs.c and orinoco_cs.c) * * A driver for Hermes or Prism 2 chipset based PCMCIA wireless * adaptors, with Lucent/Agere, Intersil or Symbol firmware. @@ -240,7 +240,7 @@ * was never very useful. * o Make Rx errors less noisy. * - * v0.10 -> v0.11 - 5 Apr Mar 2002 - David Gibson + * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson * o Laid the groundwork in hermes.[ch] for devices which map * into PCI memory space rather than IO space. * o Fixed bug in multicast handling (cleared multicast list when @@ -256,7 +256,7 @@ * o Fixes for recent Symbol firmwares which lack AP density * (Pavel Roskin). * - * v0.11 -> v0.11b - 29 Apr 2002 - David Gibson + * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson * o Handle different register spacing, necessary for Prism 2.5 * PCI adaptors (Steve Hill). * o Cleaned up initialization of card structures in orinoco_cs @@ -279,44 +279,79 @@ * o Fixed multiple bad kfree() bugs introduced by the * alloc_orinocodev() changes. * + * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson + * o Support changing the MAC address. + * o Correct display of Intersil firmware revision numbers. + * o Entirely revised locking scheme. Should be both simpler and + * better. + * o Merged some common code in orinoco_plx, orinoco_pci and + * airport by creating orinoco_default_{open,stop,reset}() + * which are used as the dev->open, dev->stop, priv->reset + * callbacks if none are specified when alloc_orinocodev() is + * called. + * o Removed orinoco_plx_interupt() and orinoco_pci_interrupt(). + * They didn't do anything. + * + * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson + * o Some rearrangement of code. + * o Numerous fixups to locking and rest handling, particularly + * for PCMCIA. + * o This allows open and stop net_device methods to be in + * orinoco.c now, rather than in the init modules. + * o In orinoco_cs.c link->priv now points to the struct + * net_device not to the struct orinoco_private. + * o Added a check for undersized SNAP frames, which could cause + * crashes. + * + * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson + * o Fix hw->num_init testing code, so num_init is actually + * incremented. + * o Fix very stupid bug in orinoco_cs which broke compile with + * CONFIG_SMP. + * o Squashed a warning. + * + * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson + * o Change to C9X style designated initializers. + * o Add support for 3Com AirConnect PCI. + * o No longer ignore the hard_reset argument to + * alloc_orinocodev(). Oops. + * + * v0.12c -> v0.13 - 13 Sep 2002 - David Gibson + * o Revert the broken 0.12* locking scheme and go to a new yet + * simpler scheme. + * o Do firmware resets only in orinoco_init() and when waking + * the card from hard sleep. + * + * v0.13 -> v0.13 - 27 Sep 2002 - David Gibson + * o Re-introduced full resets (via schedule_task()) on Tx + * timeout. + * * TODO - * o New wireless extensions API + * o New wireless extensions API (patch forthcoming from Moustafa + * Youssef). * o Handle de-encapsulation within network layer, provide 802.11 - * headers + * headers (patch from Thomas 'Dent' Mirlacher) * o Fix possible races in SPY handling. * o Disconnect wireless extensions from fundamental configuration. + * o (maybe) Software WEP support (patch from Stano Meduna). + * o (maybe) Convert /proc debugging stuff to seqfile + * o (maybe) Use multiple Tx buffers - driver handling queue + * rather than firmware. */ + +/* Locking and synchronization: * - * o Convert /proc debugging stuff to seqfile - * o Use multiple Tx buffers - */ -/* Notes on locking: - * - * The basic principle of operation is that everything except the - * interrupt handler is serialized through a single spinlock in the - * struct orinoco_private structure, using orinoco_lock() and - * orinoco_unlock() (which in turn use spin_lock_bh() and - * spin_unlock_bh()). - * - * The kernel's IRQ handling stuff ensures that the interrupt handler - * does not re-enter itself. The interrupt handler is written such - * that everything it does is safe without a lock: chiefly this means - * that the Rx path uses one of the Hermes chipset's BAPs while - * everything else uses the other. - * - * Actually, strictly speaking, the updating of the statistics from - * the interrupt handler isn't safe without a lock. However the worst - * that can happen is that we perturb the packet/byte counts slightly. - * We could fix this to use atomic types, but it's probably not worth - * it. + * The basic principle is that everything is serialized through a + * single spinlock, priv->lock. The lock is used in user, bh and irq + * context, so when taken outside hardirq context it should always be + * taken with interrupts disabled. The lock protects both the + * hardware and the struct orinoco_private. * - * The big exception is that that we don't want the irq handler - * running when we actually reset or shut down the card, because - * strange things might happen (probably the worst would be one packet - * of garbage, but you can't be too careful). For this we use - * __orinoco_stop_irqs() which will set a flag to disable the interrupt - * handler, and wait for any outstanding instances of the handler to - * complete. THIS WILL LOSE INTERRUPTS! so it shouldn't be used except - * for resets, where losing a few interrupts is acceptable. */ + * Another flag, priv->hw_unavailable indicates that the hardware is + * unavailable for an extended period of time (e.g. suspended, or in + * the middle of a hard reset). This flag is protected by the + * spinlock. All code which touches the hardware should check the + * flag after taking the lock, and if it is set, give up on whatever + * they are doing and drop the lock again. */ #include <linux/config.h> @@ -333,31 +368,20 @@ #include <asm/io.h> #include <asm/system.h> #include <linux/proc_fs.h> -#include <linux/seq_file.h> #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> #include <linux/wireless.h> -#include <linux/list.h> #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "ieee802_11.h" -/* Wireless extensions backwards compatibility */ -#ifndef SIOCIWFIRSTPRIV -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif /* SIOCIWFIRSTPRIV */ - -/* We do this this way to avoid ifdefs in the actual code */ -#ifdef WIRELESS_SPY -#define SPY_NUMBER(priv) (priv->spy_number) -#else -#define SPY_NUMBER(priv) 0 -#endif /* WIRELESS_SPY */ +/********************************************************************/ +/* Module information */ +/********************************************************************/ -static char version[] __initdata = "orinoco.c 0.11b (David Gibson <hermes@gibson.dropbear.id.au> and others)"; MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>"); MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards"); #ifdef MODULE_LICENSE @@ -371,6 +395,26 @@ MODULE_PARM(orinoco_debug, "i"); EXPORT_SYMBOL(orinoco_debug); #endif +/********************************************************************/ +/* Compile time configuration and compatibility stuff */ +/********************************************************************/ + +/* Wireless extensions backwards compatibility */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ + +/* We do this this way to avoid ifdefs in the actual code */ +#ifdef WIRELESS_SPY +#define SPY_NUMBER(priv) (priv->spy_number) +#else +#define SPY_NUMBER(priv) 0 +#endif /* WIRELESS_SPY */ + +/********************************************************************/ +/* Internal constants */ +/********************************************************************/ + #define ORINOCO_MIN_MTU 256 #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) @@ -386,6 +430,8 @@ EXPORT_SYMBOL(orinoco_debug); #define DUMMY_FID 0xFFFF +#define RUP_EVEN(a) (((a) + 1) & (~1)) + /*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \ HERMES_MAX_MULTICAST : 0)*/ #define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) @@ -419,6 +465,10 @@ struct { }; #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) +/********************************************************************/ +/* Data types */ +/********************************************************************/ + struct header_struct { /* 802.3 */ u8 dest[ETH_ALEN]; @@ -438,27 +488,29 @@ u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) -/* - * Function prototypes - */ +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ static void orinoco_stat_gather(struct net_device *dev, - struct sk_buff *skb, - struct hermes_rx_descriptor *desc); + struct sk_buff *skb, + struct hermes_rx_descriptor *desc); static struct net_device_stats *orinoco_get_stats(struct net_device *dev); static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev); /* Hardware control routines */ +static int __orinoco_program_rids(struct orinoco_private *priv); + static int __orinoco_hw_set_bitrate(struct orinoco_private *priv); static int __orinoco_hw_setup_wep(struct orinoco_private *priv); static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]); static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]); + char buf[IW_ESSID_MAX_SIZE+1]); static long orinoco_hw_get_freq(struct orinoco_private *priv); static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max); + s32 *rates, int max); static void __orinoco_set_multicast_list(struct net_device *dev); /* Interrupt handling routines */ @@ -471,6 +523,7 @@ static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw); static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); +/* ioctl() routines */ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq); static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq); @@ -495,160 +548,161 @@ static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq); static int orinoco_proc_init(void); static void orinoco_proc_cleanup(void); -/* - * Inline functions - */ -static inline void -orinoco_lock(struct orinoco_private *priv) +int __orinoco_up(struct net_device *dev) { - spin_lock_bh(&priv->lock); -} + struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + int err; -static inline void -orinoco_unlock(struct orinoco_private *priv) -{ - spin_unlock_bh(&priv->lock); -} + err = __orinoco_program_rids(priv); + if (err) { + printk(KERN_ERR "%s: Error %d configuring card\n", + dev->name, err); + return err; + } -static inline int -orinoco_irqs_allowed(struct orinoco_private *priv) -{ - return test_bit(ORINOCO_STATE_DOIRQ, &priv->state); -} + /* Fire things up again */ + hermes_set_irqmask(hw, ORINOCO_INTEN); + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Error %d enabling MAC port\n", + dev->name, err); + return err; + } -static inline void -__orinoco_stop_irqs(struct orinoco_private *priv) -{ - hermes_t *hw = &priv->hw; + netif_start_queue(dev); - hermes_set_irqmask(hw, 0); - clear_bit(ORINOCO_STATE_DOIRQ, &priv->state); - while (test_bit(ORINOCO_STATE_INIRQ, &priv->state)) - ; + return 0; } -static inline void -__orinoco_start_irqs(struct orinoco_private *priv, u16 irqmask) +int __orinoco_down(struct net_device *dev) { - hermes_t *hw = &priv->hw; + struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + int err; - TRACE_ENTER(priv->ndev->name); + netif_stop_queue(dev); - local_irq_disable(); /* FIXME: is this necessary? */ - set_bit(ORINOCO_STATE_DOIRQ, &priv->state); - hermes_set_irqmask(hw, irqmask); - local_irq_enable(); + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Error %d disabling MAC port\n", + dev->name, err); + return err; + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); - TRACE_EXIT(priv->ndev->name); + return 0; } -static inline void -set_port_type(struct orinoco_private *priv) +int orinoco_reinit_firmware(struct net_device *dev) { - switch (priv->iw_mode) { - case IW_MODE_INFRA: - priv->port_type = 1; - priv->allow_ibss = 0; - break; - case IW_MODE_ADHOC: - if (priv->prefer_port3) { - priv->port_type = 3; - priv->allow_ibss = 0; - } else { - priv->port_type = priv->ibss_port; - priv->allow_ibss = 1; - } - break; - default: - printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", - priv->ndev->name); + struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + int err; + + err = hermes_init(hw); + if (err) + return err; + + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO) { + /* Try workaround for old Symbol firmware bug */ + printk(KERN_WARNING "%s: firmware ALLOC bug detected " + "(old Symbol firmware?). Trying to work around... ", + dev->name); + + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err) + printk("failed!\n"); + else + printk("ok.\n"); } -} -static inline int -is_snap(struct header_struct *hdr) -{ - return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); + return err; } -static void -orinoco_set_multicast_list(struct net_device *dev) +static int orinoco_open(struct net_device *dev) { struct orinoco_private *priv = dev->priv; + unsigned long flags; + int err; - orinoco_lock(priv); - __orinoco_set_multicast_list(dev); - orinoco_unlock(priv); -} + err = orinoco_lock(priv, &flags); + if (err) + return err; -/* - * Hardware control routines - */ + priv->open = 1; -void -orinoco_shutdown(struct orinoco_private *priv) + err = __orinoco_up(dev); + + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_stop(struct net_device *dev) { - int err = 0; + struct orinoco_private *priv = dev->priv; + unsigned long flags; + int err; - TRACE_ENTER(priv->ndev->name); + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); - __orinoco_stop_irqs(priv); + priv->open = 0; - err = hermes_reset(&priv->hw); - if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */ - printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev->name, err); + err = __orinoco_down(dev); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); - TRACE_EXIT(priv->ndev->name); + return err; } -int -orinoco_reset(struct orinoco_private *priv) +static int __orinoco_program_rids(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; hermes_t *hw = &priv->hw; - int err = 0; + int err; struct hermes_idstring idbuf; - TRACE_ENTER(priv->ndev->name); - - /* Stop other people bothering us */ - orinoco_lock(priv); - __orinoco_stop_irqs(priv); - - /* Check if we need a card reset */ - if (priv->hard_reset) - priv->hard_reset(priv); - - /* Do standard firmware reset if we can */ - err = hermes_reset(hw); - if (err) - goto out; - - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO) { - /* Try workaround for old Symbol firmware bug */ - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err) - goto out; + /* Set the MAC address */ + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); + if (err) { + printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); + return err; } - /* Now set up all the parameters on the card */ - /* Set up the link mode */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); + return err; + } + /* Set the channel/frequency */ + if (priv->channel == 0) { + printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name); + if (priv->createibss) + priv->channel = 10; + } + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); + if (err) { + printk(KERN_ERR "%s: Error %d setting channel\n", dev->name, err); + return err; + } + if (priv->has_ibss) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, - priv->allow_ibss); - if (err) - goto out; - if((strlen(priv->desired_essid) == 0) && (priv->allow_ibss) + priv->createibss); + if (err) { + printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); + return err; + } + + if ((strlen(priv->desired_essid) == 0) && (priv->createibss) && (!priv->has_ibss_any)) { printk(KERN_WARNING "%s: This firmware requires an \ ESSID in IBSS-Ad-Hoc mode.\n", dev->name); @@ -665,13 +719,17 @@ ESSID in IBSS-Ad-Hoc mode.\n", dev->name); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err); + return err; + } err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err); + return err; + } /* Set the station name */ idbuf.len = cpu_to_le16(strlen(priv->nick)); @@ -679,26 +737,29 @@ ESSID in IBSS-Ad-Hoc mode.\n", dev->name); err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), &idbuf); - if (err) - goto out; - - /* Set the channel/frequency */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); + return err; + } /* Set AP density */ if (priv->has_sensitivity) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density); - if (err) + if (err) { + printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " + "Disabling sensitivity control\n", dev->name, err); + priv->has_sensitivity = 0; + } } /* Set RTS threshold */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err); + return err; + } /* Set fragmentation threshold or MWO robustness */ if (priv->has_mwo) @@ -709,35 +770,52 @@ ESSID in IBSS-Ad-Hoc mode.\n", dev->name); err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, priv->frag_thresh); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting framentation\n", dev->name, err); + return err; + } /* Set bitrate */ err = __orinoco_hw_set_bitrate(priv); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err); + return err; + } /* Set power management */ if (priv->has_pm) { err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, priv->pm_on); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, priv->pm_mcast); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFMAXSLEEPDURATION, priv->pm_period); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, priv->pm_timeout); - if (err) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } } /* Set preamble - only for Symbol so far... */ @@ -746,50 +824,171 @@ ESSID in IBSS-Ad-Hoc mode.\n", dev->name); HERMES_RID_CNFPREAMBLE_SYMBOL, priv->preamble); if (err) { - printk(KERN_WARNING "%s: Can't set preamble!\n", dev->name); - goto out; + printk(KERN_ERR "%s: Error %d setting preamble\n", + dev->name, err); + return err; } } - /* Set up encryption */ - if (priv->has_wep) { - err = __orinoco_hw_setup_wep(priv); - if (err) { - printk(KERN_ERR "%s: Error %d activating WEP.\n", - dev->name, err); - goto out; + /* Set up encryption */ + if (priv->has_wep) { + err = __orinoco_hw_setup_wep(priv); + if (err) { + printk(KERN_ERR "%s: Error %d activating WEP\n", + dev->name, err); + return err; + } + } + + /* Set promiscuity / multicast*/ + priv->promiscuous = 0; + priv->mc_count = 0; + __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ + + return 0; +} + +/* xyzzy */ +static int orinoco_reconfigure(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; + + orinoco_lock(priv, &flags); + + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", + priv->ndev->name); + goto out; + } + + err = __orinoco_program_rids(priv); + if (err) + goto out; + + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", + priv->ndev->name); + goto out; + } + + out: + orinoco_unlock(priv, &flags); + return err; + +} + +/* This must be called from user context, without locks held - use + * schedule_task() */ +static void orinoco_reset(struct net_device *dev) +{ + struct orinoco_private *priv = dev->priv; + int err; + unsigned long flags; + + printk(KERN_INFO "%s: orinoco_reset()\n", dev->name); + + err = orinoco_lock(priv, &flags); + if (err) + return; + + priv->hw_unavailable = 1; + orinoco_unlock(priv, &flags); + + if (priv->hard_reset) + err = (*priv->hard_reset)(priv); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n", + dev->name, err); + /* FIXME: shutdown of some sort */ + return; + } + + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", + dev->name, err); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + priv->hw_unavailable = 0; + + err = __orinoco_up(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", + dev->name, err); + } else + dev->trans_start = jiffies; + + orinoco_unlock(priv, &flags); + + return; +} + +/********************************************************************/ +/* Internal helper functions */ +/********************************************************************/ + +static inline void +set_port_type(struct orinoco_private *priv) +{ + switch (priv->iw_mode) { + case IW_MODE_INFRA: + priv->port_type = 1; + priv->createibss = 0; + break; + case IW_MODE_ADHOC: + if (priv->prefer_port3) { + priv->port_type = 3; + priv->createibss = 0; + } else { + priv->port_type = priv->ibss_port; + priv->createibss = 1; } + break; + default: + printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", + priv->ndev->name); } +} - /* Set promiscuity / multicast*/ - priv->promiscuous = 0; - priv->mc_count = 0; - __orinoco_set_multicast_list(dev); - - __orinoco_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC | - HERMES_EV_TX | HERMES_EV_TXEXC | - HERMES_EV_WTERR | HERMES_EV_INFO | - HERMES_EV_INFDROP); - - err = hermes_enable_port(hw, 0); - if (err) - goto out; +static inline int +is_snap(struct header_struct *hdr) +{ + return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); +} - out: - orinoco_unlock(priv); +static void +orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = dev->priv; + unsigned long flags; - TRACE_EXIT(priv->ndev->name); + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " + "called when hw_unavailable\n", dev->name); + return; + } - return err; + __orinoco_set_multicast_list(dev); + orinoco_unlock(priv, &flags); } +/********************************************************************/ +/* Hardware control functions */ +/********************************************************************/ + + static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; - TRACE_ENTER(priv->ndev->name); - if (priv->bitratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", priv->ndev->name, priv->bitratemode); @@ -812,8 +1011,6 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) BUG(); } - TRACE_EXIT(priv->ndev->name); - return err; } @@ -825,8 +1022,6 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) int master_wep_flag; int auth_flag; - TRACE_ENTER(priv->ndev->name); - switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ if (priv->wep_on) { @@ -920,8 +1115,6 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv) } } - TRACE_EXIT(priv->ndev->name); - return 0; } @@ -929,13 +1122,16 @@ static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN] { hermes_t *hw = &priv->hw; int err = 0; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, buf); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -948,10 +1144,11 @@ static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, struct hermes_idstring essidbuf; char *p = (char *)(&essidbuf.val); int len; + unsigned long flags; - TRACE_ENTER(priv->ndev->name); - - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (strlen(priv->desired_essid) > 0) { /* We read the desired SSID from the hardware rather @@ -988,9 +1185,7 @@ static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, buf[len] = '\0'; fail_unlock: - orinoco_unlock(priv); - - TRACE_EXIT(priv->ndev->name); + orinoco_unlock(priv, &flags); return err; } @@ -1002,13 +1197,22 @@ static long orinoco_hw_get_freq(struct orinoco_private *priv) int err = 0; u16 channel; long freq = 0; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); if (err) goto out; + /* Intersil firmware 1.3.5 returns 0 when the interface is down */ + if (channel == 0) { + err = -EBUSY; + goto out; + } + if ( (channel < 1) || (channel > NUM_CHANNELS) ) { struct net_device *dev = priv->ndev; @@ -1020,7 +1224,7 @@ static long orinoco_hw_get_freq(struct orinoco_private *priv) freq = channel_frequency[channel-1] * 100000; out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err > 0) err = -EBUSY; @@ -1036,11 +1240,15 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrate int err = 0; int num; int i; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, sizeof(list), NULL, &list); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err) return err; @@ -1122,7 +1330,7 @@ static void show_rx_frame(struct orinoco_rxframe_hdr *frame) /* * Interrupt handler */ -void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs) +void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct orinoco_private *priv = (struct orinoco_private *) dev_id; hermes_t *hw = &priv->hw; @@ -1134,38 +1342,26 @@ void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs) * we panic and shut down the hardware */ static int last_irq_jiffy = 0; /* jiffies value the last time we were called */ static int loops_this_jiffy = 0; + unsigned long flags; - if (test_and_set_bit(ORINOCO_STATE_INIRQ, &priv->state)) - BUG(); - - if (! orinoco_irqs_allowed(priv)) { - clear_bit(ORINOCO_STATE_INIRQ, &priv->state); + if (orinoco_lock(priv, &flags) != 0) { + /* If hw is unavailable */ return; } - DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev->name); - evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; -/* if (! events) { */ -/* printk(KERN_WARNING "%s: Null event\n", dev->name); */ -/* } */ - if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; last_irq_jiffy = jiffies; while (events && count--) { - DEBUG(3, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n", - count, evstat); - if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { printk(KERN_CRIT "%s: IRQ handler is looping too \ much! Shutting down.\n", dev->name); /* Perform an emergency shutdown */ - clear_bit(ORINOCO_STATE_DOIRQ, &priv->state); hermes_set_irqmask(hw, 0); break; } @@ -1199,7 +1395,7 @@ much! Shutting down.\n", events = evstat & hw->inten; }; - clear_bit(ORINOCO_STATE_INIRQ, &priv->state); + orinoco_unlock(priv, &flags); } static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw) @@ -1211,8 +1407,8 @@ static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw) { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ - DEBUG(1, "%s: MAC controller error (WTERR). Ignoring.\n", - priv->ndev->name); + printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", + priv->ndev->name); } static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) @@ -1235,8 +1431,6 @@ static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw) * The controller return to us a pseudo frame containing * the information in question - Jean II */ infofid = hermes_read_regn(hw, INFOFID); - DEBUG(3, "%s: __orinoco_ev_info(): INFOFID=0x%04x\n", dev->name, - infofid); /* Read the info frame header - don't try too hard */ err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), @@ -1277,11 +1471,6 @@ static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw) wstats->discard.retries += le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ -#if ORINOCO_DEBUG > 3 - /* Hack for debugging - should not be taken as an example */ - wstats->discard.nwid += le16_to_cpu(tallies.TxUnicastFrames); - wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames); -#endif #endif /* WIRELESS_EXT > 11 */ } break; @@ -1308,7 +1497,6 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) int err; rxfid = hermes_read_regn(hw, RXFID); - DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid); err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), rxfid, 0); @@ -1349,11 +1537,11 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) length = ntohs(hdr.len); /* Sanity checks */ - if (length < sizeof(struct header_struct)) { - printk(KERN_WARNING "%s: Undersized frame received (%d bytes)\n", - dev->name, length); - stats->rx_length_errors++; - stats->rx_errors++; + 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. */ + stats->rx_dropped++; goto drop; } if (length > IEEE802_11_DATA_LEN) { @@ -1373,7 +1561,6 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", dev->name); - stats->rx_dropped++; goto drop; } @@ -1383,8 +1570,7 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) * In most cases, the firmware tell us about SNAP frames. * For some reason, the SNAP frames sent by LinkSys APs * are not properly recognised by most firmwares. - * So, check ourselves (note : only 3 bytes out of 6). - */ + * So, check ourselves */ if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || is_snap(&hdr)) { @@ -1392,6 +1578,12 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) 802.11 frame which we'll need to de-encapsulate to the original EthernetII frame. */ + if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ + stats->rx_length_errors++; + stats->rx_dropped++; + goto drop; + } + /* Remove SNAP header, reconstruct EthernetII frame */ data_len = length - ENCAPS_OVERHEAD; data_off = HERMES_802_3_OFFSET + sizeof(hdr); @@ -1414,7 +1606,7 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len), rxfid, data_off); if (err) { - printk(KERN_ERR "%s: error %d reading frame header. " + printk(KERN_ERR "%s: error %d reading frame. " "Frame dropped.\n", dev->name, err); stats->rx_errors++; goto drop; @@ -1436,6 +1628,8 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw) return; drop: + stats->rx_dropped++; + if (skb) dev_kfree_skb_irq(skb); return; @@ -1469,11 +1663,7 @@ static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw) static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) { -/* struct net_device *dev = priv->ndev; */ struct net_device_stats *stats = &priv->stats; -/* u16 fid = hermes_read_regn(hw, TXCOMPLFID); */ - -/* DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev->name, fid); */ stats->tx_packets++; @@ -1485,8 +1675,6 @@ static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw) struct net_device *dev = priv->ndev; u16 fid = hermes_read_regn(hw, ALLOCFID); - DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev->name, fid); - if (fid != priv->txfid) { if (fid != DUMMY_FID) printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", @@ -1500,16 +1688,17 @@ static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw) } struct sta_id { - u16 id, vendor, major, minor; + u16 id, variant, major, minor; } __attribute__ ((packed)); static int determine_firmware_type(struct net_device *dev, struct sta_id *sta_id) { - u32 firmver = ((u32)sta_id->major << 16) | sta_id->minor; + /* FIXME: this is fundamentally broken */ + unsigned int firmver = ((u32)sta_id->major << 16) | sta_id->minor; - if (sta_id->vendor == 1) + if (sta_id->variant == 1) return FIRMWARE_TYPE_AGERE; - else if ((sta_id->vendor == 2) && + else if ((sta_id->variant == 2) && ((firmver == 0x10001) || (firmver == 0x20001))) return FIRMWARE_TYPE_SYMBOL; else @@ -1522,7 +1711,7 @@ static void determine_firmware(struct net_device *dev) hermes_t *hw = &priv->hw; int err; struct sta_id sta_id; - u32 firmver; + unsigned int firmver; char tmp[SYMBOL_MAX_VER_LEN+1]; /* Get the firmware version */ @@ -1533,14 +1722,12 @@ static void determine_firmware(struct net_device *dev) memset(&sta_id, 0, sizeof(sta_id)); } le16_to_cpus(&sta_id.id); - le16_to_cpus(&sta_id.vendor); + le16_to_cpus(&sta_id.variant); le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); - firmver = ((u32)sta_id.major << 16) | sta_id.minor; - printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", - dev->name, sta_id.id, sta_id.vendor, + dev->name, sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); if (! priv->firmware_type) @@ -1555,7 +1742,6 @@ static void determine_firmware(struct net_device *dev) priv->has_ibss_any = 0; priv->has_wep = 0; priv->has_big_wep = 0; - priv->broken_cor_reset = 0; /* Determine capabilities from the firmware version */ switch (priv->firmware_type) { @@ -1566,6 +1752,8 @@ static void determine_firmware(struct net_device *dev) "version %d.%02d\n", dev->name, sta_id.major, sta_id.minor); + firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; + priv->has_ibss = (firmver >= 0x60006); priv->has_ibss_any = (firmver >= 0x60010); priv->has_wep = (firmver >= 0x40020); @@ -1575,8 +1763,6 @@ static void determine_firmware(struct net_device *dev) priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->ibss_port = 1; - /* FIXME: Which firmware really do have a broken reset */ - priv->broken_cor_reset = (firmver < 0x60000); /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * Tested CableTron firmware : 4.32 => Anton */ @@ -1628,18 +1814,21 @@ static void determine_firmware(struct net_device *dev) /* D-Link MAC : 00:40:05:* */ /* Addtron MAC : 00:90:D1:* */ printk(KERN_DEBUG "%s: Looks like an Intersil firmware " - "version %d.%02d\n", dev->name, - sta_id.major, sta_id.minor); + "version %d.%d.%d\n", dev->name, + sta_id.major, sta_id.minor, sta_id.variant); - priv->has_ibss = (firmver >= 0x00007); /* FIXME */ - priv->has_big_wep = priv->has_wep = (firmver >= 0x00008); - priv->has_pm = (firmver >= 0x00007); + firmver = ((unsigned long)sta_id.major << 16) | + ((unsigned long)sta_id.minor << 8) | sta_id.variant; - if (firmver >= 0x00008) + priv->has_ibss = (firmver >= 0x000700); /* FIXME */ + priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); + priv->has_pm = (firmver >= 0x000700); + + if (firmver >= 0x000800) priv->ibss_port = 0; else { printk(KERN_NOTICE "%s: Intersil firmware earlier " - "than v0.08 - several features not supported\n", + "than v0.8.x - several features not supported\n", dev->name); priv->ibss_port = 1; } @@ -1663,16 +1852,16 @@ orinoco_init(struct net_device *dev) u16 reclen; int len; - TRACE_ENTER("orinoco"); - - orinoco_lock(priv); + TRACE_ENTER(dev->name); + /* No need to lock, the resetting flag is already set in + * alloc_orinocodev() */ priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; - /* Do standard firmware reset */ - err = hermes_reset(hw); + /* Initialize the firmware */ + err = hermes_init(hw); if (err != 0) { - printk(KERN_ERR "%s: failed to reset hardware (err = %d)\n", + printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n", dev->name, err); goto out; } @@ -1801,20 +1990,38 @@ orinoco_init(struct net_device *dev) priv->wep_on = 0; priv->tx_key = 0; - printk(KERN_DEBUG "%s: ready\n", dev->name); + priv->hw_unavailable = 0; - out: - orinoco_unlock(priv); + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO) { + /* Try workaround for old Symbol firmware bug */ + printk(KERN_WARNING "%s: firmware ALLOC bug detected " + "(old Symbol firmware?). Trying to work around... ", + dev->name); + + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err) + printk("failed!\n"); + else + printk("ok.\n"); + } + if (err) { + printk("%s: Error %d allocating Tx buffer\n", dev->name, err); + goto out; + } - TRACE_EXIT("orinoco"); + printk(KERN_DEBUG "%s: ready\n", dev->name); + out: + TRACE_EXIT(dev->name); return err; } struct net_device_stats * orinoco_get_stats(struct net_device *dev) { - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; + struct orinoco_private *priv = dev->priv; return &priv->stats; } @@ -1822,15 +2029,22 @@ orinoco_get_stats(struct net_device *dev) struct iw_statistics * orinoco_get_wireless_stats(struct net_device *dev) { - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; struct iw_statistics *wstats = &priv->wstats; int err = 0; + unsigned long flags; - if (! netif_device_present(dev)) - return NULL; /* FIXME: We may be able to do better than this */ + if (! netif_device_present(dev)) { + printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", + dev->name); + return NULL; /* FIXME: Can we do better than this? */ + } - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return NULL; /* FIXME: Erg, we've been signalled, how + * do we propagate this back up? */ if (priv->iw_mode == IW_MODE_ADHOC) { memset(&wstats->qual, 0, sizeof(wstats->qual)); @@ -1850,9 +2064,6 @@ orinoco_get_wireless_stats(struct net_device *dev) err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_COMMSQUALITY, &cq); - DEBUG(3, "%s: Global stats = %X-%X-%X\n", dev->name, - cq.qual, cq.signal, cq.noise); - wstats->qual.qual = (int)le16_to_cpu(cq.qual); wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; @@ -1862,11 +2073,11 @@ orinoco_get_wireless_stats(struct net_device *dev) /* We can't really wait for the tallies inquiry command to * complete, so we just use the previous results and trigger * a new tallies inquiry command for next time - Jean II */ - /* FIXME: Hmm.. seems a bit ugly, I wonder if there's a way to - do better - dgibson */ + /* FIXME: We're in user context (I think?), so we should just + wait for the tallies to come through */ err = hermes_inquire(hw, HERMES_INQ_TALLIES); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err) return NULL; @@ -1926,21 +2137,31 @@ orinoco_xmit(struct sk_buff *skb, struct net_device *dev) struct ethhdr *eh; int len, data_len, data_off; struct hermes_tx_descriptor desc; + unsigned long flags; + + TRACE_ENTER(dev->name); if (! netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); + TRACE_EXIT(dev->name); return 1; - } if (netif_queue_stopped(dev)) { - printk(KERN_ERR "%s: Tx while transmitter busy!\n", + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", dev->name); + TRACE_EXIT(dev->name); return 1; } - orinoco_lock(priv); + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", + dev->name); + TRACE_EXIT(dev->name); +/* BUG(); */ + return 1; + } /* Length of the packet body */ /* FIXME: what if the skb is smaller than this? */ @@ -2011,14 +2232,17 @@ orinoco_xmit(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; stats->tx_bytes += data_off + data_len; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); dev_kfree_skb(skb); + TRACE_EXIT(dev->name); + return 0; fail: + TRACE_EXIT(dev->name); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2028,27 +2252,21 @@ orinoco_tx_timeout(struct net_device *dev) struct orinoco_private *priv = (struct orinoco_private *)dev->priv; struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; - int err = 0; - printk(KERN_WARNING "%s: Tx timeout! Resetting card. ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", dev->name, hermes_read_regn(hw, ALLOCFID), hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); + printk(KERN_WARNING "%s: Tx timeout! " + "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", + dev->name, hermes_read_regn(hw, ALLOCFID), + hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); stats->tx_errors++; - err = orinoco_reset(priv); - if (err) - printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n", - dev->name, err); - else { - dev->trans_start = jiffies; - netif_wake_queue(dev); - } + schedule_task(&priv->timeout_task); } static int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = dev->priv; - TRACE_ENTER(dev->name); if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) return -EINVAL; @@ -2059,11 +2277,10 @@ orinoco_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; - TRACE_EXIT(dev->name); - return 0; } +/* FIXME: return int? */ static void __orinoco_set_multicast_list(struct net_device *dev) { @@ -2072,17 +2289,6 @@ __orinoco_set_multicast_list(struct net_device *dev) int err = 0; int promisc, mc_count; - /* We'll wait until it's ready. Anyway, the network doesn't call us - * here until we are open - Jean II */ - /* FIXME: do we need this test at all? */ - if (! netif_device_present(dev)) { - printk(KERN_WARNING "%s: __orinoco_set_multicast_list() called while device " - "not present.\n", dev->name); - return; - } - - TRACE_ENTER(dev->name); - /* The Hermes doesn't seem to have an allmulti mode, so we go * into promiscuous mode and let the upper levels deal. */ if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || @@ -2140,8 +2346,6 @@ __orinoco_set_multicast_list(struct net_device *dev) dev->flags |= IFF_PROMISC; else dev->flags &= ~IFF_PROMISC; - - TRACE_EXIT(dev->name); } /********************************************************************/ @@ -2156,6 +2360,7 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq struct iw_range range; int numrates; int i, k; + unsigned long flags; TRACE_ENTER(dev->name); @@ -2165,9 +2370,12 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq rrq->length = sizeof(range); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; + mode = priv->iw_mode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); memset(&range, 0, sizeof(range)); @@ -2240,7 +2448,9 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.min_frag = 256; range.max_frag = 2346; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_wep) { range.max_encoding_tokens = ORINOCO_MAX_KEYS; @@ -2255,7 +2465,7 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.num_encoding_sizes = 0; range.max_encoding_tokens = 0; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); range.min_pmp = 0; range.max_pmp = 65535000; @@ -2297,6 +2507,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er u16 xlen = 0; int err = 0; char keybuf[ORINOCO_MAX_KEY_SIZE]; + unsigned long flags; if (erq->pointer) { /* We actually have a key to set */ @@ -2307,7 +2518,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er return -EFAULT; } - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (erq->pointer) { if (erq->length > ORINOCO_MAX_KEY_SIZE) { @@ -2371,9 +2584,10 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er priv->tx_key = setindex; priv->wep_on = enable; priv->wep_restrict = restricted; + out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2384,9 +2598,12 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; char keybuf[ORINOCO_MAX_KEY_SIZE]; - + int err; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; @@ -2412,7 +2629,7 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (erq->pointer) { if (copy_to_user(erq->pointer, keybuf, xlen)) @@ -2426,6 +2643,8 @@ static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) { struct orinoco_private *priv = dev->priv; char essidbuf[IW_ESSID_MAX_SIZE+1]; + int err; + unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it * anyway... - Jean II */ @@ -2442,11 +2661,13 @@ static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) essidbuf[erq->length] = '\0'; } - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2457,6 +2678,7 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) char essidbuf[IW_ESSID_MAX_SIZE+1]; int active; int err = 0; + unsigned long flags; TRACE_ENTER(dev->name); @@ -2465,9 +2687,11 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) if (err) return err; } else { - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); } erq->flags = 1; @@ -2485,6 +2709,8 @@ static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) { struct orinoco_private *priv = dev->priv; char nickbuf[IW_ESSID_MAX_SIZE+1]; + int err; + unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) return -E2BIG; @@ -2496,11 +2722,13 @@ static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) nickbuf[nrq->length] = '\0'; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(priv->nick, nickbuf, sizeof(priv->nick)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2509,10 +2737,15 @@ static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) { struct orinoco_private *priv = dev->priv; char nickbuf[IW_ESSID_MAX_SIZE+1]; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); nrq->length = strlen(nickbuf)+1; @@ -2526,6 +2759,8 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) { struct orinoco_private *priv = dev->priv; int chan = -1; + int err; + unsigned long flags; /* We can only use this in Ad-Hoc demo mode to set the operating * frequency, or in IBSS mode to set the frequency where the IBSS @@ -2553,9 +2788,11 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) ! (priv->channel_mask & (1 << (chan-1)) ) ) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->channel = chan; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2566,13 +2803,16 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) hermes_t *hw = &priv->hw; u16 val; int err; + unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err) return err; @@ -2587,6 +2827,8 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) { struct orinoco_private *priv = dev->priv; int val = srq->value; + int err; + unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; @@ -2594,9 +2836,11 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) if ((val < 1) || (val > 3)) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->ap_density = val; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2605,6 +2849,8 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) { struct orinoco_private *priv = dev->priv; int val = rrq->value; + int err; + unsigned long flags; if (rrq->disabled) val = 2347; @@ -2612,9 +2858,12 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) if ( (val < 0) || (val > 2347) ) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; + priv->rts_thresh = val; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2623,8 +2872,11 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) { struct orinoco_private *priv = dev->priv; int err = 0; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_mwo) { if (frq->disabled) @@ -2646,7 +2898,7 @@ supported on this firmware. Using MWO robust instead.\n", dev->name); } } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2657,8 +2909,11 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) hermes_t *hw = &priv->hw; int err = 0; u16 val; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_mwo) { err = hermes_read_wordrec(hw, USER_BAP, @@ -2681,7 +2936,7 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) frq->fixed = 1; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2693,6 +2948,7 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) int ratemode = -1; int bitrate; /* 100s of kilobits */ int i; + unsigned long flags; /* As the user space doesn't know our highest rate, it uses -1 * to ask us to set the highest rate. Test it using "iwconfig @@ -2719,9 +2975,11 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) if (ratemode == -1) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->bitratemode = ratemode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2734,8 +2992,11 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) int ratemode; int i; u16 val; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; ratemode = priv->bitratemode; @@ -2785,7 +3046,7 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2794,9 +3055,11 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) { struct orinoco_private *priv = dev->priv; int err = 0; + unsigned long flags; - - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (prq->disabled) { priv->pm_on = 0; @@ -2836,7 +3099,7 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2847,8 +3110,11 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) hermes_t *hw = &priv->hw; int err = 0; u16 enable, period, timeout, mcast; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable); if (err) @@ -2882,7 +3148,7 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) prq->flags |= IW_POWER_UNICAST_R; out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2894,8 +3160,11 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) hermes_t *hw = &priv->hw; int err = 0; u16 short_limit, long_limit, lifetime; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, &short_limit); @@ -2932,7 +3201,7 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2942,14 +3211,19 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = dev->priv; int val = *( (int *) wrq->u.name ); + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); priv->ibss_port = val ; /* Actually update the mode we are using */ set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2957,10 +3231,15 @@ static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = dev->priv; int *val = (int *)wrq->u.name; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); *val = priv->ibss_port; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2970,15 +3249,18 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) struct orinoco_private *priv = dev->priv; int val = *( (int *) wrq->u.name ); int err = 0; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); switch (val) { case 0: /* Try to do IEEE ad-hoc mode */ if (! priv->has_ibss) { err = -EINVAL; break; } - DEBUG(2, "%s: Prefer IBSS Ad-Hoc mode\n", dev->name); priv->prefer_port3 = 0; break; @@ -2988,7 +3270,6 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) err = -EINVAL; break; } - DEBUG(2, "%s: Prefer Ad-Hoc demo mode\n", dev->name); priv->prefer_port3 = 1; break; @@ -3000,7 +3281,7 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) /* Actually update the mode we are using */ set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -3009,10 +3290,15 @@ static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = dev->priv; int *val = (int *)wrq->u.name; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); *val = priv->prefer_port3; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -3026,6 +3312,7 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) int number = srq->length; int i; int err = 0; + unsigned long flags; /* Check the number of addresses */ if (number > IW_MAX_SPY) @@ -3039,7 +3326,9 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) } /* Make sure nobody mess with the structure while we do */ - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; /* orinoco_lock() doesn't disable interrupts, so make sure the * interrupt rx path don't get confused while we copy */ @@ -3057,18 +3346,8 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) priv->spy_number = number; } - /* Time to show what we have done... */ - DEBUG(0, "%s: New spy list:\n", dev->name); - for (i = 0; i < number; i++) { - DEBUG(0, "%s: %d - %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->name, i+1, - priv->spy_address[i][0], priv->spy_address[i][1], - priv->spy_address[i][2], priv->spy_address[i][3], - priv->spy_address[i][4], priv->spy_address[i][5]); - } - /* Now, let the others play */ - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -3080,8 +3359,12 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) struct iw_quality spy_stat[IW_MAX_SPY]; int number; int i; + int err; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; number = priv->spy_number; if ((number > 0) && (srq->pointer)) { @@ -3101,7 +3384,7 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) priv->spy_stat[i].updated = 0; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); /* Push stuff to user space */ srq->length = number; @@ -3121,39 +3404,39 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) struct orinoco_private *priv = dev->priv; struct iwreq *wrq = (struct iwreq *)rq; int err = 0; + int tmp; int changed = 0; + unsigned long flags; TRACE_ENTER(dev->name); /* In theory, we could allow most of the the SET stuff to be - * done In practice, the laps of time at startup when the card - * is not ready is very short, so why bother... Note that - * netif_device_present is different from up/down (ifconfig), - * when the device is not yet up, it is usually already - * ready... Jean II */ + * done. In practice, the lapse of time at startup when the + * card is not ready is very short, so why bother... Note + * that netif_device_present is different from up/down + * (ifconfig), when the device is not yet up, it is usually + * already ready... Jean II */ if (! netif_device_present(dev)) return -ENODEV; switch (cmd) { case SIOCGIWNAME: - DEBUG(1, "%s: SIOCGIWNAME\n", dev->name); strcpy(wrq->u.name, "IEEE 802.11-DS"); break; case SIOCGIWAP: - DEBUG(1, "%s: SIOCGIWAP\n", dev->name); wrq->u.ap_addr.sa_family = ARPHRD_ETHER; err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data); break; case SIOCGIWRANGE: - DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name); err = orinoco_ioctl_getiwrange(dev, &wrq->u.data); break; case SIOCSIWMODE: - DEBUG(1, "%s: SIOCSIWMODE\n", dev->name); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; switch (wrq->u.mode) { case IW_MODE_ADHOC: if (! (priv->has_ibss || priv->has_port3) ) @@ -3174,18 +3457,18 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; } set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); break; case SIOCGIWMODE: - DEBUG(1, "%s: SIOCGIWMODE\n", dev->name); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; wrq->u.mode = priv->iw_mode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); break; case SIOCSIWENCODE: - DEBUG(1, "%s: SIOCSIWENCODE\n", dev->name); if (! priv->has_wep) { err = -EOPNOTSUPP; break; @@ -3197,7 +3480,6 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCGIWENCODE: - DEBUG(1, "%s: SIOCGIWENCODE\n", dev->name); if (! priv->has_wep) { err = -EOPNOTSUPP; break; @@ -3212,106 +3494,94 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCSIWESSID: - DEBUG(1, "%s: SIOCSIWESSID\n", dev->name); err = orinoco_ioctl_setessid(dev, &wrq->u.essid); if (! err) changed = 1; break; case SIOCGIWESSID: - DEBUG(1, "%s: SIOCGIWESSID\n", dev->name); err = orinoco_ioctl_getessid(dev, &wrq->u.essid); break; case SIOCSIWNICKN: - DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name); err = orinoco_ioctl_setnick(dev, &wrq->u.data); if (! err) changed = 1; break; case SIOCGIWNICKN: - DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name); err = orinoco_ioctl_getnick(dev, &wrq->u.data); break; case SIOCGIWFREQ: - DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name); - wrq->u.freq.m = orinoco_hw_get_freq(priv); - wrq->u.freq.e = 1; + tmp = orinoco_hw_get_freq(priv); + if (tmp < 0) { + err = tmp; + } else { + wrq->u.freq.m = tmp; + wrq->u.freq.e = 1; + } break; case SIOCSIWFREQ: - DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name); err = orinoco_ioctl_setfreq(dev, &wrq->u.freq); if (! err) changed = 1; break; case SIOCGIWSENS: - DEBUG(1, "%s: SIOCGIWSENS\n", dev->name); err = orinoco_ioctl_getsens(dev, &wrq->u.sens); break; case SIOCSIWSENS: - DEBUG(1, "%s: SIOCSIWSENS\n", dev->name); err = orinoco_ioctl_setsens(dev, &wrq->u.sens); if (! err) changed = 1; break; case SIOCGIWRTS: - DEBUG(1, "%s: SIOCGIWRTS\n", dev->name); wrq->u.rts.value = priv->rts_thresh; wrq->u.rts.disabled = (wrq->u.rts.value == 2347); wrq->u.rts.fixed = 1; break; case SIOCSIWRTS: - DEBUG(1, "%s: SIOCSIWRTS\n", dev->name); err = orinoco_ioctl_setrts(dev, &wrq->u.rts); if (! err) changed = 1; break; case SIOCSIWFRAG: - DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name); err = orinoco_ioctl_setfrag(dev, &wrq->u.frag); if (! err) changed = 1; break; case SIOCGIWFRAG: - DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name); err = orinoco_ioctl_getfrag(dev, &wrq->u.frag); break; case SIOCSIWRATE: - DEBUG(1, "%s: SIOCSIWRATE\n", dev->name); err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate); if (! err) changed = 1; break; case SIOCGIWRATE: - DEBUG(1, "%s: SIOCGIWRATE\n", dev->name); err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate); break; case SIOCSIWPOWER: - DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name); err = orinoco_ioctl_setpower(dev, &wrq->u.power); if (! err) changed = 1; break; case SIOCGIWPOWER: - DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name); err = orinoco_ioctl_getpower(dev, &wrq->u.power); break; case SIOCGIWTXPOW: - DEBUG(1, "%s: SIOCGIWTXPOW\n", dev->name); /* The card only supports one tx power, so this is easy */ wrq->u.txpower.value = 15; /* dBm */ wrq->u.txpower.fixed = 1; @@ -3321,30 +3591,23 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) #if WIRELESS_EXT > 10 case SIOCSIWRETRY: - DEBUG(1, "%s: SIOCSIWRETRY\n", dev->name); err = -EOPNOTSUPP; break; case SIOCGIWRETRY: - DEBUG(1, "%s: SIOCGIWRETRY\n", dev->name); err = orinoco_ioctl_getretry(dev, &wrq->u.retry); break; #endif /* WIRELESS_EXT > 10 */ case SIOCSIWSPY: - DEBUG(1, "%s: SIOCSIWSPY\n", dev->name); - err = orinoco_ioctl_setspy(dev, &wrq->u.data); break; case SIOCGIWSPY: - DEBUG(1, "%s: SIOCGIWSPY\n", dev->name); - err = orinoco_ioctl_getspy(dev, &wrq->u.data); break; case SIOCGIWPRIV: - DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name); if (wrq->u.data.pointer) { struct iw_priv_args privtab[] = { { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, @@ -3381,25 +3644,17 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) case SIOCIWFIRSTPRIV + 0x0: /* force_reset */ case SIOCIWFIRSTPRIV + 0x1: /* card_reset */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x0 (force_reset)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; } - printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); - /* We need the xmit lock because it protects the - multicast list which orinoco_reset() reads */ - spin_lock_bh(&dev->xmit_lock); - orinoco_reset(priv); - spin_unlock_bh(&dev->xmit_lock); + schedule_task(&priv->timeout_task); break; case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x2 (set_port3)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3411,14 +3666,10 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x3 (get_port3)\n", - dev->name); err = orinoco_ioctl_getport3(dev, wrq); break; case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3432,32 +3683,32 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if(priv->has_preamble) { int val = *( (int *) wrq->u.name ); - orinoco_lock(priv); - if(val) + err = orinoco_lock(priv, &flags); + if (err) + return err; + if (val) priv->preamble = 1; else priv->preamble = 0; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); changed = 1; } else err = -EOPNOTSUPP; break; case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n", - dev->name); if(priv->has_preamble) { int *val = (int *)wrq->u.name; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; *val = priv->preamble; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); } else err = -EOPNOTSUPP; break; case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3469,8 +3720,6 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x7 (get_ibssport)\n", - dev->name); err = orinoco_ioctl_getibssport(dev, wrq); break; @@ -3480,22 +3729,11 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } if (! err && changed && netif_running(dev)) { - /* We need the xmit lock because it protects the - multicast list which orinoco_reset() reads */ - spin_lock_bh(&dev->xmit_lock); - err = orinoco_reset(priv); - spin_unlock_bh(&dev->xmit_lock); - if (err) { - /* Ouch ! What are we supposed to do ? */ - printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n", - dev->name); - netif_device_detach(dev); - orinoco_shutdown(priv); - } + err = orinoco_reconfigure(priv); } TRACE_EXIT(dev->name); - + return err; } @@ -3833,8 +4071,6 @@ orinoco_proc_init(void) { int err = 0; - TRACE_ENTER("orinoco"); - /* create the directory for it to sit in */ dir_base = create_proc_entry("hermes", S_IFDIR, &proc_root); if (dir_base == NULL) { @@ -3843,15 +4079,13 @@ orinoco_proc_init(void) err = -ENOMEM; } - TRACE_EXIT("orinoco"); - return err; } int -orinoco_proc_dev_init(struct orinoco_private *priv) +orinoco_proc_dev_init(struct net_device *dev) { - struct net_device *dev = priv->ndev; + struct orinoco_private *priv = dev->priv; struct proc_dir_entry *e; priv->dir_dev = NULL; @@ -3889,16 +4123,14 @@ orinoco_proc_dev_init(struct orinoco_private *priv) return 0; fail: - orinoco_proc_dev_cleanup(priv); + orinoco_proc_dev_cleanup(dev); return -ENOMEM; } void -orinoco_proc_dev_cleanup(struct orinoco_private *priv) +orinoco_proc_dev_cleanup(struct net_device *dev) { - struct net_device *dev = priv->ndev; - - TRACE_ENTER(priv->ndev->name); + struct orinoco_private *priv = dev->priv; if (priv->dir_dev) { remove_proc_entry("prof", priv->dir_dev); @@ -3907,29 +4139,22 @@ orinoco_proc_dev_cleanup(struct orinoco_private *priv) remove_proc_entry(dev->name, dir_base); priv->dir_dev = NULL; } - - TRACE_EXIT(priv->ndev->name); } static void orinoco_proc_cleanup(void) { - TRACE_ENTER("orinoco"); - if (dir_base) { remove_proc_entry("hermes", &proc_root); dir_base = NULL; } - - TRACE_EXIT("orinoco"); } -struct net_device *alloc_orinocodev(int sizeof_card) +struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *)) { struct net_device *dev; struct orinoco_private *priv; - TRACE_ENTER("orinoco"); dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); priv = (struct orinoco_private *)dev->priv; priv->ndev = dev; @@ -3949,30 +4174,40 @@ struct net_device *alloc_orinocodev(int sizeof_card) dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; - dev->open = NULL; /* Caller *must* override these */ - dev->stop = NULL; - - /* Setup the private structure */ + /* Set up default callbacks */ + dev->open = orinoco_open; + dev->stop = orinoco_stop; + priv->hard_reset = hard_reset; spin_lock_init(&priv->lock); - priv->hard_reset = NULL; /* Caller may override */ - - TRACE_EXIT("orinoco"); + priv->open = 0; + priv->hw_unavailable = 1; /* orinoco_init() must clear this + * before anything else touches the + * hardware */ + INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev); + return dev; } /********************************************************************/ -/* module bookkeeping */ +/* Module initialization */ /********************************************************************/ EXPORT_SYMBOL(alloc_orinocodev); -EXPORT_SYMBOL(orinoco_shutdown); -EXPORT_SYMBOL(orinoco_reset); + +EXPORT_SYMBOL(__orinoco_up); +EXPORT_SYMBOL(__orinoco_down); +EXPORT_SYMBOL(orinoco_reinit_firmware); + EXPORT_SYMBOL(orinoco_proc_dev_init); EXPORT_SYMBOL(orinoco_proc_dev_cleanup); EXPORT_SYMBOL(orinoco_interrupt); +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = "orinoco.c 0.13 (David Gibson <hermes@gibson.dropbear.id.au> and others)"; + static int __init init_orinoco(void) { printk(KERN_DEBUG "%s\n", version); diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index d9e857954e6cc62fbfc666a21bc674ace636f5dd..019b2fcbbaf9fd774228b881c10c365594c940ef 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -7,6 +7,8 @@ #ifndef _ORINOCO_H #define _ORINOCO_H +#include "hermes.h" + /* To enable debug messages */ //#define ORINOCO_DEBUG 3 @@ -18,24 +20,26 @@ #define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEYS 4 -typedef struct orinoco_key { - u16 len; /* always store little-endian */ +struct orinoco_key { + u16 len; /* always stored as little-endian */ char data[ORINOCO_MAX_KEY_SIZE]; -} __attribute__ ((packed)) orinoco_key_t; +} __attribute__ ((packed)); -typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS]; +#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC | HERMES_EV_TX | \ + HERMES_EV_TXEXC | HERMES_EV_WTERR | HERMES_EV_INFO | \ + HERMES_EV_INFDROP ) -/*====================================================================*/ struct orinoco_private { void *card; /* Pointer to card dependant structure */ - /* card dependant extra reset code (i.e. bus/interface specific */ int (*hard_reset)(struct orinoco_private *); + /* Synchronisation stuff */ spinlock_t lock; - long state; -#define ORINOCO_STATE_INIRQ 0 -#define ORINOCO_STATE_DOIRQ 1 + int hw_unavailable; + struct tq_struct timeout_task; + + int open; /* Net device stuff */ struct net_device *ndev; @@ -58,14 +62,13 @@ struct orinoco_private { int has_preamble; int has_sensitivity; int nicbuf_size; - int broken_cor_reset; u16 channel_mask; /* Configuration paramaters */ u32 iw_mode; int prefer_port3; u16 wep_on, wep_restrict, tx_key; - orinoco_keys_t keys; + struct orinoco_key keys[ORINOCO_MAX_KEYS]; int bitratemode; char nick[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1]; @@ -81,38 +84,58 @@ struct orinoco_private { #endif /* Configuration dependent variables */ - int port_type, allow_ibss; + int port_type, createibss; int promiscuous, mc_count; - /* /proc based debugging stuff */ struct proc_dir_entry *dir_dev; }; -/*====================================================================*/ - -extern struct list_head orinoco_instances; - #ifdef ORINOCO_DEBUG extern int orinoco_debug; #define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0) -#define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0) #else #define DEBUG(n, args...) do { } while (0) -#define DEBUGMORE(n, args...) do { } while (0) #endif /* ORINOCO_DEBUG */ #define TRACE_ENTER(devname) DEBUG(2, "%s: -> " __FUNCTION__ "()\n", devname); #define TRACE_EXIT(devname) DEBUG(2, "%s: <- " __FUNCTION__ "()\n", devname); -#define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) ) +extern struct net_device *alloc_orinocodev(int sizeof_card, + int (*hard_reset)(struct orinoco_private *)); +extern int __orinoco_up(struct net_device *dev); +extern int __orinoco_down(struct net_device *dev); +int orinoco_reinit_firmware(struct net_device *dev); -/* utility routines */ -struct net_device *alloc_orinocodev(int sizeof_card); -extern void orinoco_shutdown(struct orinoco_private *dev); -extern int orinoco_reset(struct orinoco_private *dev); -extern int orinoco_proc_dev_init(struct orinoco_private *dev); -extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv); +extern int orinoco_proc_dev_init(struct net_device *dev); +extern void orinoco_proc_dev_cleanup(struct net_device *dev); extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); +/********************************************************************/ +/* Locking and synchronization functions */ +/********************************************************************/ + +/* These functions *must* be inline or they will break horribly on + * SPARC, due to its weird semantics for save/restore flags. extern + * inline should prevent the kernel from linking or module from + * loading if they are not inlined. */ +extern inline int orinoco_lock(struct orinoco_private *priv, + unsigned long *flags) +{ + spin_lock_irqsave(&priv->lock, *flags); + if (priv->hw_unavailable) { + printk(KERN_DEBUG "orinoco_lock() called with hw_unavailable (dev=%p)\n", + priv->ndev); + spin_unlock_irqrestore(&priv->lock, *flags); + return -EBUSY; + } + return 0; +} + +extern inline void orinoco_unlock(struct orinoco_private *priv, + unsigned long *flags) +{ + spin_unlock_irqrestore(&priv->lock, *flags); +} + #endif /* _ORINOCO_H */ diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index ea7e74e4869d491ddaaeeaf2d32ca7cc0042bdc0..33e369c04c63af8670602e275f8bd00564f324f2 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.11b - (formerly known as dldwd_cs.c) +/* orinoco_cs.c 0.13 - (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -32,7 +32,6 @@ #include <linux/if_arp.h> #include <linux/etherdevice.h> #include <linux/wireless.h> -#include <linux/list.h> #include <pcmcia/version.h> #include <pcmcia/cs_types.h> @@ -42,12 +41,11 @@ #include <pcmcia/ds.h> #include <pcmcia/bus_ops.h> -#include "hermes.h" #include "orinoco.h" -/*====================================================================*/ - -static char version[] __initdata = "orinoco_cs.c 0.11b (David Gibson <hermes@gibson.dropbear.id.au> and others)"; +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>"); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards"); @@ -55,235 +53,131 @@ MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar MODULE_LICENSE("Dual MPL/GPL"); #endif -/* Parameters that can be set with 'insmod' */ +/* Module parameters */ /* The old way: bit map of interrupts to choose from */ /* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ static uint irq_mask = 0xdeb8; /* Newer, simpler way of listing specific interrupts */ static int irq_list[4] = { -1 }; -/* Do a Pcmcia soft reset (may help some cards) */ -static int reset_cor = -1; + /* Some D-Link cards have buggy CIS. They do work at 5v properly, but * don't have any CIS entry for it. This workaround it... */ static int ignore_cis_vcc; /* = 0 */ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); -MODULE_PARM(reset_cor, "i"); MODULE_PARM(ignore_cis_vcc, "i"); -/* Pcmcia specific structure */ +/********************************************************************/ +/* Magic constants */ +/********************************************************************/ + +/* + * The dev_info variable is the "key" that is used to match up this + * device driver with appropriate cards, through the card + * configuration database. + */ +static dev_info_t dev_info = "orinoco_cs"; + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +/* PCMCIA specific device information (goes in the card field of + * struct orinoco_private */ struct orinoco_pccard { dev_link_t link; dev_node_t node; + + /* Used to handle hard reset */ + wait_queue_head_t hard_reset_queue; + int hard_reset_flag; }; /* - * Function prototypes + * A linked list of "instances" of the device. Each actual PCMCIA + * card corresponds to one device instance, and is described by one + * dev_link_t structure (defined in ds.h). */ +static dev_link_t *dev_list; /* = NULL */ -/* struct net_device methods */ -static int orinoco_cs_open(struct net_device *dev); -static int orinoco_cs_stop(struct net_device *dev); +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ + +/* device methods */ +static int orinoco_cs_hard_reset(struct orinoco_private *priv); /* PCMCIA gumpf */ static void orinoco_cs_config(dev_link_t * link); static void orinoco_cs_release(u_long arg); static int orinoco_cs_event(event_t event, int priority, - event_callback_args_t * args); + event_callback_args_t * args); static dev_link_t *orinoco_cs_attach(void); static void orinoco_cs_detach(dev_link_t *); -/* - The dev_info variable is the "key" that is used to match up this - device driver with appropriate cards, through the card configuration - database. -*/ -static dev_info_t dev_info = "orinoco_cs"; - -/* - A linked list of "instances" of the dummy device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -static dev_link_t *dev_list; /* = NULL */ - -/*====================================================================*/ - -static void -cs_error(client_handle_t handle, int func, int ret) -{ - error_info_t err = { func, ret }; - CardServices(ReportError, handle, &err); -} +/********************************************************************/ +/* Device methods */ +/********************************************************************/ static int -orinoco_cs_open(struct net_device *dev) +orinoco_cs_hard_reset(struct orinoco_private *priv) { - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; + struct orinoco_pccard *card = priv->card; dev_link_t *link = &card->link; int err; - - TRACE_ENTER(dev->name); - link->open++; - netif_device_attach(dev); - - err = orinoco_reset(priv); - if (err) - orinoco_cs_stop(dev); - else - netif_start_queue(dev); - - TRACE_EXIT(dev->name); + card->hard_reset_flag = 0; - return err; -} - -static int -orinoco_cs_stop(struct net_device *dev) -{ - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; - dev_link_t *link = &card->link; - - TRACE_ENTER(dev->name); + err = CardServices(ResetCard, link->handle, NULL); + if (err) + return err; - netif_stop_queue(dev); + wait_event_interruptible(card->hard_reset_queue, + card->hard_reset_flag); - orinoco_shutdown(priv); - - link->open--; - - if (link->state & DEV_STALE_CONFIG) - mod_timer(&link->release, jiffies + HZ/20); - - TRACE_EXIT(dev->name); - return 0; } -/* - * Do a soft reset of the Pcmcia card using the Configuration Option Register - * Can't do any harm, and actually may do some good on some cards... - * In fact, this seem necessary for Spectrum cards... - */ -static int -orinoco_cs_cor_reset(struct orinoco_private *priv) -{ - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; - dev_link_t *link = &card->link; - conf_reg_t reg; - u_int default_cor; - - TRACE_ENTER(priv->ndev->name); - - /* Doing it if hardware is gone is guaranteed crash */ - if(! (link->state & DEV_CONFIG) ) - return -ENODEV; - - /* Save original COR value */ - reg.Function = 0; - reg.Action = CS_READ; - reg.Offset = CISREG_COR; - reg.Value = 0; - CardServices(AccessConfigurationRegister, link->handle, ®); - default_cor = reg.Value; - - DEBUG(2, "orinoco : orinoco_cs_cor_reset() : cor=0x%X\n", default_cor); - - /* Soft-Reset card */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_COR; - reg.Value = (default_cor | COR_SOFT_RESET); - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Wait until the card has acknowledged our reset */ - /* FIXME: mdelay() is deprecated -dgibson */ - mdelay(1); - -#if 0 /* This seems to help on Symbol cards, but we're not sure why, - and we don't know what it will do to other cards */ - reg.Action = CS_READ; - reg.Offset = CISREG_CCSR; - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Write 7 (RUN) to CCSR, but preserve the original bit 4 */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_CCSR; - reg.Value = 7 | (reg.Value & 0x10); - CardServices(AccessConfigurationRegister, link->handle, ®); - mdelay(1); -#endif - - /* Restore original COR configuration index */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_COR; - reg.Value = (default_cor & ~COR_SOFT_RESET); - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Wait until the card has finished restarting */ - /* FIXME: mdelay() is deprecated -dgibson */ - mdelay(1); - - TRACE_EXIT(priv->ndev->name); +/********************************************************************/ +/* PCMCIA stuff */ +/********************************************************************/ - return 0; -} - -static int -orinoco_cs_hard_reset(struct orinoco_private *priv) +static void +cs_error(client_handle_t handle, int func, int ret) { - if (! priv->broken_cor_reset) - return orinoco_cs_cor_reset(priv); - else - return 0; - -#if 0 /* We'd like to use ResetCard, but we can't for the moment - it sleeps */ - /* Not sure what the second parameter is supposed to be - the - PCMCIA code doesn't actually use it */ - if (in_interrupt()) { - printk("Not resetting card, in_interrupt() is true\n"); - return 0; - } else { - printk("Doing ResetCard\n"); - return CardServices(ResetCard, link->handle, NULL); - } -#endif + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); } + /* Remove zombie instances (card removed, detach pending) */ static void flush_stale_links(void) { dev_link_t *link, *next; - TRACE_ENTER("orinoco"); + + TRACE_ENTER(""); + for (link = dev_list; link; link = next) { next = link->next; - if (link->state & DEV_STALE_LINK) + if (link->state & DEV_STALE_LINK) { orinoco_cs_detach(link); + } } - TRACE_EXIT("orinoco"); + TRACE_EXIT(""); } -/*====================================================================== - orinoco_cs_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. - - The dev_link structure is initialized, but we don't actually - configure the card at this point -- we wait until we receive a - card insertion event. - ======================================================================*/ - +/* + * This creates an "instance" of the driver, allocating local data + * structures for one device. The device is registered with Card + * Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a card + * insertion event. */ static dev_link_t * orinoco_cs_attach(void) { @@ -294,23 +188,19 @@ orinoco_cs_attach(void) client_reg_t client_reg; int ret, i; - TRACE_ENTER("orinoco"); /* A bit of cleanup */ flush_stale_links(); - dev = alloc_orinocodev(sizeof(*card)); + dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset); if (! dev) return NULL; priv = dev->priv; card = priv->card; - /* Overrides */ - dev->open = orinoco_cs_open; - dev->stop = orinoco_cs_stop; - priv->hard_reset = orinoco_cs_hard_reset; + init_waitqueue_head(&card->hard_reset_queue); /* Link both structures together */ link = &card->link; - link->priv = priv; + link->priv = dev; /* Initialize the dev_link_t structure */ link->release.function = &orinoco_cs_release; @@ -326,63 +216,59 @@ orinoco_cs_attach(void) link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = NULL; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ + /* General socket configuration defaults can go here. In this + * client, we assume very little, and rely on the CIS for + * almost everything. In most clients, many details (i.e., + * number, sizes, and attributes of IO windows) are fixed by + * the nature of the device, and can be hard-wired here. */ link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; /* Register with Card Services */ + /* FIXME: need a lock? */ link->next = dev_list; dev_list = link; + client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = - CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | - CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | - CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &orinoco_cs_event; - client_reg.Version = 0x0210; + client_reg.Version = 0x0210; /* FIXME: what does this mean? */ client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != CS_SUCCESS) { cs_error(link->handle, RegisterClient, ret); orinoco_cs_detach(link); - link = NULL; - goto out; + return NULL; } - out: - TRACE_EXIT("orinoco"); return link; } /* orinoco_cs_attach */ -/*====================================================================== - This deletes a driver "instance". The device is de-registered - with Card Services. If it has been released, all local data - structures are freed. Otherwise, the structures will be freed - when the device is released. - ======================================================================*/ - +/* + * This deletes a driver "instance". The device is de-registered with + * Card Services. If it has been released, all local data structures + * are freed. Otherwise, the structures will be freed when the device + * is released. + */ static void orinoco_cs_detach(dev_link_t * link) { dev_link_t **linkp; - struct orinoco_private *priv = link->priv; - struct net_device *dev = priv->ndev; - - TRACE_ENTER("orinoco"); + struct net_device *dev = link->priv; /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; - if (*linkp == NULL) - goto out; + if (*linkp == NULL) { + BUG(); + return; + } /* If the device is currently configured and active, we won't @@ -396,7 +282,7 @@ orinoco_cs_detach(dev_link_t * link) "still locked\n", link->dev->dev_name); #endif link->state |= DEV_STALE_LINK; - goto out; + return; } /* Break the link with Card Services */ @@ -408,50 +294,44 @@ orinoco_cs_detach(dev_link_t * link) DEBUG(0, "orinoco_cs: detach: link=%p link->dev=%p\n", link, link->dev); if (link->dev) { DEBUG(0, "orinoco_cs: About to unregister net device %p\n", - priv->ndev); + dev); unregister_netdev(dev); } kfree(dev); - - out: - TRACE_EXIT("orinoco"); } /* orinoco_cs_detach */ -/*====================================================================== - orinoco_cs_config() is scheduled to run after a CARD_INSERTION event - is received, to configure the PCMCIA socket, and to make the - device available to the system. - ======================================================================*/ +/* + * orinoco_cs_config() is scheduled to run after a CARD_INSERTION + * event is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ #define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed + while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed #define CFG_CHECK(fn, args...) \ -if (CardServices(fn, args) != 0) goto next_entry + if (CardServices(fn, args) != 0) goto next_entry static void -orinoco_cs_config(dev_link_t * link) +orinoco_cs_config(dev_link_t *link) { + struct net_device *dev = link->priv; client_handle_t handle = link->handle; - struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card; + struct orinoco_private *priv = dev->priv; + struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; - struct net_device *ndev = priv->ndev; - tuple_t tuple; - cisparse_t parse; int last_fn, last_ret; u_char buf[64]; config_info_t conf; - cistpl_cftable_entry_t dflt = { 0 }; cisinfo_t info; - - TRACE_ENTER("orinoco"); + tuple_t tuple; + cisparse_t parse; CS_CHECK(ValidateCIS, handle, &info); /* - This reads the card's CONFIG tuple to find its configuration - registers. + * This reads the card's CONFIG tuple to find its + * configuration registers. */ tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; @@ -471,31 +351,29 @@ orinoco_cs_config(dev_link_t * link) CS_CHECK(GetConfigurationInfo, handle, &conf); link->conf.Vcc = conf.Vcc; - DEBUG(0, "orinoco_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", - link->conf.ConfigBase, link->conf.Vcc); - /* - In this loop, we scan the CIS for configuration table entries, - each of which describes a valid card configuration, including - voltage, IO window, memory window, and interrupt settings. - - We make no assumptions about the card to be configured: we use - just the information available in the CIS. In an ideal world, - this would work for any PCMCIA card, but it requires a complete - and accurate CIS. In practice, a driver usually "knows" most of - these things without consulting the CIS, and most client drivers - will only use the CIS to fill in implementation-defined details. + * In this loop, we scan the CIS for configuration table + * entries, each of which describes a valid card + * configuration, including voltage, IO window, memory window, + * and interrupt settings. + * + * We make no assumptions about the card to be configured: we + * use just the information available in the CIS. In an ideal + * world, this would work for any PCMCIA card, but it requires + * a complete and accurate CIS. In practice, a driver usually + * "knows" most of these things without consulting the CIS, + * and most client drivers will only use the CIS to fill in + * implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + cistpl_cftable_entry_t dflt = { index: 0 }; + CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); - DEBUG(0, "orinoco_cs_config: index = 0x%x, flags = 0x%x\n", - cfg->index, cfg->flags); - if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) @@ -509,7 +387,7 @@ orinoco_cs_config(dev_link_t * link) } /* Use power settings for Vcc and Vpp if present */ - /* Note that the CIS values need to be rescaled */ + /* Note that the CIS values need to be rescaled */ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); @@ -531,8 +409,6 @@ orinoco_cs_config(dev_link_t * link) link->conf.Vpp1 = link->conf.Vpp2 = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; - DEBUG(0, "orinoco_cs_config: We seem to have configured Vcc and Vpp\n"); - /* Do we need to allocate an interrupt? */ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) link->conf.Attributes |= CONF_ENABLE_IRQ; @@ -581,9 +457,9 @@ orinoco_cs_config(dev_link_t * link) } /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. + * Allocate an interrupt line. Note that this does not assign + * a handler to the interrupt, unless the 'Handler' member of + * the irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) { int i; @@ -603,33 +479,43 @@ orinoco_cs_config(dev_link_t * link) } /* We initialize the hermes structure before completing PCMCIA - configuration just in case the interrupt handler gets - called. */ + * configuration just in case the interrupt handler gets + * called. */ hermes_struct_init(hw, link->io.BasePort1, HERMES_IO, HERMES_16BIT_REGSPACING); /* - This actually configures the PCMCIA socket -- setting up - the I/O windows and the interrupt mapping, and putting the - card and host interface into "Memory and IO" mode. + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping, and putting the + * card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, link->handle, &link->conf); - ndev->base_addr = link->io.BasePort1; - ndev->irq = link->irq.AssignedIRQ; + /* Ok, we have the configuration, prepare to register the netdev */ + dev->base_addr = link->io.BasePort1; + dev->irq = link->irq.AssignedIRQ; + SET_MODULE_OWNER(dev); + card->node.major = card->node.minor = 0; /* register_netdev will give us an ethX name */ - ndev->name[0] = '\0'; + dev->name[0] = '\0'; /* Tell the stack we exist */ - if (register_netdev(ndev) != 0) { + if (register_netdev(dev) != 0) { printk(KERN_ERR "orinoco_cs: register_netdev() failed\n"); goto failed; } - strcpy(card->node.dev_name, ndev->name); + + /* At this point, the dev_node_t structure(s) needs to be + * initialized and arranged in a linked list at link->dev. */ + strcpy(card->node.dev_name, dev->name); + link->dev = &card->node; /* link->dev being non-NULL is also + used to indicate that the + net_device has been registered */ + link->state &= ~DEV_CONFIG_PENDING; /* Finally, report what we've done */ printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d", - ndev->name, link->conf.ConfigIndex, + dev->name, link->conf.ConfigIndex, link->conf.Vcc / 10, link->conf.Vcc % 10); if (link->conf.Vpp1) printk(", Vpp %d.%d", link->conf.Vpp1 / 10, @@ -645,59 +531,40 @@ orinoco_cs_config(dev_link_t * link) printk("\n"); /* And give us the proc nodes for debugging */ - if (orinoco_proc_dev_init(priv) != 0) { + if (orinoco_proc_dev_init(dev) != 0) { printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n", - ndev->name); + dev->name); goto failed; } - /* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */ - SET_MODULE_OWNER(ndev); - - /* Let reset_cor parameter override determine_firmware()'s guess */ - if (reset_cor != -1) - priv->broken_cor_reset = ! reset_cor; - - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - card->node.major = card->node.minor = 0; - link->dev = &card->node; - link->state &= ~DEV_CONFIG_PENDING; - - TRACE_EXIT("orinoco"); return; cs_failed: cs_error(link->handle, last_fn, last_ret); + failed: orinoco_cs_release((u_long) link); - - TRACE_EXIT("orinoco"); } /* orinoco_cs_config */ -/*====================================================================== - After a card is removed, orinoco_cs_release() will unregister the - device, and release the PCMCIA configuration. If the device is - still open, this will be postponed until it is closed. - ======================================================================*/ - +/* + * After a card is removed, orinoco_cs_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ static void orinoco_cs_release(u_long arg) { dev_link_t *link = (dev_link_t *) arg; - struct orinoco_private *priv = link->priv; - - TRACE_ENTER(link->dev->dev_name); + struct net_device *dev = link->priv; + struct orinoco_private *priv = dev->priv; /* If the device is currently in use, we won't release until it is actually closed, because until then, we can't be sure that no one will try to access the device or its data structures. */ - if (link->open) { + if (priv->open) { DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; @@ -705,7 +572,7 @@ orinoco_cs_release(u_long arg) } /* Unregister proc entry */ - orinoco_proc_dev_cleanup(priv); + orinoco_proc_dev_cleanup(dev); /* Don't bother checking to see if these succeed or not */ CardServices(ReleaseConfiguration, link->handle); @@ -714,62 +581,74 @@ orinoco_cs_release(u_long arg) if (link->irq.AssignedIRQ) CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG; - - TRACE_EXIT(link->dev->dev_name); } /* orinoco_cs_release */ -/*====================================================================== - The card status event handler. Mostly, this schedules other - stuff to run after an event is received. - - When a CARD_REMOVAL event is received, we immediately set a - private flag to block future accesses to this device. All the - functions that actually access the device should check this flag - to make sure the card is still present. - ======================================================================*/ - +/* + * The card status event handler. Mostly, this schedules other stuff + * to run after an event is received. + */ static int orinoco_cs_event(event_t event, int priority, event_callback_args_t * args) { dev_link_t *link = args->client_data; - struct orinoco_private *priv = (struct orinoco_private *)link->priv; - struct net_device *dev = priv->ndev; - - TRACE_ENTER("orinoco"); + struct net_device *dev = link->priv; + struct orinoco_private *priv = dev->priv; + int err = 0; + unsigned long flags; switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { - netif_stop_queue(dev); - } - orinoco_shutdown(priv); - if (link->state & DEV_CONFIG) { + orinoco_lock(priv, &flags); + netif_device_detach(dev); + priv->hw_unavailable = 1; + + orinoco_unlock(priv, &flags); + +/* if (link->open) */ +/* orinoco_cs_stop(dev); */ + mod_timer(&link->release, jiffies + HZ / 20); } break; + case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; orinoco_cs_config(link); break; - case CS_EVENT_PM_SUSPEND: + case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: - orinoco_shutdown(priv); /* Mark the device as stopped, to block IO until later */ - if (link->state & DEV_CONFIG) { - if (link->open) { - netif_stop_queue(dev); - netif_device_detach(dev); + err = orinoco_lock(priv, &flags); + if (err) { + printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n", + dev->name); + break; } + + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: %s: Error %d downing interface\n", + dev->name, + event == CS_EVENT_PM_SUSPEND ? "SUSPEND" : "RESET_PHYSICAL", + err); + + netif_device_detach(dev); + priv->hw_unavailable = 1; + + orinoco_unlock(priv, &flags); + CardServices(ReleaseConfiguration, link->handle); } break; + case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ @@ -778,36 +657,49 @@ orinoco_cs_event(event_t event, int priority, CardServices(RequestConfiguration, link->handle, &link->conf); - if (link->open) { - if (orinoco_reset(priv) == 0) { - netif_device_attach(dev); - netif_start_queue(dev); - } else { - printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n", - dev->name); - orinoco_cs_stop(dev); - } + /* FIXME: should we double check that this is + * the same card as we had before */ + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware\n", + dev->name, err); + break; } + + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); + priv->hw_unavailable = 0; + + if (priv->open) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", + dev->name, err); + + } + + orinoco_unlock(priv, &flags); } - /* - In a normal driver, additional code may go here to restore - the device state and restart IO. - */ break; } - TRACE_EXIT("orinoco"); - - return 0; + return err; } /* orinoco_cs_event */ +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = "orinoco_cs.c 0.13 (David Gibson <hermes@gibson.dropbear.id.au> and others)"; + static int __init init_orinoco_cs(void) { servinfo_t serv; - TRACE_ENTER("orinoco"); - printk(KERN_DEBUG "%s\n", version); CardServices(GetCardServicesInfo, &serv); @@ -819,16 +711,12 @@ init_orinoco_cs(void) register_pccard_driver(&dev_info, &orinoco_cs_attach, &orinoco_cs_detach); - - TRACE_EXIT("orinoco"); return 0; } static void __exit exit_orinoco_cs(void) { - TRACE_ENTER("orinoco"); - unregister_pccard_driver(&dev_info); if (dev_list) @@ -839,9 +727,8 @@ exit_orinoco_cs(void) orinoco_cs_release((u_long) dev_list); orinoco_cs_detach(dev_list); } - - TRACE_EXIT("orinoco"); } module_init(init_orinoco_cs); module_exit(exit_orinoco_cs); + diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c index a1ea17a2a40c2089e66aab93413a7559ad15cd2e..849758e64331f188ed860f882cfc08bf850d63d6 100644 --- a/drivers/net/wireless/orinoco_plx.c +++ b/drivers/net/wireless/orinoco_plx.c @@ -1,4 +1,4 @@ -/* orinoco_plx.c 0.11b +/* orinoco_plx.c 0.13 * * Driver for Prism II devices which would usually be driven by orinoco_cs, * but are connected to the PCI bus by a PLX9052. @@ -134,13 +134,6 @@ not have time for a while.. #include "hermes.h" #include "orinoco.h" -static char version[] __initdata = "orinoco_plx.c 0.11b (Daniel Barlow <dan@telent.net>)"; -MODULE_AUTHOR("Daniel Barlow <dan@telent.net>"); -MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); -#ifdef MODULE_LICENSE -MODULE_LICENSE("Dual MPL/GPL"); -#endif - static char dev_info[] = "orinoco_plx"; #define COR_OFFSET (0x3e0 / 2) /* COR attribute offset of Prism2 PC card */ @@ -149,37 +142,6 @@ static char dev_info[] = "orinoco_plx"; #define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */ #define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */ -static int orinoco_plx_open(struct net_device *dev) -{ - struct orinoco_private *priv = (struct orinoco_private *) dev->priv; - int err; - - netif_device_attach(dev); - - err = orinoco_reset(priv); - if (err) - printk(KERN_ERR "%s: orinoco_reset failed in orinoco_plx_open()", - dev->name); - else - netif_start_queue(dev); - - return err; -} - -static int orinoco_plx_stop(struct net_device *dev) -{ - struct orinoco_private *priv = (struct orinoco_private *) dev->priv; - netif_stop_queue(dev); - orinoco_shutdown(priv); - return 0; -} - -static void -orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs); -} - static const u16 cis_magic[] = { 0x0001, 0x0003, 0x0000, 0x0000, 0x00ff, 0x0017, 0x0004, 0x0067 }; @@ -197,8 +159,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, int netdev_registered = 0; int i; - TRACE_ENTER("orinoco_plx"); - err = pci_enable_device(pdev); if (err) return -EIO; @@ -266,7 +226,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, goto fail; } - dev = alloc_orinocodev(0); + dev = alloc_orinocodev(0, NULL); if (! dev) { err = -ENOMEM; goto fail; @@ -274,8 +234,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, priv = dev->priv; dev->base_addr = pccard_ioaddr; - dev->open = orinoco_plx_open; - dev->stop = orinoco_plx_stop; SET_MODULE_OWNER(dev); printk(KERN_DEBUG @@ -286,7 +244,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, HERMES_IO, HERMES_16BIT_REGSPACING); pci_set_drvdata(pdev, dev); - err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv); + err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv); if (err) { printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq); err = -EBUSY; @@ -299,19 +257,17 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, goto fail; netdev_registered = 1; - err = orinoco_proc_dev_init(priv); + err = orinoco_proc_dev_init(dev); if (err) goto fail; - TRACE_EXIT("orinoco_plx"); - return 0; /* succeeded */ fail: printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n"); if (priv) { - orinoco_proc_dev_cleanup(priv); + orinoco_proc_dev_cleanup(dev); if (netdev_registered) unregister_netdev(dev); @@ -330,8 +286,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, pci_disable_device(pdev); - TRACE_EXIT("orinoco_plx"); - return err; } @@ -340,12 +294,10 @@ static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct orinoco_private *priv = dev->priv; - TRACE_ENTER("orinoco_plx"); - if (! dev) BUG(); - orinoco_proc_dev_cleanup(priv); + orinoco_proc_dev_cleanup(dev); unregister_netdev(dev); @@ -357,13 +309,10 @@ static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3)); pci_disable_device(pdev); - - TRACE_EXIT("orinoco_plx"); } static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = { - {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3ComAirConnect */ {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ @@ -376,7 +325,8 @@ static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = { {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by Brendan W. McAdams <rit@jacked-in.org> */ - {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,}, /* Nortel emobility */ + {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by + Damien Persohn <damien@persohn.net> */ {0,}, }; @@ -387,8 +337,17 @@ static struct pci_driver orinoco_plx_driver = { .id_table = orinoco_plx_pci_id_table, .probe = orinoco_plx_init_one, .remove = __devexit_p(orinoco_plx_remove_one), + .suspend = 0, + .resume = 0, }; +static char version[] __initdata = "orinoco_plx.c 0.13 (Daniel Barlow <dan@telent.net>, David Gibson <hermes@gibson.dropbear.id.au>)"; +MODULE_AUTHOR("Daniel Barlow <dan@telent.net>"); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual MPL/GPL"); +#endif + static int __init orinoco_plx_init(void) { printk(KERN_DEBUG "%s\n", version); diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c index c493d2f0216f5eb3251bd2f78b46971a85e2efc7..7b12381a69e27f80c7d2f6b4c8902b700cef7e26 100644 --- a/drivers/pci/hotplug.c +++ b/drivers/pci/hotplug.c @@ -1,52 +1,68 @@ #include <linux/pci.h> #include <linux/module.h> -#include <linux/kmod.h> /* for hotplug_path */ +#include "pci.h" -#ifndef FALSE -#define FALSE (0) -#define TRUE (!FALSE) -#endif #ifdef CONFIG_HOTPLUG -static void run_sbin_hotplug(struct pci_dev *pdev, int insert) +int pci_hotplug (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) { - int i; - char *argv[3], *envp[8]; - char id[20], sub_id[24], bus_id[24], class_id[20]; - - if (!hotplug_path[0]) - return; - - sprintf(class_id, "PCI_CLASS=%04X", pdev->class); - sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); - sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); - sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); - - i = 0; - argv[i++] = hotplug_path; - argv[i++] = "pci"; - argv[i] = 0; - - i = 0; - /* minimal command environment */ - envp[i++] = "HOME=/"; - envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - /* other stuff we want to pass to /sbin/hotplug */ - envp[i++] = class_id; - envp[i++] = id; - envp[i++] = sub_id; - envp[i++] = bus_id; - if (insert) - envp[i++] = "ACTION=add"; - else - envp[i++] = "ACTION=remove"; + struct pci_dev *pdev; + char *scratch; + int i = 0; + int length = 0; + + if (!dev) + return -ENODEV; + + pdev = to_pci_dev(dev); + if (!pdev) + return -ENODEV; + + scratch = buffer; + + /* stuff we want to pass to /sbin/hotplug */ + envp[i++] = scratch; + length += snprintf (scratch, buffer_size - length, "PCI_CLASS=%04X", + pdev->class); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp[i++] = scratch; + length += snprintf (scratch, buffer_size - length, "PCI_ID=%04X:%04X", + pdev->vendor, pdev->device); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp[i++] = scratch; + length += snprintf (scratch, buffer_size - length, + "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, + pdev->subsystem_device); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp[i++] = scratch; + length += snprintf (scratch, buffer_size - length, "PCI_SLOT_NAME=%s", + pdev->slot_name); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + envp[i] = 0; - call_usermodehelper (argv [0], argv, envp); + return 0; } #else -static void run_sbin_hotplug(struct pci_dev *pdev, int insert) { } +int pci_hotplug (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} #endif /** @@ -66,8 +82,6 @@ pci_insert_device(struct pci_dev *dev, struct pci_bus *bus) #ifdef CONFIG_PROC_FS pci_proc_attach_device(dev); #endif - /* notify userspace of new hotplug device */ - run_sbin_hotplug(dev, TRUE); } static void @@ -99,8 +113,6 @@ pci_remove_device(struct pci_dev *dev) #ifdef CONFIG_PROC_FS pci_proc_detach_device(dev); #endif - /* notify userspace of hotplug device removal */ - run_sbin_hotplug(dev, FALSE); } #ifdef CONFIG_HOTPLUG diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e609a22cf13ce538548821c8635b8f1474ff19e7..040cc588981b84adf6b1d533988323bcd4cd8309 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/module.h> #include <linux/init.h> +#include "pci.h" /* * Registration of PCI drivers and handling of hot-pluggable devices. @@ -199,8 +200,9 @@ static int pci_bus_match(struct device * dev, struct device_driver * drv) } struct bus_type pci_bus_type = { - name: "pci", - match: pci_bus_match, + name: "pci", + match: pci_bus_match, + hotplug: pci_hotplug, }; static int __init pci_driver_init(void) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h new file mode 100644 index 0000000000000000000000000000000000000000..5bd2dd4a15bdc34045a8bf36ac0fb203649bbe10 --- /dev/null +++ b/drivers/pci/pci.h @@ -0,0 +1,5 @@ +/* Functions internal to the PCI core code */ + +extern int pci_hotplug (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size); + diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 49a929eec1f3843cfef82064f2c9df5a04ce856a..f321806c431cc2b55698671367c6ac120cf1342a 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -26,6 +26,8 @@ #include "i82092aa.h" #include "i82365.h" +MODULE_LICENSE("GPL"); + /* PCI core routines */ static struct pci_device_id i82092aa_pci_ids[] = { { diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index d81324484ff8614c77428cfc16b399573e97a8bf..05fc24981c0a190beb362775066decec6468ba61 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -1048,11 +1048,19 @@ int isapnp_cfg_begin(int csn, int logdev) isapnp_wait(); isapnp_key(); isapnp_wake(csn); -#if 1 /* to avoid malfunction when the isapnptools package is used */ - isapnp_set_rdp(); - udelay(1000); /* delay 1000us */ - write_address(0x01); - udelay(1000); /* delay 1000us */ +#if 1 + /* to avoid malfunction when the isapnptools package is used */ + /* we must set RDP to our value again */ + /* it is possible to set RDP only in the isolation phase */ + /* Jens Thoms Toerring <Jens.Toerring@physik.fu-berlin.de> */ + isapnp_write_byte(0x02, 0x04); /* clear CSN of card */ + mdelay(2); /* is this necessary? */ + isapnp_wake(csn); /* bring card into sleep state */ + isapnp_wake(0); /* bring card into isolation state */ + isapnp_set_rdp(); /* reset the RDP port */ + udelay(1000); /* delay 1000us */ + isapnp_write_byte(0x06, csn); /* reset CSN to previous value */ + udelay(250); /* is this necessary? */ #endif if (logdev >= 0) isapnp_device(logdev); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 57e32af64aac6a3337a31879707dcef029b82918..57d6faaa621c99615fdff846201fe88053ae3bbd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -510,57 +510,42 @@ static int usb_device_match (struct device *dev, struct device_driver *drv) * cases, we know no other thread can recycle our address, since we must * already have been serialized enough to prevent that. */ -static void call_policy (char *verb, struct usb_device *dev) +static int usb_hotplug (struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) { - char *argv [3], **envp, *buf, *scratch; - int i = 0, value; + struct usb_interface *intf; + struct usb_device *usb_dev; + char *scratch; + int i = 0; + int length = 0; - if (!hotplug_path [0]) - return; - if (in_interrupt ()) { - dbg ("In_interrupt"); - return; - } - if (!current->fs->root) { - /* statically linked USB is initted rather early */ - dbg ("call_policy %s, num %d -- no FS yet", verb, dev->devnum); - return; - } - if (dev->devnum < 0) { - dbg ("device already deleted ??"); - return; - } - if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) { - dbg ("enomem"); - return; - } - if (!(buf = kmalloc (256, GFP_KERNEL))) { - kfree (envp); - dbg ("enomem2"); - return; - } + dbg ("%s", __FUNCTION__); - /* only one standardized param to hotplug command: type */ - argv [0] = hotplug_path; - argv [1] = "usb"; - argv [2] = 0; + if (!dev) + return -ENODEV; - /* minimal command environment */ - envp [i++] = "HOME=/"; - envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + /* check for generic driver, we do not call do hotplug calls for it */ + if (dev->driver == &usb_generic_driver) + return -ENODEV; -#ifdef DEBUG - /* hint that policy agent should enter no-stdout debug mode */ - envp [i++] = "DEBUG=kernel"; -#endif - /* extensible set of named bus-specific parameters, - * supporting multiple driver selection algorithms. - */ - scratch = buf; + intf = to_usb_interface(dev); + if (!intf) + return -ENODEV; - /* action: add, remove */ - envp [i++] = scratch; - scratch += sprintf (scratch, "ACTION=%s", verb) + 1; + usb_dev = interface_to_usbdev (intf); + if (!usb_dev) + return -ENODEV; + + if (usb_dev->devnum < 0) { + dbg ("device already deleted ??"); + return -ENODEV; + } + if (!usb_dev->bus) { + dbg ("bus already removed?"); + return -ENODEV; + } + + scratch = buffer; #ifdef CONFIG_USB_DEVICEFS /* If this is available, userspace programs can directly read @@ -569,27 +554,48 @@ static void call_policy (char *verb, struct usb_device *dev) * * FIXME reduce hardwired intelligence here */ - envp [i++] = "DEVFS=/proc/bus/usb"; envp [i++] = scratch; - scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d", - dev->bus->busnum, dev->devnum) + 1; + length += snprintf (scratch, buffer_size - length, + "%s", "DEVFS=/proc/bus/usb"); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, + "DEVICE=/proc/bus/usb/%03d/%03d", + usb_dev->bus->busnum, usb_dev->devnum); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; #endif /* per-device configuration hacks are common */ envp [i++] = scratch; - scratch += sprintf (scratch, "PRODUCT=%x/%x/%x", - dev->descriptor.idVendor, - dev->descriptor.idProduct, - dev->descriptor.bcdDevice) + 1; + length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x", + usb_dev->descriptor.idVendor, + usb_dev->descriptor.idProduct, + usb_dev->descriptor.bcdDevice); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; /* class-based driver binding models */ envp [i++] = scratch; - scratch += sprintf (scratch, "TYPE=%d/%d/%d", - dev->descriptor.bDeviceClass, - dev->descriptor.bDeviceSubClass, - dev->descriptor.bDeviceProtocol) + 1; - if (dev->descriptor.bDeviceClass == 0) { - int alt = dev->actconfig->interface [0].act_altsetting; + length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d", + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + if (usb_dev->descriptor.bDeviceClass == 0) { + int alt = intf->act_altsetting; /* a simple/common case: one config, one interface, one driver * with current altsetting being a reasonable setting. @@ -597,31 +603,29 @@ static void call_policy (char *verb, struct usb_device *dev) * device-specific binding policies. */ envp [i++] = scratch; - scratch += sprintf (scratch, "INTERFACE=%d/%d/%d", - dev->actconfig->interface [0].altsetting [alt].bInterfaceClass, - dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass, - dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol) - + 1; - /* INTERFACE-0, INTERFACE-1, ... ? */ + length += snprintf (scratch, buffer_size - length, + "INTERFACE=%d/%d/%d", + intf->altsetting[alt].bInterfaceClass, + intf->altsetting[alt].bInterfaceSubClass, + intf->altsetting[alt].bInterfaceProtocol); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + } envp [i++] = 0; - /* assert: (scratch - buf) < sizeof buf */ - /* NOTE: user mode daemons can call the agents too */ - - dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum); - value = call_usermodehelper (argv [0], argv, envp); - kfree (buf); - kfree (envp); - if (value != 0) - dbg ("kusbd policy returned 0x%x", value); + return 0; } #else -static inline void -call_policy (char *verb, struct usb_device *dev) -{ } +static int usb_hotplug (struct device *dev, char **envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} #endif /* CONFIG_HOTPLUG */ @@ -894,9 +898,6 @@ void usb_disconnect(struct usb_device **pdev) put_device(&dev->dev); } - /* Let policy agent unload modules etc */ - call_policy ("remove", dev); - /* Decrement the reference count, it'll auto free everything when */ /* it hits 0 which could very well be now */ usb_put_dev(dev); @@ -1174,9 +1175,6 @@ int usb_new_device(struct usb_device *dev, struct device *parent) /* add a /proc/bus/usb entry */ usbfs_add_device(dev); - /* userspace may load modules and/or configure further */ - call_policy ("add", dev); - return 0; } @@ -1439,6 +1437,7 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe, struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, + .hotplug = usb_hotplug, }; /* diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 9f9aa4f8d05687b8a34668311f8dc72f8a0f0608..20c9692c102748f78e2560ab5cf59d4d3acb0cc2 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -788,7 +788,7 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us) /* only check card status if the map isn't allocated, ie no card seen yet * or if it's been over half a second since we last accessed it */ - if (info->lba_to_pba == NULL || jiffies > (info->last_access + HZ/2)) { + if (info->lba_to_pba == NULL || time_after(jiffies, info->last_access + HZ/2)) { /* check to see if a card is fitted */ result = sddr55_status (us); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 29df8e3b2db55bcf3b92fb4f9a79c2fcb757fd90..76de10367593aee41b77173a6f7c4bc3d2f80397 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -859,6 +859,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) * This must be called with scsi_lock(us->srb->host) held */ void usb_stor_abort_transport(struct us_data *us) { + struct Scsi_Host *host; int state = atomic_read(&us->sm_state); US_DEBUGP("usb_stor_abort_transport called\n"); @@ -870,7 +871,8 @@ void usb_stor_abort_transport(struct us_data *us) /* set state to abort and release the lock */ atomic_set(&us->sm_state, US_STATE_ABORTING); - scsi_unlock(us->srb->host); + host = us->srb->host; + scsi_unlock(host); /* If the state machine is blocked waiting for an URB or an IRQ, * let's wake it up */ @@ -892,8 +894,8 @@ void usb_stor_abort_transport(struct us_data *us) /* Wait for the aborted command to finish */ wait_for_completion(&us->notify); - /* Reacquire the lock */ - scsi_lock(us->srb->host); + /* Reacquire the lock: note that us->srb is now NULL */ + scsi_lock(host); } /* diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index abb117bfe6e47747a9a4353ab87b9333f7eaf614..ef5adc419c92c236d9cbd41091a16e3a6bfbc1cf 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -331,8 +331,10 @@ UNUSUAL_DEV( 0x05e3, 0x0700, 0x0000, 0xffff, * Like the SIIG unit above, this unit needs an INQUIRY to ask for exactly * 36 bytes of data. No more, no less. That is the only reason this entry * is needed. - */ -UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, + * + * ST818 slim drives (rev 0.02) don't need special care. +*/ +UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0x0001, "EagleTec", "External Hard Disk", US_SC_SCSI, US_PR_BULK, NULL, @@ -548,17 +550,22 @@ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x0001, US_SC_SCSI, US_PR_BULK, NULL, US_FL_MODE_XLATE | US_FL_START_STOP | US_FL_FIX_INQUIRY ), -/* Submitted by Brian Hall <brihall@bigfoot.com> +/* Submitted by Brian Hall <brihall@pcisys.net> * Needed for START_STOP flag */ UNUSUAL_DEV( 0x0c76, 0x0003, 0x0100, 0x0100, "JMTek", "USBDrive", US_SC_SCSI, US_PR_BULK, NULL, US_FL_START_STOP ), +UNUSUAL_DEV( 0x0c76, 0x0005, 0x0100, 0x0100, + "JMTek", + "USBDrive", + US_SC_SCSI, US_PR_BULK, NULL, + US_FL_START_STOP ), /* Reported by Dan Pilone <pilone@slac.com> * The device needs the flags only. - * Also reported by Brian Hall <brihall@bigfoot.com>, again for flags. + * Also reported by Brian Hall <brihall@pcisys.net>, again for flags. * I also suspect this device may have a broken serial number. */ UNUSUAL_DEV( 0x1065, 0x2136, 0x0000, 0x9999, diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index f73d6ba9d43b6f2fe37c4eca3fb19b8f034003ac..9a4f71f719e7cd939915a56086e1ee4dd62b1e39 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -1,12 +1,11 @@ /* - * USB Skeleton driver - 0.7 + * USB Skeleton driver - 0.8 * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) * * 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. + * published by the Free Software Foundation, version 2. * * * This driver is to be used as a skeleton driver to be able to create a @@ -22,6 +21,8 @@ * * History: * + * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device + * driver. * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do * not have both a bulk in and bulk out endpoint. * Thanks to Holger Waechtler for the fix. @@ -133,8 +134,8 @@ static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd static int skel_open (struct inode *inode, struct file *file); static int skel_release (struct inode *inode, struct file *file); -static void * skel_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id); -static void skel_disconnect (struct usb_device *dev, void *ptr); +static int skel_probe (struct usb_interface *intf, const struct usb_device_id *id); +static void skel_disconnect (struct usb_interface *intf); static void skel_write_bulk_callback (struct urb *urb); @@ -509,10 +510,10 @@ static void skel_write_bulk_callback (struct urb *urb) * Called by the usb core when a new device is connected that it thinks * this driver might be interested in. */ -static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) +static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(interface); struct usb_skel *dev = NULL; - struct usb_interface *interface; struct usb_interface_descriptor *iface_desc; struct usb_endpoint_descriptor *endpoint; int minor; @@ -525,7 +526,7 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru /* See if the device offered us matches what we can accept */ if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) || (udev->descriptor.idProduct != USB_SKEL_PRODUCT_ID)) { - return NULL; + return -ENODEV; } down (&minor_table_mutex); @@ -545,8 +546,6 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru memset (dev, 0x00, sizeof (*dev)); minor_table[minor] = dev; - interface = &udev->actconfig->interface[ifnum]; - init_MUTEX (&dev->sem); dev->udev = udev; dev->interface = interface; @@ -619,7 +618,11 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru exit: up (&minor_table_mutex); - return dev; + if (dev) { + dev_set_drvdata (&interface->dev, dev); + return 0; + } + return -ENODEV; } @@ -628,13 +631,17 @@ static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const stru * * Called by the usb core when the device is removed from the system. */ -static void skel_disconnect(struct usb_device *udev, void *ptr) +static void skel_disconnect(struct usb_interface *interface) { struct usb_skel *dev; int minor; - dev = (struct usb_skel *)ptr; - + dev = dev_get_drvdata (&interface->dev); + dev_set_drvdata (&interface->dev, NULL); + + if (!dev) + return; + down (&minor_table_mutex); down (&dev->sem); diff --git a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c index 4dbe475140572a5b98290d73f53c1fec583f1967..e32b018c9a18e78d02c2dcbe031c33cf93b925fd 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.c +++ b/drivers/video/matrox/matroxfb_DAC1064.c @@ -276,6 +276,7 @@ static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) { hw->MXoptionReg = mx; } +#ifdef CONFIG_FB_MATROX_G450 static void g450_set_plls(WPMINFO2) { u_int32_t c2_ctl; unsigned int pxc; @@ -365,6 +366,7 @@ static void g450_set_plls(WPMINFO2) { } } } +#endif void DAC1064_global_init(WPMINFO2) { struct matrox_hw_state* hw = &ACCESS_FBINFO(hw); @@ -372,6 +374,7 @@ void DAC1064_global_init(WPMINFO2) { hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; +#ifdef CONFIG_FB_MATROX_G450 if (ACCESS_FBINFO(devflags.g450dac)) { hw->DACreg[POS1064_XPWRCTRL] = 0x1F; /* powerup everything */ hw->DACreg[POS1064_XOUTPUTCONN] = 0x00; /* disable outputs */ @@ -420,7 +423,9 @@ void DAC1064_global_init(WPMINFO2) { } /* Now set timming related variables... */ g450_set_plls(PMINFO2); - } else { + } else +#endif + { if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) { hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; @@ -621,6 +626,7 @@ static struct matrox_altout m1064 = { .compute = m1064_compute, }; +#ifdef CONFIG_FB_MATROX_G450 static int g450_compute(void* out, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)out) if (m->mnp < 0) { @@ -637,6 +643,7 @@ static struct matrox_altout g450out = { .name = "Primary output", .compute = g450_compute, }; +#endif #endif /* NEED_DAC1064 */ @@ -819,6 +826,7 @@ static void MGA1064_reset(WPMINFO2) { #endif #ifdef CONFIG_FB_MATROX_G100 +#ifdef CONFIG_FB_MATROX_G450 static void g450_mclk_init(WPMINFO2) { /* switch all clocks to PCI source */ pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4); @@ -936,6 +944,10 @@ static void g450_preinit(WPMINFO2) { return; } +#else +static inline void g450_preinit(WPMINFO2) { +} +#endif static int MGAG100_preinit(WPMINFO2) { static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960, @@ -973,9 +985,12 @@ static int MGAG100_preinit(WPMINFO2) { ACCESS_FBINFO(capable.plnwt) = ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100 ? ACCESS_FBINFO(devflags.sgram) : 1; +#ifdef CONFIG_FB_MATROX_G450 if (ACCESS_FBINFO(devflags.g450dac)) { ACCESS_FBINFO(outputs[0]).output = &g450out; - } else { + } else +#endif + { ACCESS_FBINFO(outputs[0]).output = &m1064; } ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1; diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index 64980ccd5574b4f6286870b6f140b776c828481d..7d50b7c681e265c3fa6bd9ad6c5a3ce1fe750117 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -105,7 +105,7 @@ #endif #endif -#if defined(__alpha__) || defined(__m68k__) +#if defined(__alpha__) || defined(__mc68000__) #define READx_WORKS #define MEMCPYTOIO_WORKS #else @@ -121,7 +121,7 @@ #endif #endif -#if defined(__m68k__) +#if defined(__mc68000__) #define MAP_BUSTOVIRT #else #define MAP_IOREMAP diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 28039afdb4f8d6184b1db8f59f144eec876b83e5..72b3148cb038c6e8f2cc9eaffa1a0863bcebb846 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -2830,6 +2830,86 @@ void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot) return; } +/* + * add_missing_indices() + * + * function: Fix dtree page in which one or more entries has an invalid index. + * fsck.jfs should really fix this, but it currently does not. + * Called from jfs_readdir when bad index is detected. + */ +static void add_missing_indices(struct inode *inode, s64 bn) +{ + struct ldtentry *d; + struct dt_lock *dtlck; + int i; + uint index; + struct lv *lv; + struct metapage *mp; + dtpage_t *p; + int rc; + s8 *stbl; + tid_t tid; + struct tlock *tlck; + + tid = txBegin(inode->i_sb, 0); + + DT_GETPAGE(inode, bn, mp, PSIZE, p, rc); + + if (rc) { + printk(KERN_ERR "DT_GETPAGE failed!\n"); + goto end; + } + BT_MARK_DIRTY(mp, inode); + + ASSERT(p->header.flag & BT_LEAF); + + tlck = txLock(tid, inode, mp, tlckDTREE | tlckENTRY); + dtlck = (struct dt_lock *) &tlck->lock; + + stbl = DT_GETSTBL(p); + for (i = 0; i < p->header.nextindex; i++) { + d = (struct ldtentry *) &p->slot[stbl[i]]; + index = le32_to_cpu(d->index); + if ((index < 2) || (index >= JFS_IP(inode)->next_index)) { + d->index = cpu_to_le32(add_index(tid, inode, bn, i)); + if (dtlck->index >= dtlck->maxcnt) + dtlck = (struct dt_lock *) txLinelock(dtlck); + lv = dtlck->lv; + lv->offset = stbl[i]; + lv->length = 1; + dtlck->index++; + } + } + + DT_PUTPAGE(mp); + (void) txCommit(tid, 1, &inode, 0); +end: + txEnd(tid); +} + +/* + * Buffer to hold directory entry info while traversing a dtree page + * before being fed to the filldir function + */ +struct jfs_dirent { + loff_t position; + int ino; + u16 name_len; + char name[0]; +}; + +/* + * function to determine next variable-sized jfs_dirent in buffer + */ +inline struct jfs_dirent *next_jfs_dirent(struct jfs_dirent *dirent) +{ + return (struct jfs_dirent *) + ((char *)dirent + + ((sizeof (struct jfs_dirent) + dirent->name_len + 1 + + sizeof (loff_t) - 1) & + ~(sizeof (loff_t) - 1))); +} + /* * jfs_readdir() * @@ -2846,11 +2926,12 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) struct inode *ip = filp->f_dentry->d_inode; struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab; int rc = 0; + loff_t dtpos; /* legacy OS/2 style position */ struct dtoffset { s16 pn; s16 index; s32 unused; - } *dtoffset = (struct dtoffset *) &filp->f_pos; + } *dtoffset = (struct dtoffset *) &dtpos; s64 bn; struct metapage *mp; dtpage_t *p; @@ -2860,12 +2941,17 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) int i, next; struct ldtentry *d; struct dtslot *t; - int d_namleft, d_namlen, len, outlen; - char *d_name, *name_ptr; + int d_namleft, len, outlen; + unsigned long dirent_buf; + char *name_ptr; int dtlhdrdatalen; u32 dir_index; int do_index = 0; uint loop_count = 0; + struct jfs_dirent *jfs_dirent; + int jfs_dirents; + int overflow, fix_page, page_fixed = 0; + static int unique_pos = 2; /* If we can't fix broken index */ if (filp->f_pos == DIREND) return 0; @@ -2885,7 +2971,9 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) if (dir_index > 1) { struct dir_table_slot dirtab_slot; - if (dtEmpty(ip)) { + if (dtEmpty(ip) || + (dir_index >= JFS_IP(ip)->next_index)) { + /* Stale position. Directory has shrunk */ filp->f_pos = DIREND; return 0; } @@ -2963,13 +3051,15 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) */ dtlhdrdatalen = DTLHDRDATALEN_LEGACY; - if (filp->f_pos == 0) { + dtpos = filp->f_pos; + if (dtpos == 0) { /* build "." entry */ if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino, DT_DIR)) return 0; dtoffset->index = 1; + filp->f_pos = dtpos; } if (dtoffset->pn == 0) { @@ -2985,6 +3075,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } dtoffset->pn = 1; dtoffset->index = 0; + filp->f_pos = dtpos; } if (dtEmpty(ip)) { @@ -3009,32 +3100,72 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } } - d_name = kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t), GFP_NOFS); - if (d_name == NULL) { + dirent_buf = __get_free_page(GFP_KERNEL); + if (dirent_buf == 0) { DT_PUTPAGE(mp); - jERROR(1, ("jfs_readdir: kmalloc failed!\n")); + jERROR(1, ("jfs_readdir: __get_free_page failed!\n")); filp->f_pos = DIREND; - return 0; + return -ENOMEM; } + while (1) { + jfs_dirent = (struct jfs_dirent *) dirent_buf; + jfs_dirents = 0; + overflow = fix_page = 0; + stbl = DT_GETSTBL(p); for (i = index; i < p->header.nextindex; i++) { d = (struct ldtentry *) & p->slot[stbl[i]]; + if (((long) jfs_dirent + d->namlen + 1) > + (dirent_buf + PSIZE)) { + /* DBCS codepages could overrun dirent_buf */ + index = i; + overflow = 1; + break; + } + d_namleft = d->namlen; - name_ptr = d_name; + name_ptr = jfs_dirent->name; + jfs_dirent->ino = le32_to_cpu(d->inumber); if (do_index) { - filp->f_pos = le32_to_cpu(d->index); len = min(d_namleft, DTLHDRDATALEN); - } else + jfs_dirent->position = le32_to_cpu(d->index); + /* + * d->index should always be valid, but it + * isn't. fsck.jfs doesn't create the + * directory index for the lost+found + * directory. Rather than let it go, + * we can try to fix it. + */ + if ((jfs_dirent->position < 2) || + (jfs_dirent->position >= + JFS_IP(ip)->next_index)) { + if (!page_fixed && !isReadOnly(ip)) { + fix_page = 1; + /* + * setting overflow and setting + * index to i will cause the + * same page to be processed + * again starting here + */ + overflow = 1; + index = i; + break; + } + jfs_dirent->position = unique_pos++; + } + } else { + jfs_dirent->position = dtpos; len = min(d_namleft, DTLHDRDATALEN_LEGACY); + } /* copy the name of head/only segment */ outlen = jfs_strfromUCS_le(name_ptr, d->name, len, codepage); - d_namlen = outlen; + jfs_dirent->name_len = outlen; /* copy name in the additional segment(s) */ next = d->next; @@ -3053,56 +3184,66 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) len = min(d_namleft, DTSLOTDATALEN); outlen = jfs_strfromUCS_le(name_ptr, t->name, len, codepage); - d_namlen+= outlen; + jfs_dirent->name_len += outlen; next = t->next; } - if (filldir(dirent, d_name, d_namlen, filp->f_pos, - le32_to_cpu(d->inumber), DT_UNKNOWN)) - goto out; + jfs_dirents++; + jfs_dirent = next_jfs_dirent(jfs_dirent); skip_one: if (!do_index) dtoffset->index++; } - /* - * get next leaf page - */ + if (!overflow) { + /* Point to next leaf page */ + if (p->header.flag & BT_ROOT) + bn = 0; + else { + bn = le64_to_cpu(p->header.next); + index = 0; + /* update offset (pn:index) for new page */ + if (!do_index) { + dtoffset->pn++; + dtoffset->index = 0; + } + } + page_fixed = 0; + } - if (p->header.flag & BT_ROOT) { - filp->f_pos = DIREND; - break; + /* unpin previous leaf page */ + DT_PUTPAGE(mp); + + jfs_dirent = (struct jfs_dirent *) dirent_buf; + while (jfs_dirents--) { + filp->f_pos = jfs_dirent->position; + if (filldir(dirent, jfs_dirent->name, + jfs_dirent->name_len, filp->f_pos, + jfs_dirent->ino, DT_UNKNOWN)) + goto out; + jfs_dirent = next_jfs_dirent(jfs_dirent); } - bn = le64_to_cpu(p->header.next); - if (bn == 0) { + if (fix_page) { + add_missing_indices(ip, bn); + page_fixed = 1; + } + + if (!overflow && (bn == 0)) { filp->f_pos = DIREND; break; } - /* unpin previous leaf page */ - DT_PUTPAGE(mp); - - /* get next leaf page */ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); if (rc) { - kfree(d_name); + free_page(dirent_buf); return -rc; } - - /* update offset (pn:index) for new page */ - index = 0; - if (!do_index) { - dtoffset->pn++; - dtoffset->index = 0; - } - } out: - kfree(d_name); - DT_PUTPAGE(mp); + free_page(dirent_buf); return rc; } diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 67102cca446cd55fa5e9415a8dd3fbfac9cece28..081d32a9a6a5c417e0ca22e08f03866e135d2ec9 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -365,11 +365,7 @@ int diRead(struct inode *ip) if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) || (addressPXD(&iagp->inoext[extno]) == 0)) { - jERROR(1, ("diRead: Bad inoext: 0x%lx, 0x%lx\n", - (ulong) addressPXD(&iagp->inoext[extno]), - (ulong) lengthPXD(&iagp->inoext[extno]))); release_metapage(mp); - updateSuper(ip->i_sb, FM_DIRTY); return ESTALE; } @@ -416,12 +412,9 @@ int diRead(struct inode *ip) jERROR(1, ("diRead: i_ino != di_number\n")); updateSuper(ip->i_sb, FM_DIRTY); rc = EIO; - } else if (le32_to_cpu(dp->di_nlink) == 0) { - jERROR(1, - ("diRead: di_nlink is zero. ino=%ld\n", ip->i_ino)); - updateSuper(ip->i_sb, FM_DIRTY); + } else if (le32_to_cpu(dp->di_nlink) == 0) rc = ESTALE; - } else + else /* copy the disk inode to the in-memory inode */ rc = copy_from_dinode(dp, ip); diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index a8b1ba7819ca8a4c8f6a24ec0ea742adfe646b1f..2fa8516328013c6a5c78c172fec38b167e554ad6 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1552,7 +1552,12 @@ static int lmLogFileSystem(struct jfs_log * log, char *uuid, int activate) memcpy(logsuper->active[i].uuid, NULL_UUID, 16); break; } - assert(i < MAX_ACTIVE); + if (i == MAX_ACTIVE) { + jERROR(1,("Somebody stomped on the journal!\n")); + lbmFree(bpsuper); + return EIO; + } + } /* diff --git a/fs/locks.c b/fs/locks.c index ab969a790fcad01eaa8bbf6491ffa4f2eea9c4b7..75b13e9bf5886a7898153dea2297e3e6bc12b96a 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1459,10 +1459,8 @@ int fcntl_setlk(struct file *filp, unsigned int cmd, struct flock *l) break; } -out: - if (error) { - locks_free_lock(file_lock); - } + out: + locks_free_lock(file_lock); return error; } @@ -1601,11 +1599,8 @@ int fcntl_setlk64(struct file *filp, unsigned int cmd, struct flock64 *l) break; } - out: - if (error) { - locks_free_lock(file_lock); - } + locks_free_lock(file_lock); return error; } #endif /* BITS_PER_LONG == 32 */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 3cca03f5d6c8c425de84624a9362729c0ed119db..cb30a1c7878a587bf1a2e8c1e145d2eb4e6df43a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -228,6 +228,26 @@ enum acpi_interrupt_id { ACPI_INTERRUPT_COUNT }; +#define ACPI_SPACE_MEM 0 + +struct acpi_gen_regaddr { + u8 space_id; + u8 bit_width; + u8 bit_offset; + u8 resv; + u32 addrl; + u32 addrh; +} __attribute__ ((packed)); + +struct acpi_table_hpet { + struct acpi_table_header header; + u32 id; + struct acpi_gen_regaddr addr; + u8 number; + u16 min_tick; + u8 page_protect; +} __attribute__ ((packed)); + /* * System Resource Affinity Table (SRAT) * see http://www.microsoft.com/hwdev/design/srat.htm @@ -333,6 +353,7 @@ enum acpi_table_id { ACPI_SRAT, ACPI_SSDT, ACPI_SPMI, + ACPI_HPET, ACPI_TABLE_COUNT }; diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index ffd04e267abe7b8aaa6dc9a97b3606f55af9acfb..e2f9cef26fcc9c6cbc8272277ef0032df07e6970 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -66,6 +66,7 @@ enum chipset_type { AMD_IRONGATE, AMD_761, AMD_762, + AMD_8151, ALI_M1541, ALI_M1621, ALI_M1631, @@ -161,7 +162,7 @@ extern agp_memory *agp_allocate_memory(size_t, u32); * */ -extern void agp_copy_info(agp_kern_info *); +extern int agp_copy_info(agp_kern_info *); /* * agp_copy_info : @@ -257,7 +258,7 @@ typedef struct { void (*enable)(u32); int (*acquire)(void); void (*release)(void); - void (*copy_info)(agp_kern_info *); + int (*copy_info)(agp_kern_info *); } drm_agp_t; extern const drm_agp_t *drm_agp_p; diff --git a/include/linux/device.h b/include/linux/device.h index 6053349e4303d833f7d74eb9f46fd5825726f3da..ef76b15fa93063c3d6fc33c936f0f683f89fc0ff 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -67,6 +67,8 @@ struct bus_type { int (*match)(struct device * dev, struct device_driver * drv); struct device * (*add) (struct device * parent, char * bus_id); + int (*hotplug) (struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size); }; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index b18faeef995593e956ece69bb52fa41535aaec9e..5c6c7db6e97e01528c7e2c536adfcd9fb245153f 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -1,7 +1,7 @@ #ifndef _LINUX_ELEVATOR_H #define _LINUX_ELEVATOR_H -typedef int (elevator_merge_fn) (request_queue_t *, struct request **, +typedef int (elevator_merge_fn) (request_queue_t *, struct list_head **, struct bio *); typedef void (elevator_merge_req_fn) (request_queue_t *, struct request *, struct request *); @@ -42,7 +42,7 @@ struct elevator_s */ extern void __elv_add_request(request_queue_t *, struct request *, struct list_head *); -extern int elv_merge(request_queue_t *, struct request **, struct bio *); +extern int elv_merge(request_queue_t *, struct list_head **, struct bio *); extern void elv_merge_requests(request_queue_t *, struct request *, struct request *); extern void elv_merged_request(request_queue_t *, struct request *); @@ -78,7 +78,7 @@ extern void elevator_exit(request_queue_t *, elevator_t *); extern inline int bio_rq_in_between(struct bio *, struct request *, struct list_head *); extern inline int elv_rq_merge_ok(struct request *, struct bio *); extern inline int elv_try_merge(struct request *, struct bio *); -extern inline int elv_try_last_merge(request_queue_t *, struct request **, struct bio *); +extern inline int elv_try_last_merge(request_queue_t *, struct bio *); /* * Return values from elevator merger diff --git a/include/linux/elf.h b/include/linux/elf.h index f39bf2502871c110aa82e6daa1a8c351985fe608..6801e08dddc5f6e1a14c19bea7f69ed7bdfb6208 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -75,7 +75,7 @@ typedef __s64 Elf64_Sxword; #define EM_IA_64 50 /* HP/Intel IA-64 */ -#define EM_X8664 62 /* AMD x86-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ #define EM_S390 22 /* IBM S/390 */ diff --git a/include/linux/futex.h b/include/linux/futex.h index 1b9a8f7ad5e85331179c703d61a1182fc3af06eb..415946df03d4bd3175258e12e5d2b6d539740352 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -6,6 +6,6 @@ #define FUTEX_WAKE (1) #define FUTEX_FD (2) -extern asmlinkage int sys_futex(void *uaddr, int op, int val, struct timespec *utime); +extern asmlinkage int sys_futex(unsigned long uaddr, int op, int val, struct timespec *utime); #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 482db998aca70ed4eaef95a7cb2a9ada07d08c2a..4ae8eb10dcb202c917f7640561625492b35d18c6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -374,6 +374,7 @@ extern int handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma, unsi extern int make_pages_present(unsigned long addr, unsigned long end); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); +extern struct page * follow_page(struct mm_struct *mm, unsigned long address, int write); int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9a1ad256e2cc430998c02a9614df533f6f27478f..ea37fd84068dccb9137592934a9a3239fe6ce4da 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -398,6 +398,7 @@ #define PCI_DEVICE_ID_AMD_8111_LAN 0x7462 #define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 #define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d +#define PCI_DEVICE_ID_AMD_8151_0 0x7454 #define PCI_VENDOR_ID_TRIDENT 0x1023 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 diff --git a/include/linux/vcache.h b/include/linux/vcache.h new file mode 100644 index 0000000000000000000000000000000000000000..d5756643332cc8e6d03a13bb4e226856c4350f9a --- /dev/null +++ b/include/linux/vcache.h @@ -0,0 +1,26 @@ +/* + * virtual => physical mapping cache support. + */ +#ifndef _LINUX_VCACHE_H +#define _LINUX_VCACHE_H + +typedef struct vcache_s { + unsigned long address; + struct mm_struct *mm; + struct list_head hash_entry; + void (*callback)(struct vcache_s *data, struct page *new_page); +} vcache_t; + +extern spinlock_t vcache_lock; + +extern void __attach_vcache(vcache_t *vcache, + unsigned long address, + struct mm_struct *mm, + void (*callback)(struct vcache_s *data, struct page *new_page)); + +extern void detach_vcache(vcache_t *vcache); + +extern void invalidate_vcache(unsigned long address, struct mm_struct *mm, + struct page *new_page); + +#endif diff --git a/include/net/irda/irda-usb.h b/include/net/irda/irda-usb.h index 3480c84386eceb635134f78e767cf27a6e8e33fc..7c8c9de11c36041469b423b870d35ff24f537a7b 100644 --- a/include/net/irda/irda-usb.h +++ b/include/net/irda/irda-usb.h @@ -127,7 +127,7 @@ struct irda_class_desc { struct irda_usb_cb { struct irda_class_desc *irda_desc; struct usb_device *usbdev; /* init: probe_irda */ - unsigned int ifnum; /* Interface number of the USB dev. */ + struct usb_interface *usbintf; /* init: probe_irda */ int netopen; /* Device is active for network */ int present; /* Device is present on the bus */ __u32 capability; /* Capability of the hardware */ diff --git a/kernel/fork.c b/kernel/fork.c index 5880309f3fee1204b8369c3fc28f309385aea832..150a33b8939988a8a0f2703e03ad607e1547d517 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -381,7 +381,7 @@ void mm_release(void) * not set up a proper pointer then tough luck. */ put_user(0, tsk->user_tid); - sys_futex(tsk->user_tid, FUTEX_WAKE, 1, NULL); + sys_futex((unsigned long)tsk->user_tid, FUTEX_WAKE, 1, NULL); } } diff --git a/kernel/futex.c b/kernel/futex.c index ac9fda58181e79e31781fa83dbebb6ec276830fd..4b460d628f5a803db31d5f836a53eff40f321416 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -31,6 +31,7 @@ #include <linux/init.h> #include <linux/fs.h> #include <linux/futex.h> +#include <linux/vcache.h> #include <linux/highmem.h> #include <linux/time.h> #include <linux/pagemap.h> @@ -38,7 +39,6 @@ #include <linux/poll.h> #include <linux/file.h> #include <linux/dcache.h> -#include <asm/uaccess.h> /* Simple "sleep if unchanged" interface. */ @@ -55,9 +55,14 @@ static struct vfsmount *futex_mnt; struct futex_q { struct list_head list; wait_queue_head_t waiters; + /* Page struct and offset within it. */ struct page *page; unsigned int offset; + + /* the virtual => physical cache */ + vcache_t vcache; + /* For fd, sigio sent using these. */ int fd; struct file *filp; @@ -85,21 +90,43 @@ static inline void tell_waiter(struct futex_q *q) send_sigio(&q->filp->f_owner, q->fd, POLL_IN); } +/* Get kernel address of the user page and pin it. */ +static struct page *pin_page(unsigned long page_start) +{ + struct mm_struct *mm = current->mm; + struct page *page = NULL; + int err; + + down_read(&mm->mmap_sem); + + err = get_user_pages(current, mm, page_start, + 1 /* one page */, + 0 /* not writable */, + 0 /* don't force */, + &page, + NULL /* don't return vmas */); + up_read(&mm->mmap_sem); + if (err < 0) + return ERR_PTR(err); + return page; +} + static inline void unpin_page(struct page *page) { - /* Avoid releasing the page which is on the LRU list. I don't - know if this is correct, but it stops the BUG() in - __free_pages_ok(). */ page_cache_release(page); } -static int futex_wake(struct list_head *head, - struct page *page, - unsigned int offset, - int num) +static int futex_wake(unsigned long uaddr, unsigned int offset, int num) { - struct list_head *i, *next; - int num_woken = 0; + struct list_head *i, *next, *head; + struct page *page; + int ret; + + page = pin_page(uaddr - offset); + ret = IS_ERR(page); + if (ret) + goto out; + head = hash_futex(page, offset); spin_lock(&futex_lock); list_for_each_safe(i, next, head) { @@ -108,36 +135,81 @@ static int futex_wake(struct list_head *head, if (this->page == page && this->offset == offset) { list_del_init(i); tell_waiter(this); - num_woken++; - if (num_woken >= num) break; + ret++; + if (ret >= num) + break; } } spin_unlock(&futex_lock); - return num_woken; + unpin_page(page); +out: + return ret; +} + +static void futex_vcache_callback(vcache_t *vcache, struct page *new_page) +{ + struct futex_q *q = container_of(vcache, struct futex_q, vcache); + struct list_head *head = hash_futex(new_page, q->offset); + + BUG_ON(list_empty(&q->list)); + + spin_lock(&futex_lock); + + q->page = new_page; + list_del_init(&q->list); + list_add_tail(&q->list, head); + + spin_unlock(&futex_lock); } /* Add at end to avoid starvation */ -static inline void queue_me(struct list_head *head, +static inline int queue_me(struct list_head *head, struct futex_q *q, struct page *page, unsigned int offset, int fd, - struct file *filp) + struct file *filp, + unsigned long uaddr) { - q->page = page; + struct page *tmp; + int ret = 0; + q->offset = offset; q->fd = fd; q->filp = filp; + spin_lock(&vcache_lock); spin_lock(&futex_lock); - list_add_tail(&q->list, head); + spin_lock(¤t->mm->page_table_lock); + + /* + * Has the mapping changed meanwhile? + */ + tmp = follow_page(current->mm, uaddr, 0); + + if (tmp == page) { + q->page = page; + list_add_tail(&q->list, head); + /* + * We register a futex callback to this virtual address, + * to make sure a COW properly rehashes the futex-queue. + */ + __attach_vcache(&q->vcache, uaddr, current->mm, futex_vcache_callback); + } else + ret = 1; + + spin_unlock(¤t->mm->page_table_lock); spin_unlock(&futex_lock); + spin_unlock(&vcache_lock); + + return ret; } /* Return 1 if we were still queued (ie. 0 means we were woken) */ static inline int unqueue_me(struct futex_q *q) { int ret = 0; + spin_lock(&futex_lock); if (!list_empty(&q->list)) { list_del(&q->list); @@ -147,46 +219,34 @@ static inline int unqueue_me(struct futex_q *q) return ret; } -/* Get kernel address of the user page and pin it. */ -static struct page *pin_page(unsigned long page_start) -{ - struct mm_struct *mm = current->mm; - struct page *page; - int err; - - down_read(&mm->mmap_sem); - err = get_user_pages(current, mm, page_start, - 1 /* one page */, - 0 /* writable not important */, - 0 /* don't force */, - &page, - NULL /* don't return vmas */); - up_read(&mm->mmap_sem); - - if (err < 0) - return ERR_PTR(err); - return page; -} - -static int futex_wait(struct list_head *head, - struct page *page, +static int futex_wait(unsigned long uaddr, int offset, int val, - int *uaddr, unsigned long time) { - int curval; - struct futex_q q; DECLARE_WAITQUEUE(wait, current); - int ret = 0; + struct list_head *head; + int ret = 0, curval; + struct page *page; + struct futex_q q; + +repeat_lookup: + page = pin_page(uaddr - offset); + ret = IS_ERR(page); + if (ret) + goto out; + head = hash_futex(page, offset); set_current_state(TASK_INTERRUPTIBLE); init_waitqueue_head(&q.waiters); add_wait_queue(&q.waiters, &wait); - queue_me(head, &q, page, offset, -1, NULL); + if (queue_me(head, &q, page, offset, -1, NULL, uaddr)) { + unpin_page(page); + goto repeat_lookup; + } /* Page is pinned, but may no longer be in this address space. */ - if (get_user(curval, uaddr) != 0) { + if (get_user(curval, (int *)uaddr) != 0) { ret = -EFAULT; goto out; } @@ -204,11 +264,15 @@ static int futex_wait(struct list_head *head, ret = -EINTR; goto out; } - out: +out: + detach_vcache(&q.vcache); set_current_state(TASK_RUNNING); /* Were we woken up anyway? */ if (!unqueue_me(&q)) - return 0; + ret = 0; + if (page) + unpin_page(page); + return ret; } @@ -251,25 +315,26 @@ static struct file_operations futex_fops = { /* Signal allows caller to avoid the race which would occur if they set the sigio stuff up afterwards. */ -static int futex_fd(struct list_head *head, - struct page *page, - int offset, - int signal) +static int futex_fd(unsigned long uaddr, int offset, int signal) { - int fd; + struct page *page = NULL; + struct list_head *head; struct futex_q *q; struct file *filp; + int ret; + ret = -EINVAL; if (signal < 0 || signal > _NSIG) - return -EINVAL; + goto out; - fd = get_unused_fd(); - if (fd < 0) - return fd; + ret = get_unused_fd(); + if (ret < 0) + goto out; filp = get_empty_filp(); if (!filp) { - put_unused_fd(fd); - return -ENFILE; + put_unused_fd(ret); + ret = -ENFILE; + goto out; } filp->f_op = &futex_fops; filp->f_vfsmnt = mntget(futex_mnt); @@ -280,37 +345,55 @@ static int futex_fd(struct list_head *head, ret = f_setown(filp, current->tgid, 1); if (ret) { - put_unused_fd(fd); + put_unused_fd(ret); put_filp(filp); - return ret; + goto out; } filp->f_owner.signum = signal; } q = kmalloc(sizeof(*q), GFP_KERNEL); if (!q) { - put_unused_fd(fd); + put_unused_fd(ret); + put_filp(filp); + ret = -ENOMEM; + goto out; + } + +repeat_lookup: + page = pin_page(uaddr - offset); + ret = IS_ERR(page); + if (ret) { + put_unused_fd(ret); put_filp(filp); - return -ENOMEM; + kfree(q); + page = NULL; + goto out; } + head = hash_futex(page, offset); /* Initialize queue structure, and add to hash table. */ filp->private_data = q; init_waitqueue_head(&q->waiters); - queue_me(head, q, page, offset, fd, filp); + if (queue_me(head, q, page, offset, ret, filp, uaddr)) { + unpin_page(page); + goto repeat_lookup; + } /* Now we map fd to filp, so userspace can access it */ - fd_install(fd, filp); - return fd; + fd_install(ret, filp); + page = NULL; +out: + if (page) + unpin_page(page); + return ret; } -asmlinkage int sys_futex(void *uaddr, int op, int val, struct timespec *utime) +asmlinkage int sys_futex(unsigned long uaddr, int op, int val, struct timespec *utime) { - int ret; - unsigned long pos_in_page; - struct list_head *head; - struct page *page; unsigned long time = MAX_SCHEDULE_TIMEOUT; + unsigned long pos_in_page; + int ret; if (utime) { struct timespec t; @@ -319,38 +402,27 @@ asmlinkage int sys_futex(void *uaddr, int op, int val, struct timespec *utime) time = timespec_to_jiffies(&t) + 1; } - pos_in_page = ((unsigned long)uaddr) % PAGE_SIZE; + pos_in_page = uaddr % PAGE_SIZE; /* Must be "naturally" aligned, and not on page boundary. */ if ((pos_in_page % __alignof__(int)) != 0 || pos_in_page + sizeof(int) > PAGE_SIZE) return -EINVAL; - /* Simpler if it doesn't vanish underneath us. */ - page = pin_page((unsigned long)uaddr - pos_in_page); - if (IS_ERR(page)) - return PTR_ERR(page); - - head = hash_futex(page, pos_in_page); switch (op) { case FUTEX_WAIT: - ret = futex_wait(head, page, pos_in_page, val, uaddr, time); + ret = futex_wait(uaddr, pos_in_page, val, time); break; case FUTEX_WAKE: - ret = futex_wake(head, page, pos_in_page, val); + ret = futex_wake(uaddr, pos_in_page, val); break; case FUTEX_FD: /* non-zero val means F_SETOWN(getpid()) & F_SETSIG(val) */ - ret = futex_fd(head, page, pos_in_page, val); - if (ret >= 0) - /* Leave page pinned (attached to fd). */ - return ret; + ret = futex_fd(uaddr, pos_in_page, val); break; default: ret = -EINVAL; } - unpin_page(page); - return ret; } diff --git a/mm/Makefile b/mm/Makefile index ff0df68c65c69af2b3fbd639b9f135da51e545ea..5f8ef663997109fd735c1e7ad5ede261ad0bb651 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -9,6 +9,6 @@ obj-y := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \ page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \ shmem.o highmem.o mempool.o msync.o mincore.o readahead.o \ - pdflush.o page-writeback.o rmap.o madvise.o + pdflush.o page-writeback.o rmap.o madvise.o vcache.o include $(TOPDIR)/Rules.make diff --git a/mm/filemap.c b/mm/filemap.c index f45168a04974b277d3675a5a868bebc34f47b6cf..066083f7ce63b328be1dc401ee33198233136909 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -60,8 +60,8 @@ * ->swap_list_lock * ->swap_device_lock (exclusive_swap_page, others) * ->mapping->page_lock - * ->inode_lock (__mark_inode_dirty) - * ->sb_lock (fs/fs-writeback.c) + * ->inode_lock + * ->sb_lock (fs/fs-writeback.c) */ /* diff --git a/mm/memory.c b/mm/memory.c index e58e9dee7bfcbc22e8db610657ad5c9f6e2370ce..76af586cceaad05545f198fe7cb481047607af2d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -43,6 +43,7 @@ #include <linux/iobuf.h> #include <linux/highmem.h> #include <linux/pagemap.h> +#include <linux/vcache.h> #include <asm/pgalloc.h> #include <asm/rmap.h> @@ -463,7 +464,7 @@ void zap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned * Do a quick page-table lookup for a single page. * mm->page_table_lock must be held. */ -static inline struct page * +struct page * follow_page(struct mm_struct *mm, unsigned long address, int write) { pgd_t *pgd; @@ -494,7 +495,7 @@ follow_page(struct mm_struct *mm, unsigned long address, int write) } out: - return 0; + return NULL; } /* @@ -973,6 +974,7 @@ static inline void establish_pte(struct vm_area_struct * vma, unsigned long addr static inline void break_cow(struct vm_area_struct * vma, struct page * new_page, unsigned long address, pte_t *page_table) { + invalidate_vcache(address, vma->vm_mm, new_page); flush_page_to_ram(new_page); flush_cache_page(vma, address); establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)))); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 8a1aee7777d6bf8ddf4be458c189dd589926aeb1..b67090c6f678a6406d0fe53cc15bbfcd72d0f393 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -601,3 +601,4 @@ int test_clear_page_dirty(struct page *page) } return 0; } +EXPORT_SYMBOL(test_clear_page_dirty); diff --git a/mm/vcache.c b/mm/vcache.c new file mode 100644 index 0000000000000000000000000000000000000000..ea6bc9d2b25941c652397331e646d7c823a2b046 --- /dev/null +++ b/mm/vcache.c @@ -0,0 +1,93 @@ +/* + * linux/mm/vcache.c + * + * virtual => physical page mapping cache. Users of this mechanism + * register callbacks for a given (virt,mm,phys) page mapping, and + * the kernel guarantees to call back when this mapping is invalidated. + * (ie. upon COW or unmap.) + * + * Started by Ingo Molnar, Copyright (C) 2002 + */ + +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/hash.h> +#include <linux/vcache.h> + +#define VCACHE_HASHBITS 8 +#define VCACHE_HASHSIZE (1 << VCACHE_HASHBITS) + +spinlock_t vcache_lock = SPIN_LOCK_UNLOCKED; + +static struct list_head hash[VCACHE_HASHSIZE]; + +static struct list_head *hash_vcache(unsigned long address, + struct mm_struct *mm) +{ + return &hash[hash_long(address + (unsigned long)mm, VCACHE_HASHBITS)]; +} + +void __attach_vcache(vcache_t *vcache, + unsigned long address, + struct mm_struct *mm, + void (*callback)(struct vcache_s *data, struct page *new)) +{ + struct list_head *hash_head; + + address &= PAGE_MASK; + vcache->address = address; + vcache->mm = mm; + vcache->callback = callback; + + hash_head = hash_vcache(address, mm); + + list_add(&vcache->hash_entry, hash_head); +} + +void detach_vcache(vcache_t *vcache) +{ + spin_lock(&vcache_lock); + list_del(&vcache->hash_entry); + spin_unlock(&vcache_lock); +} + +void invalidate_vcache(unsigned long address, struct mm_struct *mm, + struct page *new_page) +{ + struct list_head *l, *hash_head; + vcache_t *vcache; + + address &= PAGE_MASK; + + hash_head = hash_vcache(address, mm); + /* + * This is safe, because this path is called with the mm + * semaphore read-held, and the add/remove path calls with the + * mm semaphore write-held. So while other mm's might add new + * entries in parallel, and *this* mm is locked out, so if the + * list is empty now then we do not have to take the vcache + * lock to see it's really empty. + */ + if (likely(list_empty(hash_head))) + return; + + spin_lock(&vcache_lock); + list_for_each(l, hash_head) { + vcache = list_entry(l, vcache_t, hash_entry); + if (vcache->address != address || vcache->mm != mm) + continue; + vcache->callback(vcache, new_page); + } + spin_unlock(&vcache_lock); +} + +static int __init vcache_init(void) +{ + unsigned int i; + + for (i = 0; i < VCACHE_HASHSIZE; i++) + INIT_LIST_HEAD(hash + i); + return 0; +} +__initcall(vcache_init); +